diff --git a/driver/gomethods/gomethods_migrator.go b/driver/gomethods/gomethods_migrator.go index 2ee019253..70d57c381 100644 --- a/driver/gomethods/gomethods_migrator.go +++ b/driver/gomethods/gomethods_migrator.go @@ -1,22 +1,15 @@ package gomethods import ( - //"bytes" "bufio" - "database/sql/driver" "fmt" + "github.com/dimag-jfrog/migrate/driver" "github.com/dimag-jfrog/migrate/file" "os" "path" "strings" ) -type UnregisteredMethodsReceiverError string - -func (e UnregisteredMethodsReceiverError) Error() string { - return "Unregistered methods receiver: " + string(e) -} - type MissingMethodError string func (e MissingMethodError) Error() string { return "Non existing migrate method: " + string(e) } @@ -37,13 +30,16 @@ func (e *MethodInvocationFailedError) Error() string { } type MigrationMethodInvoker interface { - IsValid(methodName string, methodReceiver interface{}) bool - Invoke(methodName string, methodReceiver interface{}) error + IsValid(methodName string) bool + Invoke(methodName string) error } type GoMethodsDriver interface { driver.Driver + MigrationMethodInvoker + MethodsReceiver() interface{} + SetMethodsReceiver(r interface{}) error } type Migrator struct { @@ -52,7 +48,7 @@ type Migrator struct { } func (m *Migrator) Migrate(f file.File, pipe chan interface{}) error { - methods, methodsReceiver, err := m.getMigrationMethods(f) + methods, err := m.getMigrationMethods(f) if err != nil { pipe <- err return err @@ -60,7 +56,7 @@ func (m *Migrator) Migrate(f file.File, pipe chan interface{}) error { for i, methodName := range methods { pipe <- methodName - err := m.MethodInvoker.Invoke(methodName, methodsReceiver) + err := m.MethodInvoker.Invoke(methodName) if err != nil { pipe <- err if !m.RollbackOnFailure { @@ -71,12 +67,12 @@ func (m *Migrator) Migrate(f file.File, pipe chan interface{}) error { for j := i - 1; j >= 0; j-- { rollbackToMethodName := getRollbackToMethod(methods[j]) if rollbackToMethodName == "" || - !m.MethodInvoker.IsValid(rollbackToMethodName, methodsReceiver) { + !m.MethodInvoker.IsValid(rollbackToMethodName) { continue } pipe <- rollbackToMethodName - err = m.MethodInvoker.Invoke(rollbackToMethodName, methodsReceiver) + err = m.MethodInvoker.Invoke(rollbackToMethodName) if err != nil { pipe <- err break @@ -122,20 +118,17 @@ func getFileLines(file file.File) ([]string, error) { } return lines, nil } else { - //n := bytes.IndexByte(file.Content, 0) - //n := bytes.Index(file.Content, []byte{0}) - //s := string(file.Content[:n]) s := string(file.Content) return strings.Split(s, "\n"), nil } } -func (m *Migrator) getMigrationMethods(f file.File) (methods []string, methodsReceiver interface{}, err error) { +func (m *Migrator) getMigrationMethods(f file.File) (methods []string, err error) { var lines []string lines, err = getFileLines(f) if err != nil { - return nil, nil, err + return nil, err } for _, line := range lines { @@ -146,24 +139,14 @@ func (m *Migrator) getMigrationMethods(f file.File) (methods []string, methodsRe continue } - if methodsReceiver == nil { - receiverName := line - methodsReceiver = GetMethodsReceiver(receiverName) - if methodsReceiver == nil { - return nil, nil, UnregisteredMethodsReceiverError(receiverName) - } - continue - - } else { - methodName := line - if !m.MethodInvoker.IsValid(methodName, methodsReceiver) { - return nil, nil, MissingMethodError(methodName) - } - - methods = append(methods, methodName) + methodName := line + if !m.MethodInvoker.IsValid(methodName) { + return nil, MissingMethodError(methodName) } + + methods = append(methods, methodName) } - return methods, methodsReceiver, nil + return methods, nil } diff --git a/driver/gomethods/gomethods_migrator_test.go b/driver/gomethods/gomethods_migrator_test.go index eb33103f6..db2a81733 100644 --- a/driver/gomethods/gomethods_migrator_test.go +++ b/driver/gomethods/gomethods_migrator_test.go @@ -14,7 +14,7 @@ type FakeGoMethodsInvoker struct { InvokedMethods []string } -func (invoker *FakeGoMethodsInvoker) IsValid(methodName string, methodReceiver interface{}) bool { +func (invoker *FakeGoMethodsInvoker) IsValid(methodName string) bool { if methodName == "V001_some_non_existing_method_up" { return false } @@ -22,7 +22,7 @@ func (invoker *FakeGoMethodsInvoker) IsValid(methodName string, methodReceiver i return true } -func (invoker *FakeGoMethodsInvoker) Invoke(methodName string, methodReceiver interface{}) error { +func (invoker *FakeGoMethodsInvoker) Invoke(methodName string) error { invoker.InvokedMethods = append(invoker.InvokedMethods, methodName) if methodName == "V001_some_failing_method_up" || methodName == "V001_some_failing_method_down" { @@ -56,7 +56,6 @@ func TestMigrate(t *testing.T) { Name: "foobar", Direction: direction.Up, Content: []byte(` - FakeMethodsReceiver V001_init_organizations_up V001_init_users_up `), @@ -73,7 +72,6 @@ func TestMigrate(t *testing.T) { Name: "foobar", Direction: direction.Down, Content: []byte(` - FakeMethodsReceiver V001_init_users_down V001_init_organizations_down `), @@ -90,7 +88,6 @@ func TestMigrate(t *testing.T) { Name: "foobar", Direction: direction.Up, Content: []byte(` - FakeMethodsReceiver V001_init_organizations_up V001_init_users_up V001_some_non_existing_method_up @@ -108,7 +105,6 @@ func TestMigrate(t *testing.T) { Name: "foobar", Direction: direction.Up, Content: []byte(` - FakeMethodsReceiver V001_init_organizations_up V001_some_failing_method_up V001_init_users_up @@ -132,7 +128,6 @@ func TestMigrate(t *testing.T) { Name: "foobar", Direction: direction.Down, Content: []byte(` - FakeMethodsReceiver V001_init_users_down V001_some_failing_method_down V001_init_organizations_down @@ -157,7 +152,6 @@ func TestMigrate(t *testing.T) { Name: "foobar", Direction: direction.Up, Content: []byte(` - FakeMethodsReceiver V001_init_organizations_up V001_init_users_up V001_some_failing_method_up @@ -185,7 +179,6 @@ func TestMigrate(t *testing.T) { Name: "foobar", Direction: direction.Down, Content: []byte(` - FakeMethodsReceiver V001_init_users_down V001_some_failing_method_down V001_init_organizations_down @@ -203,8 +196,6 @@ func TestMigrate(t *testing.T) { }, } - RegisterMethodsReceiver("FakeMethodsReceiver", "") - for _, c := range cases { migrator := Migrator{} fakeInvoker := &FakeGoMethodsInvoker{InvokedMethods: []string{}} diff --git a/driver/gomethods/gomethods_registry.go b/driver/gomethods/gomethods_registry.go index 78ac84c42..5ceb12873 100644 --- a/driver/gomethods/gomethods_registry.go +++ b/driver/gomethods/gomethods_registry.go @@ -1,30 +1,39 @@ package gomethods import ( + "fmt" + "github.com/dimag-jfrog/migrate/driver" "sync" ) var methodsReceiversMu sync.Mutex -var methodsReceivers = make(map[string]interface{}) -// Registers a methods receiver object so it can be created from its name. Users of gomethods migration drivers should -// call this method to register objects with their migration methods before executing the migration -func RegisterMethodsReceiver(name string, receiver interface{}) { +// Registers a methods receiver for go methods driver +// Users of gomethods migration drivers should call this method +// to register objects with their migration methods before executing the migration +func RegisterMethodsReceiverForDriver(driverName string, receiver interface{}) { methodsReceiversMu.Lock() defer methodsReceiversMu.Unlock() if receiver == nil { panic("Go methods: Register receiver object is nil") } - if _, dup := methodsReceivers[name]; dup { - panic("Go methods: Register called twice for receiver " + name) + + driver := driver.GetDriver(driverName) + if driver == nil { + panic("Go methods: Trying to register receiver for not registered driver " + driverName) } - methodsReceivers[name] = receiver -} -// Retrieves a registered driver by name -func GetMethodsReceiver(name string) interface{} { - methodsReceiversMu.Lock() - defer methodsReceiversMu.Unlock() - receiver := methodsReceivers[name] - return receiver + methodsDriver, ok := driver.(GoMethodsDriver) + if !ok { + panic("Go methods: Trying to register receiver for non go methods driver " + driverName) + } + + if methodsDriver.MethodsReceiver() != nil { + panic("Go methods: Methods receiver already registered for driver " + driverName) + } + + if err := methodsDriver.SetMethodsReceiver(receiver); err != nil { + panic(fmt.Sprintf("Go methods: Failed to set methods receiver for driver %s\nError: %v", + driverName, err)) + } } diff --git a/driver/gomethods/mongodb/mongodb.go b/driver/gomethods/mongodb/mongodb.go index 0acad176d..acbc2564a 100644 --- a/driver/gomethods/mongodb/mongodb.go +++ b/driver/gomethods/mongodb/mongodb.go @@ -12,14 +12,46 @@ import ( "strings" ) -const MIGRATE_DB = "db_migrations" +type UnregisteredMethodsReceiverError string + +func (e UnregisteredMethodsReceiverError) Error() string { + return "Unregistered methods receiver for driver: " + string(e) +} + +type WrongMethodsReceiverTypeError string + +func (e WrongMethodsReceiverTypeError) Error() string { + return "Wrong methods receiver type for driver: " + string(e) +} + const MIGRATE_C = "db_migrations" +const DRIVER_NAME = "gomethods.mongodb" type MongoDbGoMethodsDriver struct { Session *mgo.Session - DbName string - migrator gomethods.Migrator + methodsReceiver MethodsReceiver + migrator gomethods.Migrator +} + +var _ gomethods.GoMethodsDriver = (*MongoDbGoMethodsDriver)(nil) + +type MethodsReceiver interface { + DbName() string +} + +func (d *MongoDbGoMethodsDriver) MethodsReceiver() interface{} { + return d.methodsReceiver +} + +func (d *MongoDbGoMethodsDriver) SetMethodsReceiver(r interface{}) error { + r1, ok := r.(MethodsReceiver) + if !ok { + return WrongMethodsReceiverTypeError(DRIVER_NAME) + } + + d.methodsReceiver = r1 + return nil } func init() { @@ -32,6 +64,10 @@ type DbMigration struct { } func (driver *MongoDbGoMethodsDriver) Initialize(url string) error { + if driver.methodsReceiver == nil { + return UnregisteredMethodsReceiverError(DRIVER_NAME) + } + urlWithoutScheme := strings.SplitN(url, "mongodb://", 2) if len(urlWithoutScheme) != 2 { return errors.New("invalid mongodb:// scheme") @@ -44,7 +80,6 @@ func (driver *MongoDbGoMethodsDriver) Initialize(url string) error { session.SetMode(mgo.Monotonic, true) driver.Session = session - driver.DbName = MIGRATE_DB driver.migrator = gomethods.Migrator{MethodInvoker: driver} return nil @@ -63,7 +98,7 @@ func (driver *MongoDbGoMethodsDriver) FilenameExtension() string { func (driver *MongoDbGoMethodsDriver) Version() (uint64, error) { var latestMigration DbMigration - c := driver.Session.DB(driver.DbName).C(MIGRATE_C) + c := driver.Session.DB(driver.methodsReceiver.DbName()).C(MIGRATE_C) err := c.Find(bson.M{}).Sort("-version").One(&latestMigration) @@ -85,7 +120,7 @@ func (driver *MongoDbGoMethodsDriver) Migrate(f file.File, pipe chan interface{} return } - migrate_c := driver.Session.DB(driver.DbName).C(MIGRATE_C) + migrate_c := driver.Session.DB(driver.methodsReceiver.DbName()).C(MIGRATE_C) if f.Direction == direction.Up { id := bson.NewObjectId() @@ -106,13 +141,13 @@ func (driver *MongoDbGoMethodsDriver) Migrate(f file.File, pipe chan interface{} } } -func (driver *MongoDbGoMethodsDriver) IsValid(methodName string, methodsReceiver interface{}) bool { - return reflect.ValueOf(methodsReceiver).MethodByName(methodName).IsValid() +func (driver *MongoDbGoMethodsDriver) IsValid(methodName string) bool { + return reflect.ValueOf(driver.methodsReceiver).MethodByName(methodName).IsValid() } -func (driver *MongoDbGoMethodsDriver) Invoke(methodName string, methodsReceiver interface{}) error { +func (driver *MongoDbGoMethodsDriver) Invoke(methodName string) error { name := methodName - migrateMethod := reflect.ValueOf(methodsReceiver).MethodByName(name) + migrateMethod := reflect.ValueOf(driver.methodsReceiver).MethodByName(name) if !migrateMethod.IsValid() { return gomethods.MissingMethodError(methodName) } diff --git a/driver/gomethods/mongodb_example/methods_receiver.go b/driver/gomethods/mongodb_example/methods_receiver.go index 24433e960..6749ae786 100644 --- a/driver/gomethods/mongodb_example/methods_receiver.go +++ b/driver/gomethods/mongodb_example/methods_receiver.go @@ -2,6 +2,8 @@ package mongodb_example import ( "github.com/dimag-jfrog/migrate/driver/gomethods" + _ "github.com/dimag-jfrog/migrate/driver/gomethods" + "github.com/dimag-jfrog/migrate/driver/gomethods/mongodb" "gopkg.in/mgo.v2" "gopkg.in/mgo.v2/bson" "time" @@ -10,16 +12,24 @@ import ( type MyMgoMethodsReceiver struct { } +func (r *MyMgoMethodsReceiver) DbName() string { + return DB_NAME +} + +var _ mongodb.MethodsReceiver = (*MyMgoMethodsReceiver)(nil) + func init() { - gomethods.RegisterMethodsReceiver("MyMgoMethodsReceiver", &MyMgoMethodsReceiver{}) + gomethods.RegisterMethodsReceiverForDriver("mongodb", &MyMgoMethodsReceiver{}) } // Here goes the specific mongodb golang methods driver logic -const DB_NAME = "test" -const SHORT_DATE_LAYOUT = "2000-Jan-01" -const USERS_C = "users" -const ORGANIZATIONS_C = "organizations" +const ( + DB_NAME = "test" + SHORT_DATE_LAYOUT = "2000-Jan-01" + USERS_C = "users" + ORGANIZATIONS_C = "organizations" +) type Organization struct { Id bson.ObjectId `bson:"_id,omitempty"` diff --git a/driver/gomethods/mongodb_example/mongodb_test.go b/driver/gomethods/mongodb_example/mongodb_test.go index 2ef2797d2..fc21c9c61 100644 --- a/driver/gomethods/mongodb_example/mongodb_test.go +++ b/driver/gomethods/mongodb_example/mongodb_test.go @@ -6,6 +6,7 @@ import ( "github.com/dimag-jfrog/migrate/file" "github.com/dimag-jfrog/migrate/migrate/direction" + "github.com/dimag-jfrog/migrate/driver" "github.com/dimag-jfrog/migrate/driver/gomethods" "github.com/dimag-jfrog/migrate/driver/gomethods/mongodb" pipep "github.com/dimag-jfrog/migrate/pipe" @@ -73,20 +74,30 @@ func RunMigrationAndAssertResult( } func TestMigrate(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Fatalf("Test failed on panic: %v", r) + } + }() + //host := os.Getenv("MONGODB_PORT_27017_TCP_ADDR") //port := os.Getenv("MONGODB_PORT_27017_TCP_PORT") host := "127.0.0.1" port := "27017" driverUrl := "mongodb://" + host + ":" + port - //gomethods.RegisterMethodsReceiver("MyMgoMethodsReceiver", &MyMgoMethodsReceiver{}) - d := &mongodb.MongoDbGoMethodsDriver{} + d0 := driver.GetDriver("mongodb") + d, ok := d0.(*mongodb.MongoDbGoMethodsDriver) + if !ok { + t.Fatal("MongoDbGoMethodsDriver has not registered") + } if err := d.Initialize(driverUrl); err != nil { t.Fatal(err) } // Reset DB + d.Session.DB(DB_NAME).C(mongodb.MIGRATE_C).DropCollection() d.Session.DB(DB_NAME).C(ORGANIZATIONS_C).DropCollection() d.Session.DB(DB_NAME).C(USERS_C).DropCollection() @@ -108,7 +119,6 @@ func TestMigrate(t *testing.T) { Name: "foobar", Direction: direction.Up, Content: []byte(` - MyMgoMethodsReceiver V001_init_organizations_up V001_init_users_up `), @@ -137,7 +147,6 @@ func TestMigrate(t *testing.T) { Name: "foobar", Direction: direction.Up, Content: []byte(` - MyMgoMethodsReceiver V002_organizations_rename_location_field_to_headquarters_up V002_change_user_cleo_to_cleopatra_up `), @@ -166,7 +175,6 @@ func TestMigrate(t *testing.T) { Name: "foobar", Direction: direction.Down, Content: []byte(` - MyMgoMethodsReceiver V002_change_user_cleo_to_cleopatra_down V002_organizations_rename_location_field_to_headquarters_down `), @@ -195,7 +203,6 @@ func TestMigrate(t *testing.T) { Name: "foobar", Direction: direction.Down, Content: []byte(` - MyMgoMethodsReceiver V001_init_users_down V001_init_organizations_down `), @@ -216,7 +223,6 @@ func TestMigrate(t *testing.T) { Name: "foobar", Direction: direction.Up, Content: []byte(` - MyMgoMethodsReceiver V001_init_organizations_up V001_init_users_up v001_non_existing_method_up @@ -229,26 +235,6 @@ func TestMigrate(t *testing.T) { Errors: []error{gomethods.MissingMethodError("v001_non_existing_method_up")}, }, }, - { - name: "v0 -> v1: not defined message receiver", - file: file.File{ - Path: "/foobar", - FileName: "001_foobar.up.gm", - Version: 1, - Name: "foobar", - Direction: direction.Up, - Content: []byte(` - V001_init_organizations_up - V001_init_users_up - `), - }, - expectedResult: ExpectedMigrationResult{ - Organizations: []Organization{}, - Organizations_v2: []Organization_v2{}, - Users: []User{}, - Errors: []error{gomethods.UnregisteredMethodsReceiverError("V001_init_organizations_up")}, - }, - }, } for _, m := range migrations {