From 32eda4b1b9d135e08bd9aed76b509038e98a07a2 Mon Sep 17 00:00:00 2001 From: Kartik-Garg Date: Mon, 3 Apr 2023 19:49:49 +0530 Subject: [PATCH] Vcluster delete --ignore-not-found Added a new flag --ignore-not-found, which ignores the resource if the flag is mentioned instead of throwing and error and exiting Signed-off-by: Kartik-Garg --- cmd/vclusterctl/cmd/delete.go | 117 ++++++++++++++++++++----------- cmd/vclusterctl/cmd/find/find.go | 13 +++- pkg/helm/helm.go | 12 +++- 3 files changed, 97 insertions(+), 45 deletions(-) diff --git a/cmd/vclusterctl/cmd/delete.go b/cmd/vclusterctl/cmd/delete.go index f9fd448c1..29b699ba8 100644 --- a/cmd/vclusterctl/cmd/delete.go +++ b/cmd/vclusterctl/cmd/delete.go @@ -3,9 +3,10 @@ package cmd import ( "context" "fmt" - "github.com/loft-sh/vcluster/pkg/util/translate" "os/exec" + "github.com/loft-sh/vcluster/pkg/util/translate" + "github.com/loft-sh/vcluster/cmd/vclusterctl/cmd/app/localkubernetes" "github.com/loft-sh/vcluster/cmd/vclusterctl/cmd/find" "k8s.io/client-go/rest" @@ -31,10 +32,11 @@ type DeleteCmd struct { DeleteNamespace bool AutoDeleteNamespace bool - rawConfig *clientcmdapi.Config - restConfig *rest.Config - kubeClient *kubernetes.Clientset - log log.Logger + ignoreNotFound bool + rawConfig *clientcmdapi.Config + restConfig *rest.Config + kubeClient *kubernetes.Clientset + log log.Logger } // NewDeleteCmd creates a new command @@ -68,6 +70,7 @@ vcluster delete test --namespace test cobraCmd.Flags().BoolVar(&cmd.KeepPVC, "keep-pvc", false, "If enabled, vcluster will not delete the persistent volume claim of the vcluster") cobraCmd.Flags().BoolVar(&cmd.DeleteNamespace, "delete-namespace", false, "If enabled, vcluster will delete the namespace of the vcluster. In the case of multi-namespace mode, will also delete all other namespaces created by vcluster") cobraCmd.Flags().BoolVar(&cmd.AutoDeleteNamespace, "auto-delete-namespace", true, "If enabled, vcluster will delete the namespace of the vcluster if it was created by vclusterctl. In the case of multi-namespace mode, will also delete all other namespaces created by vcluster") + cobraCmd.Flags().BoolVar(&cmd.ignoreNotFound, "ignore-not-found", false, "If enabled, vcluster will not error out in case vcluster does not exists") return cobraCmd } @@ -90,7 +93,12 @@ func (cmd *DeleteCmd) Run(cobraCmd *cobra.Command, args []string) error { // prepare client err = cmd.prepare(args[0]) if err != nil { - return err + var errorVclusterNotFound *find.ErrorNotFoundVcluster + if cmd.ignoreNotFound && errors.As(err, &errorVclusterNotFound) { + cmd.log.Donef("vcluster %s not found in namespace %s, ignoring since --ignore-not-found flag is set", args[0], cmd.Namespace) + } else { + return err + } } // check if namespace @@ -107,9 +115,15 @@ func (cmd *DeleteCmd) Run(cobraCmd *cobra.Command, args []string) error { cmd.log.Infof("Delete vcluster %s...", args[0]) err = helm.NewClient(cmd.rawConfig, cmd.log, helmBinaryPath).Delete(args[0], cmd.Namespace) if err != nil { - return err + var errorHelmNotFound *helm.ErrorNotFoundHelm + if cmd.ignoreNotFound && errors.As(err, &errorHelmNotFound) { + cmd.log.Donef("vcluster %s not found in namespace %s, ignoring since --ignore-not-found flag is set", args[0], cmd.Namespace) + } else { + return err + } + } else { + cmd.log.Donef("Successfully deleted virtual cluster %s in namespace %s", args[0], cmd.Namespace) } - cmd.log.Donef("Successfully deleted virtual cluster %s in namespace %s", args[0], cmd.Namespace) // try to delete the pvc if !cmd.KeepPVC && !cmd.DeleteNamespace { @@ -182,45 +196,66 @@ func (cmd *DeleteCmd) Run(cobraCmd *cobra.Command, args []string) error { } func (cmd *DeleteCmd) prepare(vClusterName string) error { - vCluster, err := find.GetVCluster(cmd.Context, vClusterName, cmd.Namespace) - if err != nil { - return err - } + if cmd.ignoreNotFound { + restConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{}).ClientConfig() + if err != nil { + return err + } + kubeClient, err := kubernetes.NewForConfig(restConfig) + if err != nil { + return err + } - // load the raw config - rawConfig, err := vCluster.ClientFactory.RawConfig() - if err != nil { - return fmt.Errorf("there is an error loading your current kube config (%v), please make sure you have access to a kubernetes cluster and the command `kubectl get namespaces` is working", err) - } - err = deleteContext(&rawConfig, find.VClusterContextName(vCluster.Name, vCluster.Namespace, vCluster.Context), vCluster.Context) - if err != nil { - return errors.Wrap(err, "delete kube context") - } + rawConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(clientcmd.NewDefaultClientConfigLoadingRules(), &clientcmd.ConfigOverrides{}).RawConfig() + if err != nil { + return err + } - rawConfig.CurrentContext = vCluster.Context - restConfig, err := vCluster.ClientFactory.ClientConfig() - if err != nil { - return err - } + cmd.rawConfig = &rawConfig + cmd.kubeClient = kubeClient + cmd.restConfig = restConfig - err = localkubernetes.CleanupLocal(vClusterName, vCluster.Namespace, &rawConfig, cmd.log) - if err != nil { - cmd.log.Warnf("error cleaning up: %v", err) - } + } else { + vCluster, err := find.GetVCluster(cmd.Context, vClusterName, cmd.Namespace) + if err != nil { + return err + } - // construct proxy name - proxyName := find.VClusterConnectBackgroundProxyName(vClusterName, vCluster.Namespace, rawConfig.CurrentContext) - _ = localkubernetes.CleanupBackgroundProxy(proxyName, cmd.log) + // load the raw config + rawConfig, err := vCluster.ClientFactory.RawConfig() + if err != nil { + return fmt.Errorf("there is an error loading your current kube config (%v), please make sure you have access to a kubernetes cluster and the command `kubectl get namespaces` is working", err) + } + err = deleteContext(&rawConfig, find.VClusterContextName(vCluster.Name, vCluster.Namespace, vCluster.Context), vCluster.Context) + if err != nil { + return errors.Wrap(err, "delete kube context") + } - kubeClient, err := kubernetes.NewForConfig(restConfig) - if err != nil { - return err - } + rawConfig.CurrentContext = vCluster.Context + restConfig, err := vCluster.ClientFactory.ClientConfig() + if err != nil { + return err + } + + err = localkubernetes.CleanupLocal(vClusterName, vCluster.Namespace, &rawConfig, cmd.log) + if err != nil { + cmd.log.Warnf("error cleaning up: %v", err) + } - cmd.Namespace = vCluster.Namespace - cmd.rawConfig = &rawConfig - cmd.restConfig = restConfig - cmd.kubeClient = kubeClient + // construct proxy name + proxyName := find.VClusterConnectBackgroundProxyName(vClusterName, vCluster.Namespace, rawConfig.CurrentContext) + _ = localkubernetes.CleanupBackgroundProxy(proxyName, cmd.log) + + kubeClient, err := kubernetes.NewForConfig(restConfig) + if err != nil { + return err + } + + cmd.Namespace = vCluster.Namespace + cmd.rawConfig = &rawConfig + cmd.restConfig = restConfig + cmd.kubeClient = kubeClient + } return nil } diff --git a/cmd/vclusterctl/cmd/find/find.go b/cmd/vclusterctl/cmd/find/find.go index c0611360e..8b41b7479 100644 --- a/cmd/vclusterctl/cmd/find/find.go +++ b/cmd/vclusterctl/cmd/find/find.go @@ -3,10 +3,11 @@ package find import ( "context" "fmt" - "github.com/loft-sh/vcluster/cmd/vclusterctl/log" "strings" "time" + "github.com/loft-sh/vcluster/cmd/vclusterctl/log" + "github.com/loft-sh/vcluster/pkg/constants" "github.com/pkg/errors" appsv1 "k8s.io/api/apps/v1" @@ -57,7 +58,7 @@ func GetVCluster(context, name, namespace string) (*VCluster, error) { if err != nil { return nil, err } else if len(vclusters) == 0 { - return nil, fmt.Errorf("couldn't find vcluster %s", name) + return nil, &ErrorNotFoundVcluster{Name: name} } else if len(vclusters) == 1 { return &vclusters[0], nil } @@ -65,6 +66,14 @@ func GetVCluster(context, name, namespace string) (*VCluster, error) { return nil, fmt.Errorf("multiple vclusters with name %s found, please specify a namespace via -n", name) } +type ErrorNotFoundVcluster struct { + Name string +} + +func (e *ErrorNotFoundVcluster) Error() string { + return fmt.Sprintf("couldn't find vcluster %s", e.Name) +} + func ListVClusters(context, name, namespace string) ([]VCluster, error) { if context == "" { var err error diff --git a/pkg/helm/helm.go b/pkg/helm/helm.go index 579bb84b5..92d8cc302 100644 --- a/pkg/helm/helm.go +++ b/pkg/helm/helm.go @@ -220,15 +220,23 @@ func (c *client) Delete(name, namespace string) error { output, err := exec.Command(c.helmPath, args...).CombinedOutput() if err != nil { if strings.Contains(string(output), "release: not found") { - return fmt.Errorf("release '%s' was not found in namespace '%s'", name, namespace) + return &ErrorNotFoundHelm{Name: name, Namespace: namespace} } - return fmt.Errorf("error executing helm delete: %s", string(output)) } return nil } +type ErrorNotFoundHelm struct { + Name string + Namespace string +} + +func (e *ErrorNotFoundHelm) Error() string { + return fmt.Sprintf("release '%s' was not found in namespace '%s'", e.Name, e.Namespace) +} + func (c *client) Exists(name, namespace string) (bool, error) { kubeConfig, err := WriteKubeConfig(c.config) if err != nil {