registry/datastore/migrations/postmigrations/migrator.go (67 lines of code) (raw):

package postmigrations import ( "context" "database/sql" "fmt" "github.com/docker/distribution/registry/bbm" "github.com/docker/distribution/registry/datastore" "github.com/docker/distribution/registry/datastore/migrations" "github.com/hashicorp/go-multierror" ) // PostDeployTypeName is the type name for post-deployment migrations. const PostDeployTypeName = "post-deployment" // MigratorImpl is the implementation of the Migrator interface for post-deployment migrations. type MigratorImpl struct { migrations.MigratorImpl // Embed the base MigratorImpl for common functionality. db *sql.DB migrations []*migrations.Migration preMigrator *migrations.DepMigratorImp bbmWorker *bbm.SyncWorker } // NewMigrator creates a new instance of MigratorImpl. func NewMigrator(dsdb *datastore.DB, opts ...MigratorOption) *MigratorImpl { var db *sql.DB if dsdb != nil { db = dsdb.DB } m := &MigratorImpl{ MigratorImpl: *migrations.NewMigrator(dsdb, migrations.WithTable(migrations.PostDeployMigrationTableName), migrations.WithMigrations(migrations.AllPostMigrations())), db: db, migrations: migrations.AllPostMigrations(), bbmWorker: bbm.NewSyncWorker(dsdb), preMigrator: &migrations.DepMigratorImp{ MigratorImpl: *migrations.NewMigrator(dsdb, migrations.WithTable(migrations.PreDeployMigrationTableName)), }, } // Apply any additional options to the migrator. for _, o := range opts { o(m) } return m } // MigratorOption enables the creation of functional options for the // configuration of the migrator. type MigratorOption func(m *MigratorImpl) // Name returns the name of the migrator. func (*MigratorImpl) Name() string { return PostDeployTypeName } // Up applies all pending up migrations. Returns the number of applied migrations and background migrations. func (m *MigratorImpl) Up(_ ...migrations.MigrationDependencyResolver) (migrations.MigrationResult, error) { return m.MigratorImpl.Up(preDeployCheckFunc(m)) } // UpN applies up to n pending up migrations. All pending migrations will be applied if n is 0. Returns the number of // applied migrations and background migrations. func (m *MigratorImpl) UpN(n int, _ ...migrations.MigrationDependencyResolver) (migrations.MigrationResult, error) { return m.MigratorImpl.UpN(n, preDeployCheckFunc(m)) } // preDeployCheckFunc is a function that checks if all required pre-deployment migrations are completed before applying a migration. func preDeployCheckFunc(m *MigratorImpl) migrations.MigrationDependencyResolver { return func(_ context.Context, migration *migrations.Migration) (int, error) { // If no migrations are required, return early if len(migration.RequiredPreDeploy) == 0 { return 0, nil } // Check if all required migrations are completed statuses := m.preMigrator.StatusCache() var depErrs *multierror.Error for _, id := range migration.RequiredPreDeploy { val, found := statuses[id] if !found { return 0, fmt.Errorf("pre-deploy migration %s, does not exist in migration source", id) } if val.AppliedAt != nil { continue } depErrs = multierror.Append(depErrs, fmt.Errorf("post-deploy migration %s cannot be applied until the following pre-deploy migrations is applied: %v", migration.Id, id)) } return 0, depErrs.ErrorOrNil() } }