package migrations import ( "net/http" "git.icedream.tech/icedream/loggalicious/backend/internal/database" "github.com/blang/semver" "github.com/fjl/go-couchdb" ) type MigrationFunc func(*database.DatabaseServer) error var ( orderedRegisteredVersions = []semver.Version{} registeredMigrations = map[string][]MigrationFunc{} ) func RegisteredVersions() []semver.Version { return orderedRegisteredVersions } func RegisteredMigrations(version semver.Version) []MigrationFunc { return registeredMigrations[version.String()] } func RegisterVersionIfNotRegistered(version semver.Version) { versionStr := version.String() if _, ok := registeredMigrations[versionStr]; !ok { registeredMigrations[versionStr] = []MigrationFunc{} injected := false for index, currentVersion := range orderedRegisteredVersions { if currentVersion.GT(version) /* first version to be newer than this version */ { newOrderedRegisteredVersions := append(orderedRegisteredVersions[0:index], version) newOrderedRegisteredVersions = append(newOrderedRegisteredVersions, orderedRegisteredVersions[index:]...) orderedRegisteredVersions = newOrderedRegisteredVersions injected = true break } } if !injected { orderedRegisteredVersions = append(orderedRegisteredVersions, version) } } } func RegisterMigration(versionStr string, cb MigrationFunc) { version := MustParseVersion(versionStr) RegisterVersionIfNotRegistered(version) registeredMigrations[versionStr] = append(registeredMigrations[versionStr], cb) } func MustParseVersion(version string) semver.Version { semverVersion, err := semver.Parse(version) if err != nil { panic(err) } return semverVersion } func Run(s *database.DatabaseServer) (err error) { // Fetch current version migrationDatabase := s.GetMigrationDatabase() version, rev, err := migrationDatabase.GetVersion() if err != nil { isActuallyAnError := true if cerr, ok := err.(*couchdb.Error); ok { // CouchDB error if cerr.StatusCode == http.StatusNotFound { isActuallyAnError = false } } if isActuallyAnError { return } version = semver.Version{ Major: 0, Minor: 0, Patch: 0, } err = nil } for _, targetVersion := range orderedRegisteredVersions { // log.Println("Check if migration is needed:", targetVersion) if targetVersion.GT(version) { // log.Println("Migrating database:", version, "->", targetVersion) for _, cb := range registeredMigrations[targetVersion.String()] { err = cb(s) if err != nil { return } } version = targetVersion rev, err = migrationDatabase.PutVersion(version, rev) if err != nil { return } } } return }