Skip to content

Commit

Permalink
atlasmigration: allow migrate down to the reviously version
Browse files Browse the repository at this point in the history
  • Loading branch information
giautm committed Apr 11, 2024
1 parent b974a1d commit ffed097
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 17 deletions.
3 changes: 3 additions & 0 deletions api/v1alpha1/atlasmigration_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ type (
// ExecOrder controls how Atlas computes and executes pending migration files to the database.
// +kubebuilder:default=linear
ExecOrder MigrateExecOrder `json:"execOrder,omitempty"`
// AllowedMigrateDown allows downgrading the database to a previous version.
// +kubebuilder:default=false
AllowedMigrateDown bool `json:"allowedMigrateDown,omitempty"`
}
// Cloud defines the Atlas Cloud configuration.
Cloud struct {
Expand Down
5 changes: 5 additions & 0 deletions charts/atlas-operator/templates/crds/crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ spec:
spec:
description: AtlasMigrationSpec defines the desired state of AtlasMigration
properties:
allowedMigrateDown:
default: false
description: AllowedMigrateDown allows downgrading the database to
a previous version.
type: boolean
baseline:
description: BaselineVersion defines the baseline version of the database
on the first migration.
Expand Down
5 changes: 5 additions & 0 deletions config/crd/bases/db.atlasgo.io_atlasmigrations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ spec:
spec:
description: AtlasMigrationSpec defines the desired state of AtlasMigration
properties:
allowedMigrateDown:
default: false
description: AllowedMigrateDown allows downgrading the database to
a previous version.
type: boolean
baseline:
description: BaselineVersion defines the baseline version of the database
on the first migration.
Expand Down
36 changes: 22 additions & 14 deletions controllers/atlasmigration_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ type (
RevisionsSchema string
Baseline string
ExecOrder string
MigrateDown bool
}
cloud struct {
URL string
Expand Down Expand Up @@ -187,7 +188,7 @@ func (r *AtlasMigrationReconciler) Reconcile(ctx context.Context, req ctrl.Reque
}
defer wd.Close()
// Reconcile given resource
status, err := r.reconcile(ctx, wd.Path(), data.Cloud, data.EnvName)
status, err := r.reconcile(ctx, wd.Path(), data)
if err != nil {
res.SetNotReady("Migrating", strings.TrimSpace(err.Error()))
r.recordErrEvent(res, err)
Expand Down Expand Up @@ -244,42 +245,48 @@ const (
)

// Reconcile the given AtlasMigration resource.
func (r *AtlasMigrationReconciler) reconcile(ctx context.Context, wd string, cloud *cloud, envName string) (_ *dbv1alpha1.AtlasMigrationStatus, _ error) {
func (r *AtlasMigrationReconciler) reconcile(ctx context.Context, wd string, data *migrationData) (_ *dbv1alpha1.AtlasMigrationStatus, _ error) {
log := ctrl.Log.WithName("atlas_migration.reconcile")
c, err := atlas.NewClient(wd, r.execPath)
if err != nil {
return nil, err
}
log.Info("reconciling migration", "env", envName)
log.Info("reconciling migration", "env", data.EnvName)
// Check if there are any pending migration files
status, err := c.MigrateStatus(ctx, &atlas.MigrateStatusParams{Env: envName})
status, err := c.MigrateStatus(ctx, &atlas.MigrateStatusParams{Env: data.EnvName})
if err != nil {
if isChecksumErr(err) {
return nil, err
}
return nil, transient(err)
}
// FIXME: Move this flag to the resource spec
allowedDowngraded := true
migrateDown := data.MigrateDown
switch {
case len(status.Pending) == 0 && len(status.Applied) > 0 && len(status.Available) < len(status.Applied):
// Migration is downgraded
log.Info("downgrading migration detected", "allowed", allowedDowngraded)
if allowedDowngraded {
log.Info("downgrading migration detected", "migrateDown", migrateDown)
if migrateDown && (data.Cloud == nil || data.Cloud.RemoteDir == nil) {
migrateDown = false
log.Info("downgrade only supported with cloud directory", "migrateDown", migrateDown)
}
if migrateDown {
// The downgrade is allowed, apply the last migration version
last := status.Available[len(status.Available)-1]
log.Info("downgrading to the last available version", "version", last.Version)
params := &atlas.MigrateDownParams{
Env: envName,
Env: data.EnvName,
ToVersion: last.Version,
Context: &atlas.DeployRunContext{
TriggerType: atlas.TriggerTypeKubernetes,
TriggerVersion: dbv1alpha1.VersionFromContext(ctx),
},
}
if cloud != nil && cloud.RemoteDir != nil {
// Use the latest tag of the remote directory
params.DirURL = fmt.Sprintf("atlas://%s", cloud.RemoteDir.Name)
if c := data.Cloud; c != nil && c.RemoteDir != nil {
// Atlas needs full version of the remote directory
// to perform the downgrade.
//
// Use the `latest` tag of the remote directory.
params.DirURL = fmt.Sprintf("atlas://%s", c.RemoteDir.Name)
}
run, err := c.MigrateDown(ctx, params)
if err != nil {
Expand All @@ -302,7 +309,8 @@ func (r *AtlasMigrationReconciler) reconcile(ctx context.Context, wd string, clo
}, nil
}
}
// The downgrade is not allowed, fall through to default behavior: No action
// The downgrade isn't allowed/supported,
// fall through to default behavior: no action
fallthrough
case len(status.Pending) == 0:
log.Info("no pending migrations")
Expand All @@ -320,7 +328,7 @@ func (r *AtlasMigrationReconciler) reconcile(ctx context.Context, wd string, clo
// There are pending migrations
// Execute Atlas CLI migrate command
report, err := c.MigrateApply(ctx, &atlas.MigrateApplyParams{
Env: envName,
Env: data.EnvName,
Context: &atlas.DeployRunContext{
TriggerType: atlas.TriggerTypeKubernetes,
TriggerVersion: dbv1alpha1.VersionFromContext(ctx),
Expand Down
9 changes: 6 additions & 3 deletions controllers/atlasmigration_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ func TestReconcile_reconcile(t *testing.T) {
defer func() {
require.NoError(t, wd.Close())
}()
status, err := tt.r.reconcile(context.Background(), wd.Path(), nil, "test")
status, err := tt.r.reconcile(context.Background(), wd.Path(), md)
require.NoError(t, err)
require.EqualValues(t, "20230412003626", status.LastAppliedVersion)
}
Expand All @@ -420,6 +420,7 @@ func TestReconcile_reconciling(t *testing.T) {
},
Spec: v1alpha1.AtlasMigrationSpec{
TargetSpec: v1alpha1.TargetSpec{URL: tt.dburl},
EnvName: "test",
Dir: v1alpha1.Dir{
Local: map[string]string{
"1.sql": "bar",
Expand Down Expand Up @@ -451,6 +452,7 @@ func TestReconcile_reconcile_upToDate(t *testing.T) {
ObjectMeta: migrationObjmeta(),
Spec: v1alpha1.AtlasMigrationSpec{
TargetSpec: v1alpha1.TargetSpec{URL: tt.dburl},
EnvName: "test",
Dir: v1alpha1.Dir{
ConfigMapRef: &corev1.LocalObjectReference{Name: "my-configmap"},
},
Expand All @@ -465,7 +467,7 @@ func TestReconcile_reconcile_upToDate(t *testing.T) {
defer func() {
require.NoError(t, wd.Close())
}()
status, err := tt.r.reconcile(context.Background(), wd.Path(), nil, "test")
status, err := tt.r.reconcile(context.Background(), wd.Path(), md)
require.NoError(t, err)
require.EqualValues(t, "20230412003626", status.LastAppliedVersion)
}
Expand All @@ -480,6 +482,7 @@ func TestReconcile_reconcile_baseline(t *testing.T) {
ObjectMeta: migrationObjmeta(),
Spec: v1alpha1.AtlasMigrationSpec{
TargetSpec: v1alpha1.TargetSpec{URL: tt.dburl},
EnvName: "test",
Dir: v1alpha1.Dir{
ConfigMapRef: &corev1.LocalObjectReference{Name: "my-configmap"},
},
Expand All @@ -495,7 +498,7 @@ func TestReconcile_reconcile_baseline(t *testing.T) {
defer func() {
require.NoError(t, wd.Close())
}()
status, err := tt.r.reconcile(context.Background(), wd.Path(), nil, "test")
status, err := tt.r.reconcile(context.Background(), wd.Path(), md)
require.NoError(t, err)
require.EqualValues(t, "20230412003628", status.LastAppliedVersion)
cli, err := atlasexec.NewClient(wd.Path(), tt.r.execPath)
Expand Down

0 comments on commit ffed097

Please sign in to comment.