github.com/oam-dev/kubevela@v1.9.11/references/cli/uninstall.go (about) 1 /* 2 Copyright 2021 The KubeVela Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cli 18 19 import ( 20 "bufio" 21 "context" 22 "fmt" 23 "time" 24 25 "github.com/pkg/errors" 26 "github.com/spf13/cobra" 27 corev1 "k8s.io/api/core/v1" 28 apierror "k8s.io/apimachinery/pkg/api/errors" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 apitypes "k8s.io/apimachinery/pkg/types" 31 "k8s.io/client-go/rest" 32 "sigs.k8s.io/controller-runtime/pkg/client" 33 34 "github.com/oam-dev/kubevela/apis/core.oam.dev/v1beta1" 35 "github.com/oam-dev/kubevela/apis/types" 36 pkgaddon "github.com/oam-dev/kubevela/pkg/addon" 37 "github.com/oam-dev/kubevela/pkg/oam" 38 "github.com/oam-dev/kubevela/pkg/utils/common" 39 "github.com/oam-dev/kubevela/pkg/utils/helm" 40 "github.com/oam-dev/kubevela/pkg/utils/util" 41 ) 42 43 // UnInstallArgs the args for uninstall command 44 type UnInstallArgs struct { 45 userInput *UserInput 46 helmHelper *helm.Helper 47 Args common.Args 48 Namespace string 49 Detail bool 50 force bool 51 cancel bool 52 } 53 54 // NewUnInstallCommand creates `uninstall` command to uninstall vela core 55 func NewUnInstallCommand(c common.Args, order string, ioStreams util.IOStreams) *cobra.Command { 56 unInstallArgs := &UnInstallArgs{Args: c, userInput: &UserInput{ 57 Writer: ioStreams.Out, 58 Reader: bufio.NewReader(ioStreams.In), 59 }, helmHelper: helm.NewHelper()} 60 cmd := &cobra.Command{ 61 Use: "uninstall", 62 Short: "Uninstalls KubeVela from a Kubernetes cluster.", 63 Example: `vela uninstall`, 64 Long: "Uninstalls KubeVela from a Kubernetes cluster.", 65 Args: cobra.ExactArgs(0), 66 PreRunE: func(cmd *cobra.Command, args []string) error { 67 unInstallArgs.cancel = unInstallArgs.userInput.AskBool("Would you like to uninstall KubeVela from this cluster?", &UserInputOptions{AssumeYes: assumeYes}) 68 if !unInstallArgs.cancel { 69 return nil 70 } 71 kubeClient, err := c.GetClient() 72 if err != nil { 73 return errors.Wrapf(err, "failed to get kube client") 74 } 75 76 if !unInstallArgs.force { 77 // if use --force flag will skip checking the addon 78 addons, err := checkInstallAddon(kubeClient) 79 if err != nil { 80 return errors.Wrapf(err, "cannot check installed addon") 81 } 82 if len(addons) != 0 { 83 return fmt.Errorf("these addons have been enabled :%v, please guarantee there is no application using these addons and use `vela uninstall -f` uninstall include addon ", addons) 84 } 85 } 86 87 // filter out addon related app, these app will be delete by force uninstall 88 // ignore the error, this error cannot be not nil 89 labels, _ := metav1.LabelSelectorAsSelector(&metav1.LabelSelector{ 90 MatchExpressions: []metav1.LabelSelectorRequirement{{Key: oam.LabelAddonName, Operator: metav1.LabelSelectorOpDoesNotExist}}}) 91 var apps v1beta1.ApplicationList 92 err = kubeClient.List(context.Background(), &apps, &client.ListOptions{ 93 Namespace: "", 94 LabelSelector: labels, 95 }) 96 if err != nil { 97 return errors.Wrapf(err, "failed to check app in cluster") 98 } 99 if len(apps.Items) > 0 { 100 return fmt.Errorf("please delete all applications before uninstall. using \"vela ls -A\" view all applications") 101 } 102 return nil 103 }, 104 RunE: func(cmd *cobra.Command, args []string) error { 105 if !unInstallArgs.cancel { 106 return nil 107 } 108 ioStreams.Info("Starting to uninstall KubeVela") 109 restConfig, err := c.GetConfig() 110 if err != nil { 111 return errors.Wrapf(err, "failed to get kube config, You can set KUBECONFIG env or make file ~/.kube/config") 112 } 113 kubeClient, err := c.GetClient() 114 if err != nil { 115 return errors.Wrapf(err, "failed to get kube client") 116 } 117 if unInstallArgs.force { 118 // if use --force disable all addons 119 err := forceDisableAddon(context.Background(), kubeClient, restConfig) 120 if err != nil { 121 return errors.Wrapf(err, "cannot force disabe all addons") 122 } 123 } 124 if err := unInstallArgs.helmHelper.UninstallRelease(kubeVelaReleaseName, unInstallArgs.Namespace, restConfig, unInstallArgs.Detail, ioStreams); err != nil { 125 return err 126 } 127 // Clean up vela-system namespace 128 if err := deleteNamespace(kubeClient, unInstallArgs.Namespace); err != nil { 129 return err 130 } 131 var namespace corev1.Namespace 132 var namespaceExists = true 133 if err := kubeClient.Get(cmd.Context(), apitypes.NamespacedName{Name: "kubevela"}, &namespace); err != nil { 134 if !apierror.IsNotFound(err) { 135 return fmt.Errorf("failed to check if namespace kubevela already exists: %w", err) 136 } 137 namespaceExists = false 138 } 139 if namespaceExists { 140 fmt.Printf("The namespace kubevela is exist, it is the default database of the velaux\n\n") 141 userConfirmation := unInstallArgs.userInput.AskBool("Do you want to delete it?", &UserInputOptions{assumeYes}) 142 if userConfirmation { 143 if err := deleteNamespace(kubeClient, "kubevela"); err != nil { 144 return err 145 } 146 } 147 } 148 ioStreams.Info("Successfully uninstalled KubeVela") 149 ioStreams.Info("Please delete all CRD from cluster using \"kubectl get crd |grep oam | awk '{print $1}' | xargs kubectl delete crd\"") 150 return nil 151 }, 152 Annotations: map[string]string{ 153 types.TagCommandOrder: order, 154 types.TagCommandType: types.TypeSystem, 155 }, 156 } 157 158 cmd.Flags().StringVarP(&unInstallArgs.Namespace, "namespace", "n", "vela-system", "namespace scope for installing KubeVela Core") 159 cmd.Flags().BoolVarP(&unInstallArgs.Detail, "detail", "d", true, "show detail log of installation") 160 cmd.Flags().BoolVarP(&unInstallArgs.force, "force", "f", false, "force uninstall whole vela include all addons") 161 return cmd 162 } 163 164 func deleteNamespace(kubeClient client.Client, namespace string) error { 165 var ns corev1.Namespace 166 ns.Name = namespace 167 return kubeClient.Delete(context.Background(), &ns) 168 } 169 170 func checkInstallAddon(kubeClient client.Client) ([]string, error) { 171 apps := &v1beta1.ApplicationList{} 172 if err := kubeClient.List(context.Background(), apps, client.InNamespace(types.DefaultKubeVelaNS), client.HasLabels{oam.LabelAddonName}); err != nil { 173 return nil, err 174 } 175 var res []string 176 for _, application := range apps.Items { 177 res = append(res, application.Labels[oam.LabelAddonName]) 178 } 179 return res, nil 180 } 181 182 // forceDisableAddon force delete all enabled addons, fluxcd must be the last one to be deleted 183 func forceDisableAddon(ctx context.Context, kubeClient client.Client, config *rest.Config) error { 184 addons, err := checkInstallAddon(kubeClient) 185 if err != nil { 186 return errors.Wrapf(err, "cannot check the installed addon") 187 } 188 // fluxcd addon should be deleted lastly 189 fluxcdFlag := false 190 for _, addon := range addons { 191 if addon == "fluxcd" { 192 fluxcdFlag = true 193 continue 194 } 195 if err := pkgaddon.DisableAddon(ctx, kubeClient, addon, config, true); err != nil { 196 return err 197 } 198 } 199 if fluxcdFlag { 200 timeConsumed := time.Now() 201 var addons []string 202 for { 203 // block 5 minute until other addons have been deleted 204 if time.Now().After(timeConsumed.Add(5 * time.Minute)) { 205 return fmt.Errorf("timeout disable addon, please disable theis addons: %v", addons) 206 } 207 addons, err = checkInstallAddon(kubeClient) 208 if err != nil { 209 return err 210 } 211 if len(addons) == 1 && addons[0] == "fluxcd" { 212 break 213 } 214 time.Sleep(5 * time.Second) 215 } 216 if err := pkgaddon.DisableAddon(ctx, kubeClient, "fluxcd", config, true); err != nil { 217 return err 218 } 219 timeConsumed = time.Now() 220 for { 221 if time.Now().After(timeConsumed.Add(5 * time.Minute)) { 222 return errors.New("timeout disable fluxcd addon, please disable the addon manually") 223 } 224 addons, err := checkInstallAddon(kubeClient) 225 if err != nil { 226 return err 227 } 228 if len(addons) == 0 { 229 break 230 } 231 fmt.Printf("Waiting delete the fluxcd addon, timeout left %s . \r\n", 5*time.Minute-time.Since(timeConsumed)) 232 time.Sleep(2 * time.Second) 233 } 234 } 235 return nil 236 }