Make uninstall more robust and informative
Signed-off-by: Carlisia <carlisia@vmware.com>pull/3618/head
parent
e9c997839e
commit
4fff2a4a5c
|
@ -19,13 +19,17 @@ package uninstall
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
kubeerrs "k8s.io/apimachinery/pkg/util/errors"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
|
||||
|
@ -33,82 +37,139 @@ import (
|
|||
|
||||
"github.com/vmware-tanzu/velero/pkg/client"
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd"
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd/util/output"
|
||||
"github.com/vmware-tanzu/velero/pkg/cmd/cli"
|
||||
"github.com/vmware-tanzu/velero/pkg/install"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/kube"
|
||||
)
|
||||
|
||||
// Uninstall uninstalls all components deployed using velero install command
|
||||
func Uninstall(ctx context.Context, client *kubernetes.Clientset, extensionsClient *apiextensionsclientset.Clientset, veleroNamespace string) error {
|
||||
if veleroNamespace == "" {
|
||||
veleroNamespace = "velero"
|
||||
}
|
||||
err := DeleteNamespace(ctx, client, veleroNamespace)
|
||||
if err != nil {
|
||||
return errors.WithMessagef(err, "Uninstall failed removing Velero namespace %s", veleroNamespace)
|
||||
}
|
||||
|
||||
rolebinding := install.ClusterRoleBinding(veleroNamespace)
|
||||
|
||||
err = client.RbacV1().ClusterRoleBindings().Delete(ctx, rolebinding.Name, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
return errors.WithMessagef(err, "Uninstall failed removing Velero cluster role binding %s", rolebinding)
|
||||
}
|
||||
veleroLabels := labels.FormatLabels(install.Labels())
|
||||
|
||||
crds, err := extensionsClient.ApiextensionsV1().CustomResourceDefinitions().List(ctx, metav1.ListOptions{
|
||||
LabelSelector: veleroLabels,
|
||||
})
|
||||
if err != nil {
|
||||
return errors.WithMessagef(err, "Uninstall failed listing Velero crds")
|
||||
}
|
||||
for _, removeCRD := range crds.Items {
|
||||
err = extensionsClient.ApiextensionsV1().CustomResourceDefinitions().Delete(ctx, removeCRD.ObjectMeta.Name, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
return errors.WithMessagef(err, "Uninstall failed removing CRD %s", removeCRD.ObjectMeta.Name)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("Uninstalled Velero")
|
||||
return nil
|
||||
// uninstallOptions collects all the options for uninstalling Velero from a Kubernetes cluster.
|
||||
type uninstallOptions struct {
|
||||
wait, force bool
|
||||
}
|
||||
|
||||
func DeleteNamespace(ctx context.Context, client *kubernetes.Clientset, namespace string) error {
|
||||
err := client.CoreV1().Namespaces().Delete(ctx, namespace, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
return errors.WithMessagef(err, "Delete namespace failed removing namespace %s", namespace)
|
||||
}
|
||||
return wait.Poll(1*time.Second, 3*time.Minute, func() (bool, error) {
|
||||
_, err := client.CoreV1().Namespaces().Get(ctx, namespace, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
// Commented this out because not sure if removing this is okay
|
||||
// Printing this on Uninstall will lead to confusion
|
||||
// fmt.Printf("Namespaces.Get after delete return err %v\n", err)
|
||||
return true, nil // Assume any error means the delete was successful
|
||||
}
|
||||
return false, nil
|
||||
})
|
||||
// BindFlags adds command line values to the options struct.
|
||||
func (o *uninstallOptions) BindFlags(flags *pflag.FlagSet) {
|
||||
flags.BoolVar(&o.wait, "wait", o.wait, "Wait for Velero deployment to be ready. Optional.")
|
||||
flags.BoolVar(&o.force, "force", o.force, "Forces the Velero uninstall. Optional.")
|
||||
}
|
||||
|
||||
// NewCommand creates a cobra command.
|
||||
func NewCommand(f client.Factory) *cobra.Command {
|
||||
o := &uninstallOptions{}
|
||||
|
||||
c := &cobra.Command{
|
||||
Use: "uninstall",
|
||||
Short: "Uninstall Velero",
|
||||
Long: `Uninstall Velero along with the CRDs.
|
||||
|
||||
The '--namespace' flag can be used to specify the namespace where velero is installed (default: velero).
|
||||
Use '--wait' to wait for the Velero Deployment to be ready before proceeding.
|
||||
Use '--force' to skip the prompt confirming if you want to uninstall Velero.
|
||||
`,
|
||||
Example: `# velero uninstall -n backup`,
|
||||
Example: `# velero uninstall -n staging`,
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
veleroNs := strings.TrimSpace(f.Namespace())
|
||||
cl, extCl, err := kube.GetClusterClient()
|
||||
|
||||
// Confirm if not asked to force-skip confirmation
|
||||
if !o.force {
|
||||
fmt.Println("You are about to uninstall Velero.")
|
||||
if !cli.GetConfirmation() {
|
||||
// Don't do anything unless we get confirmation
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
client, extCl, err := kube.GetClusterClient()
|
||||
cmd.CheckError(err)
|
||||
cmd.CheckError(Uninstall(context.Background(), cl, extCl, veleroNs))
|
||||
cmd.CheckError(Run(context.Background(), client, extCl, f.Namespace(), o.wait))
|
||||
},
|
||||
}
|
||||
|
||||
output.BindFlags(c.Flags())
|
||||
output.ClearOutputFlagDefault(c)
|
||||
o.BindFlags(c.Flags())
|
||||
return c
|
||||
}
|
||||
|
||||
// Run removes all components that were deployed using the Velero install command
|
||||
func Run(ctx context.Context, client *kubernetes.Clientset, extensionsClient *apiextensionsclientset.Clientset, namespace string, waitToTerminate bool) error {
|
||||
var errs []error
|
||||
|
||||
// namespace
|
||||
ns, err := client.CoreV1().Namespaces().Get(ctx, namespace, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
fmt.Printf("Velero installation namespace %q does not exist, skipping.\n", namespace)
|
||||
} else {
|
||||
errs = append(errs, errors.WithStack(err))
|
||||
}
|
||||
} else {
|
||||
if ns.Status.Phase == corev1.NamespaceTerminating {
|
||||
fmt.Printf("Velero installation namespace %q is terminating.\n", namespace)
|
||||
} else {
|
||||
err = client.CoreV1().Namespaces().Delete(ctx, ns.Name, metav1.DeleteOptions{})
|
||||
if err != nil {
|
||||
errs = append(errs, errors.WithStack(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rolebinding
|
||||
crb := install.ClusterRoleBinding(namespace)
|
||||
if err := client.RbacV1().ClusterRoleBindings().Delete(ctx, crb.Name, metav1.DeleteOptions{}); err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
fmt.Printf("Velero installation rolebinding %q does not exist, skipping.\n", crb.Name)
|
||||
} else {
|
||||
errs = append(errs, errors.WithStack(err))
|
||||
}
|
||||
}
|
||||
|
||||
// CRDs
|
||||
veleroLabels := labels.FormatLabels(install.Labels())
|
||||
crds, err := extensionsClient.ApiextensionsV1().CustomResourceDefinitions().List(ctx, metav1.ListOptions{
|
||||
LabelSelector: veleroLabels,
|
||||
})
|
||||
if err != nil {
|
||||
errs = append(errs, errors.WithStack(err))
|
||||
}
|
||||
if len(crds.Items) == 0 {
|
||||
fmt.Print("Velero CRDs do not exist, skipping.\n")
|
||||
} else {
|
||||
for _, removeCRD := range crds.Items {
|
||||
if err = extensionsClient.ApiextensionsV1().CustomResourceDefinitions().Delete(ctx, removeCRD.ObjectMeta.Name, metav1.DeleteOptions{}); err != nil {
|
||||
errs = append(errs, errors.WithStack(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if waitToTerminate && len(ns.Name) != 0 {
|
||||
fmt.Println("Waiting for Velero uninstall to complete. You may safely press ctrl-c to stop waiting - uninstall will continue in the background.")
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
|
||||
checkFunc := func() {
|
||||
nsUpdated, _ := client.CoreV1().Namespaces().Get(ctx, namespace, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
errs = append(errs, errors.WithStack(err))
|
||||
}
|
||||
|
||||
if nsUpdated.Status.Phase == corev1.NamespaceTerminating {
|
||||
fmt.Print(".")
|
||||
}
|
||||
|
||||
if nsUpdated.Status.Phase != corev1.NamespaceTerminating {
|
||||
fmt.Print("\n")
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
|
||||
wait.Until(checkFunc, 5*time.Millisecond, ctx.Done())
|
||||
|
||||
}
|
||||
|
||||
if kubeerrs.NewAggregate(errs) != nil {
|
||||
fmt.Printf("Errors while attempting to uninstall Velero: %q", kubeerrs.NewAggregate(errs))
|
||||
return kubeerrs.NewAggregate(errs)
|
||||
}
|
||||
|
||||
fmt.Println("Velero uninstalled ⛵")
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -292,7 +292,12 @@ func VeleroInstall(ctx context.Context, veleroImage string, veleroNamespace stri
|
|||
}
|
||||
|
||||
func VeleroUninstall(ctx context.Context, client *kubernetes.Clientset, extensionsClient *apiextensionsclient.Clientset, veleroNamespace string) error {
|
||||
return uninstall.Uninstall(ctx, client, extensionsClient, veleroNamespace)
|
||||
err := uninstall.Run(ctx, client, extensionsClient, veleroNamespace, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func VeleroBackupLogs(ctx context.Context, veleroCLI string, veleroNamespace string, backupName string) error {
|
||||
|
|
Loading…
Reference in New Issue