github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/cmd/cluster/delete.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package cluster
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  
    26  	"github.com/spf13/cobra"
    27  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    30  	"k8s.io/apimachinery/pkg/runtime"
    31  	"k8s.io/apimachinery/pkg/util/errors"
    32  	"k8s.io/cli-runtime/pkg/genericiooptions"
    33  	"k8s.io/client-go/kubernetes"
    34  	"k8s.io/klog/v2"
    35  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    36  	"k8s.io/kubectl/pkg/util/templates"
    37  
    38  	appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    39  	"github.com/1aal/kubeblocks/pkg/cli/delete"
    40  	"github.com/1aal/kubeblocks/pkg/cli/types"
    41  	"github.com/1aal/kubeblocks/pkg/cli/util"
    42  )
    43  
    44  var (
    45  	deleteExample = templates.Examples(`
    46  		# delete a cluster named mycluster
    47  		kbcli cluster delete mycluster
    48  		# delete a cluster by label selector
    49  		kbcli cluster delete --selector clusterdefinition.kubeblocks.io/name=apecloud-mysql
    50  `)
    51  
    52  	rbacEnabled = false
    53  )
    54  
    55  func NewDeleteCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command {
    56  	o := delete.NewDeleteOptions(f, streams, types.ClusterGVR())
    57  	o.PreDeleteHook = clusterPreDeleteHook
    58  	o.PostDeleteHook = clusterPostDeleteHook
    59  
    60  	cmd := &cobra.Command{
    61  		Use:               "delete NAME",
    62  		Short:             "Delete clusters.",
    63  		Example:           deleteExample,
    64  		ValidArgsFunction: util.ResourceNameCompletionFunc(f, types.ClusterGVR()),
    65  		Run: func(cmd *cobra.Command, args []string) {
    66  			util.CheckErr(deleteCluster(o, args))
    67  		},
    68  	}
    69  	o.AddFlags(cmd)
    70  	cmd.Flags().BoolVar(&rbacEnabled, "rbac-enabled", false, "Specify whether rbac resources will be deleted by kbcli")
    71  	return cmd
    72  }
    73  
    74  func deleteCluster(o *delete.DeleteOptions, args []string) error {
    75  	if len(args) == 0 && len(o.LabelSelector) == 0 {
    76  		return fmt.Errorf("missing cluster name or a lable selector")
    77  	}
    78  	o.Names = args
    79  	return o.Run()
    80  }
    81  
    82  func clusterPreDeleteHook(o *delete.DeleteOptions, object runtime.Object) error {
    83  	if object == nil {
    84  		return nil
    85  	}
    86  
    87  	cluster, err := getClusterFromObject(object)
    88  	if err != nil {
    89  		return err
    90  	}
    91  	if cluster.Spec.TerminationPolicy == appsv1alpha1.DoNotTerminate {
    92  		return fmt.Errorf("cluster %s is protected by termination policy %s, skip deleting", cluster.Name, appsv1alpha1.DoNotTerminate)
    93  	}
    94  	return nil
    95  }
    96  
    97  func clusterPostDeleteHook(o *delete.DeleteOptions, object runtime.Object) error {
    98  	if object == nil {
    99  		return nil
   100  	}
   101  
   102  	c, err := getClusterFromObject(object)
   103  	if err != nil {
   104  		return err
   105  	}
   106  
   107  	client, err := o.Factory.KubernetesClientSet()
   108  	if err != nil {
   109  		return err
   110  	}
   111  
   112  	if err = deleteDependencies(client, c.Namespace, c.Name); err != nil {
   113  		return err
   114  	}
   115  	return nil
   116  }
   117  
   118  func deleteDependencies(client kubernetes.Interface, ns string, name string) error {
   119  	if !rbacEnabled {
   120  		return nil
   121  	}
   122  
   123  	klog.V(1).Infof("delete dependencies for cluster %s", name)
   124  	var (
   125  		saName                 = saNamePrefix + name
   126  		roleName               = roleNamePrefix + name
   127  		roleBindingName        = roleBindingNamePrefix + name
   128  		clusterRoleName        = clusterRolePrefix + name
   129  		clusterRoleBindingName = clusterRoleBindingPrefix + name
   130  		allErr                 []error
   131  	)
   132  
   133  	// now, delete the dependencies, for postgresql, we delete sa, role and rolebinding
   134  	ctx := context.TODO()
   135  	gracePeriod := int64(0)
   136  	deleteOptions := metav1.DeleteOptions{GracePeriodSeconds: &gracePeriod}
   137  	checkErr := func(err error) bool {
   138  		if err != nil && !apierrors.IsNotFound(err) {
   139  			return true
   140  		}
   141  		return false
   142  	}
   143  
   144  	// delete cluster role binding
   145  	klog.V(1).Infof("delete cluster role binding %s", clusterRoleBindingName)
   146  	if err := client.RbacV1().ClusterRoleBindings().Delete(ctx, clusterRoleBindingName, deleteOptions); checkErr(err) {
   147  		allErr = append(allErr, err)
   148  	}
   149  
   150  	// delete cluster role
   151  	klog.V(1).Infof("delete cluster role %s", clusterRoleName)
   152  	if err := client.RbacV1().ClusterRoles().Delete(ctx, clusterRoleName, deleteOptions); checkErr(err) {
   153  		allErr = append(allErr, err)
   154  	}
   155  
   156  	// delete role binding
   157  	klog.V(1).Infof("delete role binding %s", roleBindingName)
   158  	if err := client.RbacV1().RoleBindings(ns).Delete(ctx, roleBindingName, deleteOptions); checkErr(err) {
   159  		allErr = append(allErr, err)
   160  	}
   161  
   162  	// delete role
   163  	klog.V(1).Infof("delete role %s", roleName)
   164  	if err := client.RbacV1().Roles(ns).Delete(ctx, roleName, deleteOptions); checkErr(err) {
   165  		allErr = append(allErr, err)
   166  	}
   167  
   168  	// delete service account
   169  	klog.V(1).Infof("delete service account %s", saName)
   170  	if err := client.CoreV1().ServiceAccounts(ns).Delete(ctx, saName, deleteOptions); checkErr(err) {
   171  		allErr = append(allErr, err)
   172  	}
   173  
   174  	return errors.NewAggregate(allErr)
   175  }
   176  
   177  func getClusterFromObject(object runtime.Object) (*appsv1alpha1.Cluster, error) {
   178  	if object.GetObjectKind().GroupVersionKind().Kind != appsv1alpha1.ClusterKind {
   179  		return nil, fmt.Errorf("object %s is not of kind %s", object.GetObjectKind().GroupVersionKind().Kind, appsv1alpha1.ClusterKind)
   180  	}
   181  	u := object.(*unstructured.Unstructured)
   182  	cluster := &appsv1alpha1.Cluster{}
   183  	if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, cluster); err != nil {
   184  		return nil, err
   185  	}
   186  	return cluster, nil
   187  }