2021-09-27 00:52:50 +00:00
package migrator
import (
2022-04-14 04:25:13 +00:00
"reflect"
"runtime"
2021-09-27 00:52:50 +00:00
2022-11-18 00:18:09 +00:00
"github.com/pkg/errors"
2021-09-27 00:52:50 +00:00
portainer "github.com/portainer/portainer/api"
2022-09-16 16:18:44 +00:00
2022-11-18 00:18:09 +00:00
"github.com/Masterminds/semver"
2022-09-16 16:18:44 +00:00
"github.com/rs/zerolog/log"
2021-09-27 00:52:50 +00:00
)
func migrationError ( err error , context string ) error {
2022-09-28 17:56:32 +00:00
return errors . Wrap ( err , "failed in " + context )
2021-09-27 00:52:50 +00:00
}
2022-04-14 04:25:13 +00:00
func GetFunctionName ( i interface { } ) string {
return runtime . FuncForPC ( reflect . ValueOf ( i ) . Pointer ( ) ) . Name ( )
}
2021-09-27 00:52:50 +00:00
// Migrate checks the database version and migrate the existing data to the most recent data model.
func ( m * Migrator ) Migrate ( ) error {
2022-11-18 00:18:09 +00:00
version , err := m . versionService . Version ( )
2021-09-27 00:52:50 +00:00
if err != nil {
2022-11-18 00:18:09 +00:00
return migrationError ( err , "get version service" )
2021-09-27 00:52:50 +00:00
}
2022-11-18 00:18:09 +00:00
schemaVersion , err := semver . NewVersion ( version . SchemaVersion )
if err != nil {
return migrationError ( err , "invalid db schema version" )
}
2021-09-27 00:52:50 +00:00
2022-11-18 00:18:09 +00:00
newMigratorCount := 0
2022-11-28 06:28:10 +00:00
apiVersion := semver . MustParse ( portainer . APIVersion )
if schemaVersion . Equal ( apiVersion ) {
2022-11-18 00:18:09 +00:00
// detect and run migrations when the versions are the same.
// e.g. development builds
2022-12-21 00:18:51 +00:00
latestMigrations := m . LatestMigrations ( )
if latestMigrations . Version . Equal ( schemaVersion ) &&
version . MigratorCount != len ( latestMigrations . MigrationFuncs ) {
err := runMigrations ( latestMigrations . MigrationFuncs )
2022-11-18 00:18:09 +00:00
if err != nil {
return err
}
2022-12-21 00:18:51 +00:00
newMigratorCount = len ( latestMigrations . MigrationFuncs )
2022-11-18 00:18:09 +00:00
}
} else {
// regular path when major/minor/patch versions differ
for _ , migration := range m . migrations {
2022-12-21 00:18:51 +00:00
if schemaVersion . LessThan ( migration . Version ) {
2022-11-28 06:28:10 +00:00
2022-12-21 00:18:51 +00:00
log . Info ( ) . Msgf ( "migrating data to %s" , migration . Version . String ( ) )
err := runMigrations ( migration . MigrationFuncs )
2022-11-18 00:18:09 +00:00
if err != nil {
return err
}
}
2022-04-26 11:25:20 +00:00
2022-12-21 00:18:51 +00:00
if apiVersion . Equal ( migration . Version ) {
newMigratorCount = len ( migration . MigrationFuncs )
2022-11-28 06:28:10 +00:00
}
2022-11-18 00:18:09 +00:00
}
}
2022-06-03 04:00:13 +00:00
2022-11-28 06:28:10 +00:00
err = m . Always ( )
if err != nil {
return migrationError ( err , "Always migrations returned error" )
}
2022-09-22 20:05:10 +00:00
2022-11-28 06:28:10 +00:00
version . SchemaVersion = portainer . APIVersion
version . MigratorCount = newMigratorCount
2022-11-07 23:28:18 +00:00
2022-11-28 06:28:10 +00:00
err = m . versionService . UpdateVersion ( version )
if err != nil {
return migrationError ( err , "StoreDBVersion" )
2021-09-27 00:52:50 +00:00
}
2022-11-28 06:28:10 +00:00
log . Info ( ) . Msgf ( "db migrated to %s" , portainer . APIVersion )
2022-11-18 00:18:09 +00:00
return nil
}
2021-09-27 00:52:50 +00:00
2022-11-18 00:18:09 +00:00
func runMigrations ( migrationFuncs [ ] func ( ) error ) error {
for _ , migrationFunc := range migrationFuncs {
err := migrationFunc ( )
if err != nil {
return migrationError ( err , GetFunctionName ( migrationFunc ) )
2021-11-17 00:21:09 +00:00
}
}
2022-11-18 00:18:09 +00:00
return nil
}
2021-11-17 00:21:09 +00:00
2022-11-18 00:18:09 +00:00
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
2022-09-16 16:18:44 +00:00
2022-11-18 00:18:09 +00:00
// 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
2021-09-27 00:52:50 +00:00
}
2022-09-16 16:18:44 +00:00
2022-11-18 00:18:09 +00:00
if m . CurrentSemanticDBVersion ( ) . LessThan ( semver . MustParse ( portainer . APIVersion ) ) {
return true
}
// Check if we have any migrations for the current version
2022-12-21 00:18:51 +00:00
latestMigrations := m . LatestMigrations ( )
if latestMigrations . Version . Equal ( semver . MustParse ( portainer . APIVersion ) ) {
if m . currentDBVersion . MigratorCount != len ( latestMigrations . MigrationFuncs ) {
2022-11-18 00:18:09 +00:00
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
}
}
2021-09-27 00:52:50 +00:00
2022-11-18 00:18:09 +00:00
return false
2021-09-27 00:52:50 +00:00
}