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 }