mirror of https://github.com/portainer/portainer
126 lines
3.7 KiB
Go
126 lines
3.7 KiB
Go
package migrator
|
|
|
|
import (
|
|
"reflect"
|
|
"runtime"
|
|
|
|
"github.com/pkg/errors"
|
|
portainer "github.com/portainer/portainer/api"
|
|
|
|
"github.com/Masterminds/semver"
|
|
"github.com/rs/zerolog/log"
|
|
)
|
|
|
|
func migrationError(err error, context string) error {
|
|
return errors.Wrap(err, "failed in "+context)
|
|
}
|
|
|
|
func GetFunctionName(i interface{}) string {
|
|
return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
|
|
}
|
|
|
|
// Migrate checks the database version and migrate the existing data to the most recent data model.
|
|
func (m *Migrator) Migrate() error {
|
|
version, err := m.versionService.Version()
|
|
if err != nil {
|
|
return migrationError(err, "get version service")
|
|
}
|
|
|
|
schemaVersion, err := semver.NewVersion(version.SchemaVersion)
|
|
if err != nil {
|
|
return migrationError(err, "invalid db schema version")
|
|
}
|
|
|
|
newMigratorCount := 0
|
|
apiVersion := semver.MustParse(portainer.APIVersion)
|
|
if schemaVersion.Equal(apiVersion) {
|
|
// detect and run migrations when the versions are the same.
|
|
// e.g. development builds
|
|
latestMigrations := m.LatestMigrations()
|
|
if latestMigrations.Version.Equal(schemaVersion) &&
|
|
version.MigratorCount != len(latestMigrations.MigrationFuncs) {
|
|
err := runMigrations(latestMigrations.MigrationFuncs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
newMigratorCount = len(latestMigrations.MigrationFuncs)
|
|
}
|
|
} else {
|
|
// regular path when major/minor/patch versions differ
|
|
for _, migration := range m.migrations {
|
|
if schemaVersion.LessThan(migration.Version) {
|
|
|
|
log.Info().Msgf("migrating data to %s", migration.Version.String())
|
|
err := runMigrations(migration.MigrationFuncs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if apiVersion.Equal(migration.Version) {
|
|
newMigratorCount = len(migration.MigrationFuncs)
|
|
}
|
|
}
|
|
}
|
|
|
|
err = m.Always()
|
|
if err != nil {
|
|
return migrationError(err, "Always migrations returned error")
|
|
}
|
|
|
|
version.SchemaVersion = portainer.APIVersion
|
|
version.MigratorCount = newMigratorCount
|
|
|
|
err = m.versionService.UpdateVersion(version)
|
|
if err != nil {
|
|
return migrationError(err, "StoreDBVersion")
|
|
}
|
|
|
|
log.Info().Msgf("db migrated to %s", portainer.APIVersion)
|
|
|
|
return nil
|
|
}
|
|
|
|
func runMigrations(migrationFuncs []func() error) error {
|
|
for _, migrationFunc := range migrationFuncs {
|
|
err := migrationFunc()
|
|
if err != nil {
|
|
return migrationError(err, GetFunctionName(migrationFunc))
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (m *Migrator) NeedsMigration() bool {
|
|
// we need to migrate if anything changes with the version in the DB vs what our software version is.
|
|
// If the version matches, then it's all down to the number of migration funcs we have for the current version
|
|
// i.e. the MigratorCount
|
|
|
|
// In this particular instance we should log a fatal error
|
|
if m.CurrentDBEdition() != portainer.PortainerCE {
|
|
log.Fatal().Msg("the Portainer database is set for Portainer Business Edition, please follow the instructions in our documentation to downgrade it: https://documentation.portainer.io/v2.0-be/downgrade/be-to-ce/")
|
|
return false
|
|
}
|
|
|
|
if m.CurrentSemanticDBVersion().LessThan(semver.MustParse(portainer.APIVersion)) {
|
|
return true
|
|
}
|
|
|
|
// Check if we have any migrations for the current version
|
|
latestMigrations := m.LatestMigrations()
|
|
if latestMigrations.Version.Equal(semver.MustParse(portainer.APIVersion)) {
|
|
if m.currentDBVersion.MigratorCount != len(latestMigrations.MigrationFuncs) {
|
|
return true
|
|
}
|
|
} else {
|
|
// One remaining possibility if we get here. If our migrator count > 0 and we have no migration funcs
|
|
// for the current version (i.e. they were deleted during development). Then we we need to migrate.
|
|
// This is to reset the migrator count back to 0
|
|
if m.currentDBVersion.MigratorCount > 0 {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|