From b61126a71c84eec15e2c39bc62407f8aadefdde8 Mon Sep 17 00:00:00 2001 From: Tanvir Tatla Date: Tue, 6 Feb 2024 16:49:42 -0800 Subject: [PATCH] add delete mgmt workflow --- cmd/eksctl-anywhere/cmd/deletecluster.go | 4 ++ pkg/clustermanager/deleter.go | 36 ++++++++++ pkg/workflows/interfaces/interfaces.go | 1 + pkg/workflows/interfaces/mocks/clients.go | 15 +++++ pkg/workflows/management/create_bootstrap.go | 33 ++++++++++ .../management/create_delete_bootstrap.go | 30 +++++++++ .../management/create_install_capi.go | 30 +++++++++ pkg/workflows/management/create_move_capi.go | 25 +++++++ pkg/workflows/management/delete.go | 56 ++++++++++++++++ pkg/workflows/management/delete_cluster.go | 65 +++++++++++++++++++ pkg/workflows/management/validate.go | 25 +++++++ 11 files changed, 320 insertions(+) create mode 100644 pkg/workflows/management/delete.go create mode 100644 pkg/workflows/management/delete_cluster.go diff --git a/cmd/eksctl-anywhere/cmd/deletecluster.go b/cmd/eksctl-anywhere/cmd/deletecluster.go index 3d2865065fe54..c4d21cd51d9bd 100644 --- a/cmd/eksctl-anywhere/cmd/deletecluster.go +++ b/cmd/eksctl-anywhere/cmd/deletecluster.go @@ -15,6 +15,7 @@ import ( "github.com/aws/eks-anywhere/pkg/types" "github.com/aws/eks-anywhere/pkg/validations" "github.com/aws/eks-anywhere/pkg/workflows" + "github.com/aws/eks-anywhere/pkg/workflows/management" "github.com/aws/eks-anywhere/pkg/workflows/workload" ) @@ -159,6 +160,9 @@ func (dc *deleteClusterOptions) deleteCluster(ctx context.Context) error { if features.UseControllerViaCLIWorkflow().IsActive() && clusterSpec.Cluster.IsManaged() { deleteWorkload := workload.NewDelete(deps.Provider, deps.Writer, deps.ClusterManager, deps.ClusterDeleter) err = deleteWorkload.Run(ctx, cluster, clusterSpec) + } else if features.UseControllerViaCLIWorkflow().IsActive() && clusterSpec.Cluster.IsSelfManaged() { + deleteManagement := management.NewDelete(deps.Bootstrapper, deps.Provider, deps.Writer, deps.ClusterManager, deps.ClusterDeleter, deps.GitOpsFlux) + err = deleteManagement.Run(ctx, cluster, clusterSpec) } else { err = deleteCluster.Run(ctx, cluster, clusterSpec, dc.forceCleanup, dc.managementKubeconfig) } diff --git a/pkg/clustermanager/deleter.go b/pkg/clustermanager/deleter.go index 9e5b6d6291e9d..e6ea09ebe660f 100644 --- a/pkg/clustermanager/deleter.go +++ b/pkg/clustermanager/deleter.go @@ -7,9 +7,11 @@ import ( "github.com/go-logr/logr" "github.com/pkg/errors" + "sigs.k8s.io/cluster-api/api/v1beta1" "github.com/aws/eks-anywhere/pkg/clients/kubernetes" "github.com/aws/eks-anywhere/pkg/cluster" + "github.com/aws/eks-anywhere/pkg/constants" "github.com/aws/eks-anywhere/pkg/retrier" "github.com/aws/eks-anywhere/pkg/types" ) @@ -99,3 +101,37 @@ func (a Deleter) Run(ctx context.Context, spec *cluster.Spec, managementCluster return nil } + +// DeleteCAPICluster deletes the CAPI cluster in the bootstrap cluster and waits +// until the changes are fully reconciled. +func (a Deleter) DeleteCAPICluster(ctx context.Context, cluster string, bootstrapCluster types.Cluster) error { + var client kubernetes.Client + a.log.V(3).Info("Deleting cluster spec") + err := retrier.New( + a.deleteClusterTimeout, + retrier.WithRetryPolicy(retrier.BackOffPolicy(a.retryBackOff)), + ).Retry(func() error { + var err error + client, err = a.clientFactory.BuildClientFromKubeconfig(bootstrapCluster.KubeconfigFile) + if err != nil { + return errors.Wrap(err, "building client to delete cluster") + } + + capiCluster := &v1beta1.Cluster{} + + if err := client.Get(ctx, cluster, constants.EksaSystemNamespace, capiCluster); err != nil { + return errors.Wrapf(err, "getting capi cluster") + } + + if err := client.Delete(ctx, capiCluster); err != nil { + return errors.Wrapf(err, "deleting cluster") + } + + return nil + }) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/workflows/interfaces/interfaces.go b/pkg/workflows/interfaces/interfaces.go index efd0d4dbf5444..2c00105a885a3 100644 --- a/pkg/workflows/interfaces/interfaces.go +++ b/pkg/workflows/interfaces/interfaces.go @@ -110,4 +110,5 @@ type EksaInstaller interface { // ClusterDeleter deletes the cluster. type ClusterDeleter interface { Run(ctx context.Context, spec *cluster.Spec, managementCluster types.Cluster) error + DeleteCAPICluster(ctx context.Context, cluster string, bootstrapCluster types.Cluster) error } diff --git a/pkg/workflows/interfaces/mocks/clients.go b/pkg/workflows/interfaces/mocks/clients.go index ae148d8c3f62d..897e70b4f1250 100644 --- a/pkg/workflows/interfaces/mocks/clients.go +++ b/pkg/workflows/interfaces/mocks/clients.go @@ -1,5 +1,6 @@ // Code generated by MockGen. DO NOT EDIT. // Source: github.com/aws/eks-anywhere/pkg/workflows/interfaces (interfaces: Bootstrapper,ClusterManager,GitOpsManager,Validator,CAPIManager,EksdInstaller,EksdUpgrader,PackageInstaller,ClusterUpgrader,ClusterCreator,ClientFactory,EksaInstaller,ClusterDeleter) +// Source: github.com/aws/eks-anywhere/pkg/workflows/interfaces (interfaces: Bootstrapper,ClusterManager,GitOpsManager,Validator,CAPIManager,EksdInstaller,EksdUpgrader,PackageInstaller,ClusterUpgrader,ClusterCreator,ClientFactory,EksaInstaller,ClusterDeleter) // Package mocks is a generated GoMock package. package mocks @@ -1064,6 +1065,20 @@ func (m *MockClusterDeleter) EXPECT() *MockClusterDeleterMockRecorder { return m.recorder } +// DeleteCAPICluster mocks base method. +func (m *MockClusterDeleter) DeleteCAPICluster(arg0 context.Context, arg1 string, arg2 types.Cluster) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeleteCAPICluster", arg0, arg1, arg2) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeleteCAPICluster indicates an expected call of DeleteCAPICluster. +func (mr *MockClusterDeleterMockRecorder) DeleteCAPICluster(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteCAPICluster", reflect.TypeOf((*MockClusterDeleter)(nil).DeleteCAPICluster), arg0, arg1, arg2) +} + // Run mocks base method. func (m *MockClusterDeleter) Run(arg0 context.Context, arg1 *cluster.Spec, arg2 types.Cluster) error { m.ctrl.T.Helper() diff --git a/pkg/workflows/management/create_bootstrap.go b/pkg/workflows/management/create_bootstrap.go index 139ad9e7746d0..f74ce7b5d0551 100644 --- a/pkg/workflows/management/create_bootstrap.go +++ b/pkg/workflows/management/create_bootstrap.go @@ -39,3 +39,36 @@ func (s *createBootStrapClusterTask) Restore(ctx context.Context, commandContext func (s *createBootStrapClusterTask) Checkpoint() *task.CompletedTask { return nil } + +type createBootStrapClusterForDeleteTask struct{} + +func (s *createBootStrapClusterForDeleteTask) Run(ctx context.Context, commandContext *task.CommandContext) task.Task { + logger.Info("Creating new bootstrap cluster") + + bootstrapOptions, err := commandContext.Provider.BootstrapClusterOpts(commandContext.ClusterSpec) + if err != nil { + commandContext.SetError(err) + return nil + } + + bootstrapCluster, err := commandContext.Bootstrapper.CreateBootstrapCluster(ctx, commandContext.ClusterSpec, bootstrapOptions...) + if err != nil { + commandContext.SetError(err) + return nil + } + commandContext.BootstrapCluster = bootstrapCluster + + return &installCAPIComponentsForDeleteTask{} +} + +func (s *createBootStrapClusterForDeleteTask) Name() string { + return "bootstrap-cluster-for-delete-init" +} + +func (s *createBootStrapClusterForDeleteTask) Restore(ctx context.Context, commandContext *task.CommandContext, completedTask *task.CompletedTask) (task.Task, error) { + return nil, nil +} + +func (s *createBootStrapClusterForDeleteTask) Checkpoint() *task.CompletedTask { + return nil +} diff --git a/pkg/workflows/management/create_delete_bootstrap.go b/pkg/workflows/management/create_delete_bootstrap.go index dad43c82d113a..186107a2e9383 100644 --- a/pkg/workflows/management/create_delete_bootstrap.go +++ b/pkg/workflows/management/create_delete_bootstrap.go @@ -6,6 +6,7 @@ import ( "github.com/aws/eks-anywhere/pkg/constants" "github.com/aws/eks-anywhere/pkg/logger" "github.com/aws/eks-anywhere/pkg/task" + "github.com/aws/eks-anywhere/pkg/workflows" ) type deleteBootstrapClusterTask struct{} @@ -33,3 +34,32 @@ func (s *deleteBootstrapClusterTask) Restore(ctx context.Context, commandContext func (s *deleteBootstrapClusterTask) Checkpoint() *task.CompletedTask { return nil } + +type deleteBootstrapClusterForDeleteTask struct{} + +func (s *deleteBootstrapClusterForDeleteTask) Run(ctx context.Context, commandContext *task.CommandContext) task.Task { + logger.Info("Deleting bootstrap cluster") + if err := commandContext.Bootstrapper.DeleteBootstrapCluster(ctx, commandContext.BootstrapCluster, constants.Delete, true); err != nil { + commandContext.SetError(err) + } + + if commandContext.OriginalError != nil { + return &workflows.CollectMgmtClusterDiagnosticsTask{} + } + + commandContext.Writer.CleanUp() + logger.MarkSuccess("Cluster deleted!") + return nil +} + +func (s *deleteBootstrapClusterForDeleteTask) Name() string { + return "kind-cluster-delete" +} + +func (s *deleteBootstrapClusterForDeleteTask) Restore(ctx context.Context, commandContext *task.CommandContext, completedTask *task.CompletedTask) (task.Task, error) { + return nil, nil +} + +func (s *deleteBootstrapClusterForDeleteTask) Checkpoint() *task.CompletedTask { + return nil +} diff --git a/pkg/workflows/management/create_install_capi.go b/pkg/workflows/management/create_install_capi.go index 0f32963cebba9..53d43703eb6d6 100644 --- a/pkg/workflows/management/create_install_capi.go +++ b/pkg/workflows/management/create_install_capi.go @@ -45,3 +45,33 @@ func (s *installCAPIComponentsTask) Restore(ctx context.Context, commandContext func (s *installCAPIComponentsTask) Checkpoint() *task.CompletedTask { return nil } + +type installCAPIComponentsForDeleteTask struct{} + +func (s *installCAPIComponentsForDeleteTask) Run(ctx context.Context, commandContext *task.CommandContext) task.Task { + logger.Info("Provider specific pre-capi-install-setup on bootstrap cluster") + if err := commandContext.Provider.PreCAPIInstallOnBootstrap(ctx, commandContext.BootstrapCluster, commandContext.ClusterSpec); err != nil { + commandContext.SetError(err) + return &workflows.CollectMgmtClusterDiagnosticsTask{} + } + + logger.Info("Installing cluster-api providers on bootstrap cluster") + if err := commandContext.ClusterManager.InstallCAPI(ctx, commandContext.ClusterSpec, commandContext.BootstrapCluster, commandContext.Provider); err != nil { + commandContext.SetError(err) + return &deleteManagementCluster{} + } + + return &moveClusterManagementForDeleteTask{} +} + +func (s *installCAPIComponentsForDeleteTask) Name() string { + return "install-capi-components-bootstrap-for-delete" +} + +func (s *installCAPIComponentsForDeleteTask) Restore(ctx context.Context, commandContext *task.CommandContext, completedTask *task.CompletedTask) (task.Task, error) { + return nil, nil +} + +func (s *installCAPIComponentsForDeleteTask) Checkpoint() *task.CompletedTask { + return nil +} diff --git a/pkg/workflows/management/create_move_capi.go b/pkg/workflows/management/create_move_capi.go index 4686fff7e7e5d..ab289f0b25057 100644 --- a/pkg/workflows/management/create_move_capi.go +++ b/pkg/workflows/management/create_move_capi.go @@ -39,3 +39,28 @@ func (s *moveClusterManagementTask) Restore(ctx context.Context, commandContext func (s *moveClusterManagementTask) Checkpoint() *task.CompletedTask { return nil } + +type moveClusterManagementForDeleteTask struct{} + +func (s *moveClusterManagementForDeleteTask) Run(ctx context.Context, commandContext *task.CommandContext) task.Task { + logger.Info("Moving cluster management from workload cluster to bootstrap") + err := commandContext.ClusterManager.MoveCAPI(ctx, commandContext.WorkloadCluster, commandContext.BootstrapCluster, commandContext.WorkloadCluster.Name, commandContext.ClusterSpec, types.WithNodeRef()) + if err != nil { + commandContext.SetError(err) + return &workflows.CollectDiagnosticsTask{} + } + + return &deleteManagementCluster{} +} + +func (s *moveClusterManagementForDeleteTask) Name() string { + return "capi-management-move-for-delete" +} + +func (s *moveClusterManagementForDeleteTask) Restore(ctx context.Context, commandContext *task.CommandContext, completedTask *task.CompletedTask) (task.Task, error) { + return nil, nil +} + +func (s *moveClusterManagementForDeleteTask) Checkpoint() *task.CompletedTask { + return nil +} diff --git a/pkg/workflows/management/delete.go b/pkg/workflows/management/delete.go new file mode 100644 index 0000000000000..28716c7007291 --- /dev/null +++ b/pkg/workflows/management/delete.go @@ -0,0 +1,56 @@ +package management + +import ( + "context" + + "github.com/aws/eks-anywhere/pkg/cluster" + "github.com/aws/eks-anywhere/pkg/filewriter" + "github.com/aws/eks-anywhere/pkg/providers" + "github.com/aws/eks-anywhere/pkg/task" + "github.com/aws/eks-anywhere/pkg/types" + "github.com/aws/eks-anywhere/pkg/workflows/interfaces" +) + +// Delete is the workflow that deletes a workload clusters. +type Delete struct { + bootstrapper interfaces.Bootstrapper + provider providers.Provider + writer filewriter.FileWriter + clusterManager interfaces.ClusterManager + clusterDeleter interfaces.ClusterDeleter + gitopsManager interfaces.GitOpsManager +} + +// NewDelete builds a new delete construct. +func NewDelete(bootstrapper interfaces.Bootstrapper, + provider providers.Provider, + writer filewriter.FileWriter, + clusterManager interfaces.ClusterManager, + clusterDeleter interfaces.ClusterDeleter, + gitopsManager interfaces.GitOpsManager, +) *Delete { + return &Delete{ + bootstrapper: bootstrapper, + provider: provider, + writer: writer, + clusterManager: clusterManager, + clusterDeleter: clusterDeleter, + gitopsManager: gitopsManager, + } +} + +// Run executes the tasks to delete a management cluster. +func (c *Delete) Run(ctx context.Context, workload *types.Cluster, clusterSpec *cluster.Spec) error { + commandContext := &task.CommandContext{ + Bootstrapper: c.bootstrapper, + Provider: c.provider, + Writer: c.writer, + ClusterManager: c.clusterManager, + ClusterSpec: clusterSpec, + WorkloadCluster: workload, + ClusterDeleter: c.clusterDeleter, + GitOpsManager: c.gitopsManager, + } + + return task.NewTaskRunner(&setupAndValidateDelete{}, c.writer).RunTask(ctx, commandContext) +} diff --git a/pkg/workflows/management/delete_cluster.go b/pkg/workflows/management/delete_cluster.go new file mode 100644 index 0000000000000..feff7adbb1177 --- /dev/null +++ b/pkg/workflows/management/delete_cluster.go @@ -0,0 +1,65 @@ +package management + +import ( + "context" + + "github.com/aws/eks-anywhere/pkg/logger" + "github.com/aws/eks-anywhere/pkg/task" + "github.com/aws/eks-anywhere/pkg/workflows" +) + +type deleteManagementCluster struct{} + +func (s *deleteManagementCluster) Run(ctx context.Context, commandContext *task.CommandContext) task.Task { + logger.Info("Deleting management cluster") + err := commandContext.ClusterDeleter.DeleteCAPICluster(ctx, commandContext.ClusterSpec.Cluster.Name, *commandContext.BootstrapCluster) + if err != nil { + commandContext.SetError(err) + return &workflows.CollectMgmtClusterDiagnosticsTask{} + } + + err = commandContext.Provider.PostClusterDeleteValidate(ctx, commandContext.BootstrapCluster) + if err != nil { + commandContext.SetError(err) + return &workflows.CollectMgmtClusterDiagnosticsTask{} + } + + return &cleanupGitRepo{} +} + +func (s *deleteManagementCluster) Name() string { + return "delete-management-cluster" +} + +func (s *deleteManagementCluster) Restore(ctx context.Context, commandContext *task.CommandContext, completedTask *task.CompletedTask) (task.Task, error) { + return nil, nil +} + +func (s *deleteManagementCluster) Checkpoint() *task.CompletedTask { + return nil +} + +type cleanupGitRepo struct{} + +func (s *cleanupGitRepo) Run(ctx context.Context, commandContext *task.CommandContext) task.Task { + logger.Info("Clean up Git Repo") + err := commandContext.GitOpsManager.CleanupGitRepo(ctx, commandContext.ClusterSpec) + if err != nil { + commandContext.SetError(err) + return &workflows.CollectDiagnosticsTask{} + } + + return &deleteBootstrapClusterForDeleteTask{} +} + +func (s *cleanupGitRepo) Name() string { + return "clean-up-git-repo" +} + +func (s *cleanupGitRepo) Restore(ctx context.Context, commandContext *task.CommandContext, completedTask *task.CompletedTask) (task.Task, error) { + return nil, nil +} + +func (s *cleanupGitRepo) Checkpoint() *task.CompletedTask { + return nil +} diff --git a/pkg/workflows/management/validate.go b/pkg/workflows/management/validate.go index edf97ba02347d..f7d5e3df92d03 100644 --- a/pkg/workflows/management/validate.go +++ b/pkg/workflows/management/validate.go @@ -111,3 +111,28 @@ func (s *setupAndValidateUpgrade) Checkpoint() *task.CompletedTask { Checkpoint: nil, } } + +type setupAndValidateDelete struct{} + +func (s *setupAndValidateDelete) Run(ctx context.Context, commandContext *task.CommandContext) task.Task { + logger.Info("Performing provider setup and validations") + err := commandContext.Provider.SetupAndValidateDeleteCluster(ctx, commandContext.WorkloadCluster, commandContext.ClusterSpec) + if err != nil { + commandContext.SetError(err) + return nil + } + + return &createBootStrapClusterForDeleteTask{} +} + +func (s *setupAndValidateDelete) Name() string { + return "setup-and-validate-delete" +} + +func (s *setupAndValidateDelete) Restore(ctx context.Context, commandContext *task.CommandContext, completedTask *task.CompletedTask) (task.Task, error) { + return nil, nil +} + +func (s *setupAndValidateDelete) Checkpoint() *task.CompletedTask { + return nil +}