Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add description field to the migration plan and add support for execution from specified step #235

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 20 additions & 10 deletions pkg/migration/api_steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,34 +278,44 @@ func (pg *PlanGenerator) stepAPI(s step) *Step { // nolint:gocyclo // all steps
pg.Plan.Spec.stepMap[stepKey] = &Step{}
switch s { // nolint:exhaustive
case stepPauseManaged:
setPatchStep("pause-managed", pg.Plan.Spec.stepMap[stepKey])
setPatchStep("pause-managed", "Adding pause annotation to the Managed Resource so that the Managed Resource is not reconciled during migration",
pg.Plan.Spec.stepMap[stepKey])

case stepPauseComposites:
setPatchStep("pause-composites", pg.Plan.Spec.stepMap[stepKey])
setPatchStep("pause-composites", "Adding pause annotation to the Composite Resource so that the Composite Resource is not reconciled during migration",
pg.Plan.Spec.stepMap[stepKey])

case stepCreateNewManaged:
setApplyStep("create-new-managed", pg.Plan.Spec.stepMap[stepKey])
setApplyStep("create-new-managed", "Creating the Managed Resource that has the new API",
pg.Plan.Spec.stepMap[stepKey])

case stepNewCompositions:
setApplyStep("new-compositions", pg.Plan.Spec.stepMap[stepKey])
setApplyStep("new-compositions", "Creating the Compositions that have the new API",
pg.Plan.Spec.stepMap[stepKey])

case stepEditComposites:
setPatchStep("edit-composites", pg.Plan.Spec.stepMap[stepKey])
setPatchStep("edit-composites", "Editing the Composite Resources with the correct references",
pg.Plan.Spec.stepMap[stepKey])

case stepEditClaims:
setPatchStep("edit-claims", pg.Plan.Spec.stepMap[stepKey])
setPatchStep("edit-claims", "Editing the Claims with the correct references",
pg.Plan.Spec.stepMap[stepKey])

case stepDeletionPolicyOrphan:
setPatchStep("deletion-policy-orphan", pg.Plan.Spec.stepMap[stepKey])
setPatchStep("deletion-policy-orphan", "Setting Deletion Policy to Orphan before deleting old Managed Resource so that physical resource is not deleted",
pg.Plan.Spec.stepMap[stepKey])

case stepDeleteOldManaged:
setDeleteStep("delete-old-managed", pg.Plan.Spec.stepMap[stepKey])
setDeleteStep("delete-old-managed", "Deleting the old Managed Resource",
pg.Plan.Spec.stepMap[stepKey])

case stepStartManaged:
setPatchStep("start-managed", pg.Plan.Spec.stepMap[stepKey])
setPatchStep("start-managed", "Removing the pause annotation to start the reconciliation of the newly created Managed Resource",
pg.Plan.Spec.stepMap[stepKey])

case stepStartComposites:
setPatchStep("start-composites", pg.Plan.Spec.stepMap[stepKey])
setPatchStep("start-composites", "Removing the pause annotation to start the reconciliation of the newly created Composite Resource",
pg.Plan.Spec.stepMap[stepKey])
default:
panic(fmt.Sprintf(errInvalidStepFmt, s))
}
Expand Down
63 changes: 42 additions & 21 deletions pkg/migration/configurationmetadata_steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,47 +125,68 @@ func (pg *PlanGenerator) stepConfigurationWithSubStep(s step, newSubStep bool) *
pg.Plan.Spec.stepMap[stepKey] = &Step{}
switch s { // nolint:gocritic,exhaustive
case stepOrphanMRs:
setPatchStep("deletion-policy-orphan", pg.Plan.Spec.stepMap[stepKey])
setPatchStep("deletion-policy-orphan", "Setting the deletion policies of Managed Resources to Orphan as a precaution against any unexpected problems that may occur during migration",
pg.Plan.Spec.stepMap[stepKey])
case stepRevertOrphanMRs:
setPatchStep("deletion-policy-delete", pg.Plan.Spec.stepMap[stepKey])
setPatchStep("deletion-policy-delete", "Setting the deletion policies of Managed Resources whose deletion policy had been set to Orphan at the beginning of the migration process, back to Delete",
pg.Plan.Spec.stepMap[stepKey])
case stepNewFamilyProvider:
setApplyStep("new-ssop", pg.Plan.Spec.stepMap[stepKey])
setApplyStep("new-ssop", "Installing the new family config provider",
pg.Plan.Spec.stepMap[stepKey])
case stepNewServiceScopedProvider:
setApplyStep("new-ssop", pg.Plan.Spec.stepMap[stepKey])
setApplyStep("new-ssop", "Installing the new family resource providers",
pg.Plan.Spec.stepMap[stepKey])
case stepConfigurationPackageDisableDepResolution:
setPatchStep("disable-dependency-resolution", pg.Plan.Spec.stepMap[stepKey])
setPatchStep("disable-dependency-resolution", "Setting the value of spec.skipDependencyResolution field to true so that dependencies of the Crossplane Configuration package are not resolved automatically, in preparation of deleting the monolithic provider packages",
pg.Plan.Spec.stepMap[stepKey])
case stepConfigurationPackageEnableDepResolution:
setPatchStep("enable-dependency-resolution", pg.Plan.Spec.stepMap[stepKey])
setPatchStep("enable-dependency-resolution", "Setting the value of spec.skipDependencyResolution field in the Configuration package back to false",
pg.Plan.Spec.stepMap[stepKey])
case stepEditConfigurationPackage:
setPatchStep("edit-configuration-package", pg.Plan.Spec.stepMap[stepKey])
setPatchStep("edit-configuration-package", "Setting the Configuration package reference (spec.package) to the new one",
pg.Plan.Spec.stepMap[stepKey])
case stepEditPackageLock:
setPatchStep("edit-package-lock", pg.Plan.Spec.stepMap[stepKey])
setPatchStep("edit-package-lock", "Deleting the Configuration package dependency from the Lock resource in preparation of deleting the monolithic provider packages",
pg.Plan.Spec.stepMap[stepKey])
case stepDeleteMonolithicProvider:
setDeleteStep("delete-monolithic-provider", pg.Plan.Spec.stepMap[stepKey])
setDeleteStep("delete-monolithic-provider", "Deleting the monolithic provider package",
pg.Plan.Spec.stepMap[stepKey])
case stepActivateFamilyProviderRevision:
setPatchStep("activate-ssop", pg.Plan.Spec.stepMap[stepKey])
setPatchStep("activate-ssop", "Activating the new family config provider after the deletion of the monolithic one",
pg.Plan.Spec.stepMap[stepKey])
case stepActivateServiceScopedProviderRevision:
setPatchStep("activate-ssop", pg.Plan.Spec.stepMap[stepKey])
setPatchStep("activate-ssop", "Activating the new family resource providers",
pg.Plan.Spec.stepMap[stepKey])
case stepEditConfigurationMetadata:
setExecStep("edit-configuration-metadata", pg.Plan.Spec.stepMap[stepKey])
setExecStep("edit-configuration-metadata", "Replacing the monolithic provider dependencies in the Configuration metadata with references to the new family providers",
pg.Plan.Spec.stepMap[stepKey])
case stepBackupMRs:
setExecStep("backup-managed-resources", pg.Plan.Spec.stepMap[stepKey])
setExecStep("backup-managed-resources", "Backing up Managed Resources",
pg.Plan.Spec.stepMap[stepKey])
case stepBackupComposites:
setExecStep("backup-composite-resources", pg.Plan.Spec.stepMap[stepKey])
setExecStep("backup-composite-resources", "Backing up Composite Resources",
pg.Plan.Spec.stepMap[stepKey])
case stepBackupClaims:
setExecStep("backup-claim-resources", pg.Plan.Spec.stepMap[stepKey])
setExecStep("backup-claim-resources", "Backing up Claims from all namespaces",
pg.Plan.Spec.stepMap[stepKey])
case stepCheckHealthFamilyProvider:
setExecStep("wait-for-healthy", pg.Plan.Spec.stepMap[stepKey])
setExecStep("wait-for-healthy", "Checking the health of new family config provider",
pg.Plan.Spec.stepMap[stepKey])
case stepCheckHealthNewServiceScopedProvider:
setExecStep("wait-for-healthy", pg.Plan.Spec.stepMap[stepKey])
setExecStep("wait-for-healthy", "Checking health of new family resource provider",
pg.Plan.Spec.stepMap[stepKey])
case stepCheckInstallationFamilyProviderRevision:
setExecStep("wait-for-installed", pg.Plan.Spec.stepMap[stepKey])
setExecStep("wait-for-installed", "Checking the installation of new family config provider",
pg.Plan.Spec.stepMap[stepKey])
case stepCheckInstallationServiceScopedProviderRevision:
setExecStep("wait-for-installed", pg.Plan.Spec.stepMap[stepKey])
setExecStep("wait-for-installed", "Checking installation of new service scoped provider",
pg.Plan.Spec.stepMap[stepKey])
case stepBuildConfiguration:
setExecStep("build-configuration", pg.Plan.Spec.stepMap[stepKey])
setExecStep("build-configuration", "Building the new Configuration package using up",
pg.Plan.Spec.stepMap[stepKey])
case stepPushConfiguration:
setExecStep("push-configuration", pg.Plan.Spec.stepMap[stepKey])
setExecStep("push-configuration", "Pushing the new Configuration package",
pg.Plan.Spec.stepMap[stepKey])
default:
panic(fmt.Sprintf(errInvalidStepFmt, s))
}
Expand Down
81 changes: 44 additions & 37 deletions pkg/migration/configurationpackage_steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,50 +30,57 @@ const (
errEditConfigurationPackageFmt = `failed to put the edited Configuration package: %s`
)

func (pg *PlanGenerator) convertConfigurationPackage(o UnstructuredWithMetadata) error {
func (pg *PlanGenerator) convertConfigurationPackage(o UnstructuredWithMetadata) error { //nolint:gocyclo
pkg, err := toConfigurationPackageV1(o.Object)
if err != nil {
return err
}

// add step for disabling the dependency resolution
// for the configuration package
s := pg.stepConfiguration(stepConfigurationPackageDisableDepResolution)
p := fmt.Sprintf("%s/%s.yaml", s.Name, getVersionedName(o.Object))
s.Patch.Files = append(s.Patch.Files, p)
if err := pg.target.Put(UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: addNameGVK(o.Object, map[string]any{
"spec": map[string]any{
"skipDependencyResolution": true,
},
}),
},
Metadata: Metadata{
Path: p,
},
}); err != nil {
return err
pv := fieldpath.Pave(o.Object.Object)
p, err := pv.GetBool("spec.skipDependencyResolution")
if err != nil && !fieldpath.IsNotFound(err) {
return errors.Wrapf(err, "failed to get the current skipping dependency resolution behavior from Configuration Package: %s", o.Object.GetName())
}
if !p {
// add step for disabling the dependency resolution
// for the configuration package
s := pg.stepConfiguration(stepConfigurationPackageDisableDepResolution)
p := fmt.Sprintf("%s/%s.yaml", s.Name, getVersionedName(o.Object))
s.Patch.Files = append(s.Patch.Files, p)
if err := pg.target.Put(UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: addNameGVK(o.Object, map[string]any{
"spec": map[string]any{
"skipDependencyResolution": true,
},
}),
},
Metadata: Metadata{
Path: p,
},
}); err != nil {
return err
}

// add step for enabling the dependency resolution
// for the configuration package
s = pg.stepConfiguration(stepConfigurationPackageEnableDepResolution)
p = fmt.Sprintf("%s/%s.yaml", s.Name, getVersionedName(o.Object))
s.Patch.Files = append(s.Patch.Files, p)
if err := pg.target.Put(UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: addNameGVK(o.Object, map[string]any{
"spec": map[string]any{
"skipDependencyResolution": false,
},
}),
},
Metadata: Metadata{
Path: p,
},
}); err != nil {
return err
// add step for enabling the dependency resolution
// for the configuration package
s = pg.stepConfiguration(stepConfigurationPackageEnableDepResolution)
p = fmt.Sprintf("%s/%s.yaml", s.Name, getVersionedName(o.Object))
s.Patch.Files = append(s.Patch.Files, p)
if err := pg.target.Put(UnstructuredWithMetadata{
Object: unstructured.Unstructured{
Object: addNameGVK(o.Object, map[string]any{
"spec": map[string]any{
"skipDependencyResolution": false,
},
}),
},
Metadata: Metadata{
Path: p,
},
}); err != nil {
return err
}
}

