github.com/kubeshop/testkube@v1.17.23/pkg/migrator/migrator.go (about) 1 package migrator 2 3 import ( 4 "fmt" 5 "strings" 6 7 "go.uber.org/zap" 8 9 "github.com/kubeshop/testkube/pkg/log" 10 "github.com/kubeshop/testkube/pkg/process" 11 "github.com/kubeshop/testkube/pkg/semver" 12 ) 13 14 type Migration interface { 15 Migrate() error 16 Version() string 17 Info() string 18 Type() MigrationType 19 } 20 21 // MigrationType is migration type 22 type MigrationType int 23 24 const ( 25 // MigrationTypeClient is client migration type 26 MigrationTypeClient MigrationType = iota + 1 27 // MigrationTypeServer is server migration type 28 MigrationTypeServer 29 ) 30 31 // NewMigrator returns new Migrator instance 32 func NewMigrator() *Migrator { 33 return &Migrator{ 34 Log: log.DefaultLogger, 35 } 36 } 37 38 // Migrator struct to manage migrations of Testkube API and CRDs 39 type Migrator struct { 40 Migrations []Migration 41 Log *zap.SugaredLogger 42 } 43 44 // Add adds new migration 45 func (m *Migrator) Add(migration Migration) { 46 m.Migrations = append(m.Migrations, migration) 47 } 48 49 // GetValidMigrations returns valid migration list for currentVersion 50 func (m *Migrator) GetValidMigrations(currentVersion string, migrationTypes ...MigrationType) (migrations []Migration) { 51 types := make(map[MigrationType]struct{}, len(migrationTypes)) 52 for _, migrationType := range migrationTypes { 53 types[migrationType] = struct{}{} 54 } 55 56 for _, migration := range m.Migrations { 57 if ok, err := m.IsValid(migration.Version(), currentVersion); ok && err == nil { 58 if _, ok = types[migration.Type()]; ok { 59 migrations = append(migrations, migration) 60 } 61 } 62 } 63 64 return 65 } 66 67 // Run runs migrations of passed migration types 68 func (m *Migrator) Run(currentVersion string, migrationTypes ...MigrationType) error { 69 for _, migration := range m.GetValidMigrations(currentVersion, migrationTypes...) { 70 err := migration.Migrate() 71 if err != nil { 72 return err 73 } 74 } 75 76 return nil 77 } 78 79 // IsValid checks if versions constraints are met, assuming that currentVersion 80 // is just updated version and it should be taken for migration 81 func (m Migrator) IsValid(migrationVersion, currentVersion string) (bool, error) { 82 83 // clean possible v prefixes 84 migrationVersion = strings.TrimPrefix(migrationVersion, "v") 85 currentVersion = strings.TrimPrefix(currentVersion, "v") 86 87 if migrationVersion == "" || currentVersion == "" { 88 return false, fmt.Errorf("empty version migration:'%s', current:'%s'", migrationVersion, currentVersion) 89 } 90 91 return semver.Lte(currentVersion, migrationVersion) 92 } 93 94 // ExecuteCommands executes multiple commands returns multiple commands outputs 95 func (m Migrator) ExecuteCommands(commands []string) (outputs []string, err error) { 96 for _, command := range commands { 97 out, err := process.ExecuteString(command) 98 if err != nil { 99 return outputs, err 100 } 101 102 outputs = append(outputs, string(out)) 103 } 104 105 return outputs, nil 106 }