github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/cmd/cluster/delete_ops.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 jsonpatch "github.com/evanphx/json-patch" 27 "github.com/spf13/cobra" 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 apitypes "k8s.io/apimachinery/pkg/types" 32 "k8s.io/apimachinery/pkg/util/json" 33 "k8s.io/cli-runtime/pkg/genericiooptions" 34 cmdutil "k8s.io/kubectl/pkg/cmd/util" 35 "k8s.io/kubectl/pkg/util/templates" 36 37 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 38 "github.com/1aal/kubeblocks/pkg/cli/delete" 39 "github.com/1aal/kubeblocks/pkg/cli/types" 40 "github.com/1aal/kubeblocks/pkg/cli/util" 41 ) 42 43 var ( 44 deleteOpsExample = templates.Examples(` 45 # delete all ops belong the specified cluster 46 kbcli cluster delete-ops mycluster 47 48 # delete the specified ops belong the specify cluster 49 kbcli cluster delete-ops --name=mysql-restart-82zxv 50 `) 51 ) 52 53 func NewDeleteOpsCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { 54 o := delete.NewDeleteOptions(f, streams, types.OpsGVR()) 55 o.PreDeleteHook = preDeleteOps 56 cmd := &cobra.Command{ 57 Use: "delete-ops", 58 Short: "Delete an OpsRequest.", 59 Example: deleteOpsExample, 60 ValidArgsFunction: util.ResourceNameCompletionFunc(f, types.ClusterGVR()), 61 Run: func(cmd *cobra.Command, args []string) { 62 util.CheckErr(completeForDeleteOps(o, args)) 63 util.CheckErr(o.Run()) 64 }, 65 } 66 cmd.Flags().StringSliceVar(&o.Names, "name", []string{}, "OpsRequest names") 67 _ = cmd.RegisterFlagCompletionFunc("name", util.ResourceNameCompletionFunc(f, types.OpsGVR())) 68 o.AddFlags(cmd) 69 return cmd 70 } 71 72 func preDeleteOps(o *delete.DeleteOptions, obj runtime.Object) error { 73 unstructured := obj.(*unstructured.Unstructured) 74 opsRequest := &appsv1alpha1.OpsRequest{} 75 if err := runtime.DefaultUnstructuredConverter.FromUnstructured(unstructured.Object, opsRequest); err != nil { 76 return err 77 } 78 if opsRequest.Status.Phase != appsv1alpha1.OpsRunningPhase { 79 return nil 80 } 81 if !o.Force { 82 return fmt.Errorf(`OpsRequest "%s" is Running, you can specify "--force" to delete it`, opsRequest.Name) 83 } 84 // remove the finalizers 85 dynamic, err := o.Factory.DynamicClient() 86 if err != nil { 87 return err 88 } 89 oldOps := opsRequest.DeepCopy() 90 opsRequest.Finalizers = []string{} 91 oldData, err := json.Marshal(oldOps) 92 if err != nil { 93 return err 94 } 95 newData, err := json.Marshal(opsRequest) 96 if err != nil { 97 return err 98 } 99 patchBytes, err := jsonpatch.CreateMergePatch(oldData, newData) 100 if err != nil { 101 return err 102 } 103 _, err = dynamic.Resource(types.OpsGVR()).Namespace(opsRequest.Namespace).Patch(context.TODO(), 104 opsRequest.Name, apitypes.MergePatchType, patchBytes, metav1.PatchOptions{}) 105 return err 106 } 107 108 // completeForDeleteOps completes cmd for delete OpsRequest, if resource name 109 // is not specified, construct a label selector based on the cluster name to 110 // delete all OpeRequests belonging to the cluster. 111 func completeForDeleteOps(o *delete.DeleteOptions, args []string) error { 112 // If resource name is not empty, delete these resources by name, do not need 113 // to construct the label selector. 114 if len(o.Names) > 0 { 115 o.ConfirmedNames = o.Names 116 return nil 117 } 118 119 if len(args) == 0 { 120 return fmt.Errorf("missing cluster name") 121 } 122 123 if len(args) > 1 { 124 return fmt.Errorf("only support to delete the OpsRequests of one cluster") 125 } 126 127 o.ConfirmedNames = args 128 // If OpsRequest name is unset and cluster name is set, delete all OpsRequests belonging to the cluster 129 o.LabelSelector = util.BuildLabelSelectorByNames(o.LabelSelector, args) 130 return nil 131 }