// add the step for editing the configuration package
Expand Down
1 change: 1 addition & 0 deletions pkg/migration/exec_steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,6 @@ func (pg *PlanGenerator) stepBuildConfiguration() {

func (pg *PlanGenerator) stepPushConfiguration() {
s := pg.stepConfiguration(stepPushConfiguration)
s.Description = fmt.Sprintf("%s to {{TARGET_CONFIGURATION_PACKAGE}}", s.Description)
s.Exec.Args = []string{"-c", "up xpkg push {{TARGET_CONFIGURATION_PACKAGE}} -f {{PKG_PATH}}"}
}
20 changes: 16 additions & 4 deletions pkg/migration/plan_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ const (
// PlanExecutor drives the execution of a plan's steps and
// uses the configured `executors` to execute those steps.
type PlanExecutor struct {
executors []Executor
plan Plan
callback ExecutorCallback
executors []Executor
plan Plan
callback ExecutorCallback
LastSuccessfulStep int
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where do we read LastSuccessfulStep?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We read this value in the family-migrator. Please see this PR.

StartIndex int
}

// Action represents an action to be taken by the PlanExecutor.
Expand Down Expand Up @@ -69,6 +71,14 @@ func WithExecutorCallback(cb ExecutorCallback) PlanExecutorOption {
}
}

// WithStartIndex configures a StartIndex for a PlanExecutor
// to modify the starting index of the execution
func WithStartIndex(startIndex int) PlanExecutorOption {
sergenyalcin marked this conversation as resolved.
Show resolved Hide resolved
return func(pe *PlanExecutor) {
pe.StartIndex = startIndex
}
}

// ExecutorCallback is the interface for the callback implementations
// to be notified while executing each Step of a migration Plan.
type ExecutorCallback interface {
Expand Down Expand Up @@ -103,14 +113,15 @@ func NewPlanExecutor(plan Plan, executors []Executor, opts ...PlanExecutorOption

func (pe *PlanExecutor) Execute() error { //nolint:gocyclo // easier to follow this way
ctx := make(map[string]any)
for i := 0; i < len(pe.plan.Spec.Steps); i++ {
for i := pe.StartIndex; i < len(pe.plan.Spec.Steps); i++ {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have access to a logger in migration.PlanExecutor? If so, we may consider adding at least a debug statement that will convey the step index we are starting execution at.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have access to a logger. One option can be adding a log to callback implementation.

var r CallbackResult
if pe.callback != nil {
r = pe.callback.StepToExecute(pe.plan.Spec.Steps[i], i)
switch r.Action {
case ActionCancel:
return nil
case ActionSkip:
pe.LastSuccessfulStep = i
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we use LastSucccessfulStep? If a step is skipped, is it still a successful one?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left that up to the reviews to talk about. In family-migrator, we return the skip action for the steps that the user has run manually. We should consider successful steps that are run manually and that the user validates it.

However, the skip action is a much more general action. Therefore, we can specify another type of action for such situations. (For manually performed steps)

Copy link
Member Author

@sergenyalcin sergenyalcin Jul 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or the another option can be that changing the name of the LastSucccessfulStep to LastPerformedStep.

This field is used for caching mechanism for plan. Please see this PR

I think, other approach can be moving this logic (storing the last performed step) to the migrator side.

continue
case ActionContinue, ActionRepeat:
}
Expand All @@ -124,6 +135,7 @@ func (pe *PlanExecutor) Execute() error { //nolint:gocyclo // easier to follow t
}
} else if pe.callback != nil {
r = pe.callback.StepSucceeded(pe.plan.Spec.Steps[i], i, diag)
pe.LastSuccessfulStep = i
}

switch r.Action {
Expand Down
2 changes: 1 addition & 1 deletion pkg/migration/plan_generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ func TestGeneratePlan(t *testing.T) {
if err != nil {
t.Fatalf("Failed to load plan file from path %s: %v", tt.want.migrationPlanPath, err)
}
if diff := cmp.Diff(p, &pg.Plan, cmpopts.IgnoreUnexported(Spec{})); diff != "" {
if diff := cmp.Diff(p, &pg.Plan, cmpopts.IgnoreUnexported(Plan{}, Spec{})); diff != "" {
t.Errorf("GeneratePlan(): -wantPlan, +gotPlan: %s", diff)
}
// compare generated migration files with the expected ones
Expand Down
12 changes: 8 additions & 4 deletions pkg/migration/plan_steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,22 @@ const (
errInvalidStepFmt = "invalid step ID: %d"
)

func setApplyStep(name string, s *Step) {
func setApplyStep(name, description string, s *Step) {
s.Name = name
s.Type = StepTypeApply
s.Apply = &ApplyStep{}
s.Description = description
}

func setPatchStep(name string, s *Step) {
func setPatchStep(name, description string, s *Step) {
s.Name = name
s.Type = StepTypePatch
s.Patch = &PatchStep{}
s.Patch.Type = PatchTypeMerge
s.Description = description
}

func setDeleteStep(name string, s *Step) {
func setDeleteStep(name, description string, s *Step) {
s.Name = name
s.Type = StepTypeDelete
deletePolicy := FinalizerPolicyRemove
Expand All @@ -58,14 +60,16 @@ func setDeleteStep(name string, s *Step) {
FinalizerPolicy: &deletePolicy,
},
}
s.Description = description
}

func setExecStep(name string, s *Step) {
func setExecStep(name, description string, s *Step) {
s.Name = name
s.Type = StepTypeExec
s.Exec = &ExecStep{
Command: "sh",
}
s.Description = description
}

func (pg *PlanGenerator) commitSteps() {
Expand Down
1 change: 1 addition & 0 deletions pkg/migration/provider_package_steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ func (pg *PlanGenerator) stepDeleteMonolith(source UnstructuredWithMetadata) err
// delete the monolithic provider package
s := pg.stepConfigurationWithSubStep(stepDeleteMonolithicProvider, true)
source.Metadata.Path = fmt.Sprintf("%s/%s.yaml", s.Name, getVersionedName(source.Object))
s.Description = fmt.Sprintf("%s: %s", s.Description, source.Object.GetName())
s.Delete.Resources = []Resource{
{
GroupVersionKind: FromGroupVersionKind(source.Object.GroupVersionKind()),
Expand Down
Loading
Loading