From a0af4dd7b6546142a4ec8b0da6f80c25d34804b0 Mon Sep 17 00:00:00 2001 From: Slava Lysunkin Date: Tue, 3 Sep 2024 15:13:49 -0500 Subject: [PATCH] Improved HelmRelease deletion --- .gitignore | 3 + api/v1alpha1/deployment_types.go | 3 + internal/controller/deployment_controller.go | 95 ++++++++++++++++++- .../aws-hosted-cp/templates/awscluster.yaml | 2 + templates/hmc/templates/rbac/roles.yaml | 17 ++++ 5 files changed, 118 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 884169b67..d655382e7 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ go.work.sum *.swp *.swo *~ + +# Vendoring directory +vendor diff --git a/api/v1alpha1/deployment_types.go b/api/v1alpha1/deployment_types.go index 93a69fe14..880af2951 100644 --- a/api/v1alpha1/deployment_types.go +++ b/api/v1alpha1/deployment_types.go @@ -23,10 +23,13 @@ import ( const ( DeploymentFinalizer = "hmc.mirantis.com/deployment" + BlockingFinalizer = "hmc.mirantis.com/do-not-delete" FluxHelmChartNameKey = "helm.toolkit.fluxcd.io/name" HMCManagedLabelKey = "hmc.mirantis.com/managed" HMCManagedLabelValue = "true" + + ClusterNameLabelKey = "cluster.x-k8s.io/cluster-name" ) const ( diff --git a/internal/controller/deployment_controller.go b/internal/controller/deployment_controller.go index 1767a5bf3..8a3737813 100644 --- a/internal/controller/deployment_controller.go +++ b/internal/controller/deployment_controller.go @@ -18,10 +18,9 @@ import ( "context" "errors" "fmt" + "slices" "time" - "k8s.io/apimachinery/pkg/labels" - hcv2 "github.com/fluxcd/helm-controller/api/v2" fluxmeta "github.com/fluxcd/pkg/apis/meta" fluxconditions "github.com/fluxcd/pkg/runtime/conditions" @@ -33,6 +32,7 @@ import ( apimeta "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" @@ -395,6 +395,12 @@ func (r *DeploymentReconciler) Delete(ctx context.Context, l logr.Logger, deploy } return ctrl.Result{}, err } + + err = r.releaseAWSCluster(ctx, deployment.Namespace, hr.Name, deployment.Spec.Template) + if err != nil { + return ctrl.Result{}, err + } + err = helm.DeleteHelmRelease(ctx, r.Client, deployment.Name, deployment.Namespace) if err != nil { return ctrl.Result{}, err @@ -403,6 +409,91 @@ func (r *DeploymentReconciler) Delete(ctx context.Context, l logr.Logger, deploy return ctrl.Result{RequeueAfter: 10 * time.Second}, nil } +func (r *DeploymentReconciler) releaseAWSCluster(ctx context.Context, namespace, clusterName, templateName string) error { + aws, err := r.hasAWSProvider(ctx, templateName) + if err != nil { + return err + } + if !aws { + // nothing to do - we can skip this step + return nil + } + + found, err := r.machinesAvailable(ctx, namespace, clusterName) + if err != nil { + return err + } + + if !found { + return r.removeAWSClusterFinalizer(ctx, namespace, clusterName) + } + + return nil +} + +func (r *DeploymentReconciler) hasAWSProvider(ctx context.Context, templateName string) (bool, error) { + template := &hmc.Template{} + templateRef := types.NamespacedName{Name: templateName, Namespace: hmc.TemplatesNamespace} + if err := r.Get(ctx, templateRef, template); err != nil { + log.FromContext(ctx).Error(err, "Failed to get Template") + return false, err + } + return slices.Contains(template.Status.Providers.InfrastructureProviders, "aws"), nil +} + +func (r *DeploymentReconciler) removeAWSClusterFinalizer(ctx context.Context, namespace, clusterName string) error { + l := log.FromContext(ctx) + opts := &client.ListOptions{ + LabelSelector: labels.SelectorFromSet(map[string]string{hmc.FluxHelmChartNameKey: clusterName}), + } + gvk := schema.GroupVersionKind{ + Group: "infrastructure.cluster.x-k8s.io", + Version: "v1beta2", + Kind: "awscluster", + } + itemsList := &metav1.PartialObjectMetadataList{} + itemsList.SetGroupVersionKind(gvk) + if err := r.Client.List(ctx, itemsList, opts); err != nil { + return err + } + if len(itemsList.Items) == 0 { + l.Info("AWSCluster object not found", "awscluster", clusterName) + return nil + } + + cluster := itemsList.Items[0] + + l.Info("Ensure we allow to stop AWSCluster", "finalizer", hmc.BlockingFinalizer) + originalCluster := cluster + finalizersUpdated := controllerutil.RemoveFinalizer(&cluster, hmc.BlockingFinalizer) + if finalizersUpdated { + if err := r.Client.Patch(ctx, &originalCluster, client.MergeFrom(&cluster)); err != nil { + return fmt.Errorf("failed to patch cluster %s/%s: %w", namespace, clusterName, err) + } + } + + return nil +} + +func (r *DeploymentReconciler) machinesAvailable(ctx context.Context, namespace, clusterName string) (bool, error) { + opts := &client.ListOptions{ + LabelSelector: labels.SelectorFromSet(map[string]string{hmc.ClusterNameLabelKey: clusterName}), + Namespace: namespace, + Limit: 1, + } + gvk := schema.GroupVersionKind{ + Group: "cluster.x-k8s.io", + Version: "v1beta1", + Kind: "machine", + } + itemsList := &metav1.PartialObjectMetadataList{} + itemsList.SetGroupVersionKind(gvk) + if err := r.Client.List(ctx, itemsList, opts); err != nil { + return false, err + } + return len(itemsList.Items) != 0, nil +} + // SetupWithManager sets up the controller with the Manager. func (r *DeploymentReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). diff --git a/templates/aws-hosted-cp/templates/awscluster.yaml b/templates/aws-hosted-cp/templates/awscluster.yaml index c6dc896fc..65e370873 100644 --- a/templates/aws-hosted-cp/templates/awscluster.yaml +++ b/templates/aws-hosted-cp/templates/awscluster.yaml @@ -4,6 +4,8 @@ metadata: name: {{ include "cluster.name" . }} annotations: cluster.x-k8s.io/managed-by: k0smotron + finalizers: + - hmc.mirantis.com/do-not-delete spec: region: {{ .Values.region }} # identityRef: diff --git a/templates/hmc/templates/rbac/roles.yaml b/templates/hmc/templates/rbac/roles.yaml index 182595578..4b9f2b41f 100644 --- a/templates/hmc/templates/rbac/roles.yaml +++ b/templates/hmc/templates/rbac/roles.yaml @@ -121,6 +121,23 @@ rules: - certificates verbs: - create +- apiGroups: + - infrastructure.cluster.x-k8s.io + resources: + - awsclusters + verbs: + - get + - list + - patch + - watch +- apiGroups: + - cluster.x-k8s.io + resources: + - machines + verbs: + - get + - list + - watch --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role