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  }