diff --git a/pkg/migration/api_steps.go b/pkg/migration/api_steps.go index 28d95383..6ae56fec 100644 --- a/pkg/migration/api_steps.go +++ b/pkg/migration/api_steps.go @@ -35,6 +35,7 @@ const ( stepEditComposites stepEditClaims stepDeletionPolicyOrphan + stepRemoveFinalizers stepDeleteOldManaged stepStartManaged stepStartComposites @@ -78,6 +79,9 @@ func (pg *PlanGenerator) addStepsForManagedResource(u *UnstructuredWithMetadata) if err := pg.stepOrphanManagedResource(u, qName); err != nil { return err } + if err := pg.stepRemoveFinalizersManagedResource(u, qName); err != nil { + return err + } pg.stepDeleteOldManagedResource(u) orphaned, err := pg.stepOrhanMR(*u) if err != nil { @@ -135,6 +139,15 @@ func (pg *PlanGenerator) stepOrphanManagedResource(u *UnstructuredWithMetadata, }), errResourceOrphan) } +func (pg *PlanGenerator) stepRemoveFinalizersManagedResource(u *UnstructuredWithMetadata, qName string) error { + if !pg.stepEnabled(stepRemoveFinalizers) { + return nil + } + u.Metadata.Path = fmt.Sprintf("%s/%s.yaml", pg.stepAPI(stepRemoveFinalizers).Name, qName) + pg.stepAPI(stepRemoveFinalizers).Patch.Files = append(pg.stepAPI(stepRemoveFinalizers).Patch.Files, u.Metadata.Path) + return pg.removeFinalizers(*u) +} + func (pg *PlanGenerator) stepDeleteOldManagedResource(u *UnstructuredWithMetadata) { if !pg.stepEnabled(stepDeleteOldManaged) { return @@ -202,6 +215,21 @@ func (pg *PlanGenerator) pause(u UnstructuredWithMetadata, isPaused bool) error }), errPause) } +func (pg *PlanGenerator) removeFinalizers(u UnstructuredWithMetadata) error { + return errors.Wrap(pg.target.Put(UnstructuredWithMetadata{ + Object: unstructured.Unstructured{ + Object: addNameGVK(u.Object, map[string]any{ + "metadata": map[string]any{ + "finalizers": []any{}, + }, + }), + }, + Metadata: Metadata{ + Path: u.Metadata.Path, + }, + }), errResourceRemoveFinalizer) +} + func (pg *PlanGenerator) stepEditComposites(composites []UnstructuredWithMetadata, convertedMap map[corev1.ObjectReference][]UnstructuredWithMetadata, convertedComposition map[string]string) error { if !pg.stepEnabled(stepEditComposites) { return nil @@ -311,6 +339,9 @@ func (pg *PlanGenerator) stepAPI(s step) *Step { // nolint:gocyclo // all steps case stepDeletionPolicyOrphan: setPatchStep("deletion-policy-orphan", pg.Plan.Spec.stepMap[stepKey]) + case stepRemoveFinalizers: + setPatchStep("remove-finalizers", pg.Plan.Spec.stepMap[stepKey]) + case stepDeleteOldManaged: setDeleteStep("delete-old-managed", pg.Plan.Spec.stepMap[stepKey]) diff --git a/pkg/migration/plan_generator.go b/pkg/migration/plan_generator.go index aa629656..b74fb3af 100644 --- a/pkg/migration/plan_generator.go +++ b/pkg/migration/plan_generator.go @@ -54,6 +54,7 @@ const ( errComposedTemplateMigrate = "failed to migrate the composed templates of the composition" errResourceOutput = "failed to output migrated resource" errResourceOrphan = "failed to orphan managed resource" + errResourceRemoveFinalizer = "failed to remove finalizers of managed resource" errCompositionOutput = "failed to output migrated composition" errCompositeOutput = "failed to output migrated composite" errClaimOutput = "failed to output migrated claim" diff --git a/pkg/migration/plan_generator_test.go b/pkg/migration/plan_generator_test.go index c3315209..4b718ac3 100644 --- a/pkg/migration/plan_generator_test.go +++ b/pkg/migration/plan_generator_test.go @@ -136,6 +136,7 @@ func TestGeneratePlan(t *testing.T) { "pause-composites/my-resource-dwjgh.xmyresources.test.com.yaml", "edit-composites/my-resource-dwjgh.xmyresources.test.com.yaml", "deletion-policy-orphan/sample-vpc.vpcs.fakesourceapi.yaml", + "remove-finalizers/sample-vpc.vpcs.fakesourceapi.yaml", "new-compositions/example-migrated.compositions.apiextensions.crossplane.io.yaml", "start-composites/my-resource-dwjgh.xmyresources.test.com.yaml", "create-new-managed/sample-vpc.vpcs.faketargetapi.yaml", diff --git a/pkg/migration/plan_steps.go b/pkg/migration/plan_steps.go index e6445582..4e29ed56 100644 --- a/pkg/migration/plan_steps.go +++ b/pkg/migration/plan_steps.go @@ -17,6 +17,7 @@ package migration import ( "fmt" "sort" + "strconv" "strings" "github.com/pkg/errors" @@ -68,7 +69,7 @@ func setExecStep(name string, s *Step) { } } -func (pg *PlanGenerator) commitSteps() { +func (pg *PlanGenerator) commitSteps() { //nolint: gocyclo if len(pg.Plan.Spec.stepMap) == 0 { return } @@ -77,8 +78,23 @@ func (pg *PlanGenerator) commitSteps() { for s := range pg.Plan.Spec.stepMap { keys = append(keys, s) } - sort.Strings(keys) - + // keys slice consist of the step keys of enabled migration steps. + // step keys are the string representation of integer or float64 numbers, + // which correspond to the step execution order (greater number executes later) + // therefore needs to be sorted according to their numeric values. + // otherwise, sorting the strings directly causes faulty behavior e.g "1" < "10" < "2" + // sorting will panic if a non-numeric step key is found in keys + sort.SliceStable(keys, func(i, j int) bool { + fi, err := strconv.ParseFloat(keys[i], 64) + if err != nil { + panic(err) + } + fj, err := strconv.ParseFloat(keys[j], 64) + if err != nil { + panic(err) + } + return fi < fj + }) addManualExecution := true switch t := pg.source.(type) { case *sources: diff --git a/pkg/migration/testdata/plan/generated/migration_plan.yaml b/pkg/migration/testdata/plan/generated/migration_plan.yaml index e55ca631..1a8b4e91 100644 --- a/pkg/migration/testdata/plan/generated/migration_plan.yaml +++ b/pkg/migration/testdata/plan/generated/migration_plan.yaml @@ -61,6 +61,15 @@ spec: - "kubectl patch --type='merge' -f deletion-policy-orphan/sample-vpc.vpcs.fakesourceapi.yaml --patch-file deletion-policy-orphan/sample-vpc.vpcs.fakesourceapi.yaml" type: Patch + - patch: + type: merge + files: + - remove-finalizers/sample-vpc.vpcs.fakesourceapi.yaml + name: remove-finalizers + manualExecution: + - "kubectl patch --type='merge' -f remove-finalizers/sample-vpc.vpcs.fakesourceapi.yaml --patch-file remove-finalizers/sample-vpc.vpcs.fakesourceapi.yaml" + type: Patch + - delete: options: finalizerPolicy: Remove diff --git a/pkg/migration/testdata/plan/generated/remove-finalizers/sample-vpc.vpcs.fakesourceapi.yaml b/pkg/migration/testdata/plan/generated/remove-finalizers/sample-vpc.vpcs.fakesourceapi.yaml new file mode 100644 index 00000000..3544a379 --- /dev/null +++ b/pkg/migration/testdata/plan/generated/remove-finalizers/sample-vpc.vpcs.fakesourceapi.yaml @@ -0,0 +1,6 @@ +apiVersion: fakesourceapi/v1alpha1 +kind: VPC +metadata: + name: sample-vpc + finalizers: [] +