diff --git a/cli/cmd/uninstall/uninstall.go b/cli/cmd/uninstall/uninstall.go index 24689003e5..f365a0ab3f 100644 --- a/cli/cmd/uninstall/uninstall.go +++ b/cli/cmd/uninstall/uninstall.go @@ -27,10 +27,11 @@ const ( FlagReleaseName = "name" DefaultAnyReleaseName = "" - FlagWipeAllData = "wipe-all-data" - DefaultWipeAllData = false - //this is the auto-approve for wipe all data - // todo auto-approve to NOT wipe all data + FlagWipeData = "wipe-data" + DefaultWipeData = false + + FlagSkipWipeData = "skip-wipe-data" + DefaultSkipWipeData = false ) type Command struct { @@ -40,10 +41,11 @@ type Command struct { set *flag.Sets - flagNamespace string - flagReleaseName string - flagAutoApprove bool - flagWipeAllData bool + flagNamespace string + flagReleaseName string + flagAutoApprove bool + flagWipeData bool + flagSkipWipeData bool flagKubeConfig string flagKubeContext string @@ -60,19 +62,29 @@ func (c *Command) init() { Name: FlagAutoApprove, Target: &c.flagAutoApprove, Default: DefaultAutoApprove, - Usage: "Skip confirmation prompt.", + Usage: "Skip approval prompt for uninstalling Consul.", + }) + // This is like the auto-approve to wipe all data without prompting for non-interactive environments that want + // to remove everything. + f.BoolVar(&flag.BoolVar{ + Name: FlagWipeData, + Target: &c.flagWipeData, + Default: DefaultWipeData, + Usage: "Delete all PVCs, Secrets, and Service Accounts associated with Consul Helm installation without prompting for approval to delete. Only use this when persisted data from previous installations is no longer necessary.", }) + // This is like the auto-approve to NOT wipe all data without prompting for non-interactive environments that + // only want to remove the Consul Helm installation but keep the data. f.BoolVar(&flag.BoolVar{ - Name: FlagWipeAllData, - Target: &c.flagWipeAllData, - Default: DefaultWipeAllData, - Usage: "Will NOT prompt for approval before delete all PVCs, Secrets, and Service Accounts associated with Consul Helm installation. Only use this when persisted data from previous installations is no longer necessary.", + Name: FlagSkipWipeData, + Target: &c.flagSkipWipeData, + Default: DefaultSkipWipeData, + Usage: "Skip deleting all PVCs, Secrets, and Service Accounts associated with Consul Helm installation without prompting for approval to delete.", }) f.StringVar(&flag.StringVar{ Name: FlagNamespace, Target: &c.flagNamespace, Default: DefaultAllNamespaces, - Usage: fmt.Sprintf("Namespace for the Consul installation. Defaults to \"%q\".", DefaultAllNamespaces), + Usage: "Namespace for the Consul installation.", }) f.StringVar(&flag.StringVar{ Name: FlagReleaseName, @@ -214,6 +226,11 @@ func (c *Command) Run(args []string) int { } } + if c.flagSkipWipeData { + c.UI.Output("Skipping deleting PVCs, secrets, and service accounts.", terminal.WithSuccessStyle()) + return 0 + } + // At this point, even if no Helm release was found and uninstalled, there could // still be PVCs, Secrets, and Service Accounts left behind from a previous installation. // If there isn't a foundReleaseName and foundReleaseNamespace, we'll use the values of the @@ -228,8 +245,9 @@ func (c *Command) Run(args []string) int { foundReleaseNamespace = c.flagNamespace } - // Prompt with a warning for approval before deleting PVCs, Secrets and ServiceAccounts. - if !c.flagWipeAllData { + // Prompt with a warning for approval before deleting PVCs, Secrets and ServiceAccounts. If flagWipeData is true, + // then it will proceed to delete those without a prompt. + if !c.flagWipeData { confirmation, err := c.UI.Input(&terminal.Input{ Prompt: "WARNING: Proceed with deleting PVCs, Secrets, and ServiceAccounts? \n Only approve if all data from previous installation can be deleted (y/n)", Style: terminal.WarningStyle, diff --git a/cli/cmd/uninstall/uninstall_test.go b/cli/cmd/uninstall/uninstall_test.go index d875f96cb4..e0ae2e1d21 100644 --- a/cli/cmd/uninstall/uninstall_test.go +++ b/cli/cmd/uninstall/uninstall_test.go @@ -36,12 +36,6 @@ func getInitializedCommand(t *testing.T) *Command { return c } -// TestDebugger is used to play with install.go for ad-hoc testing. -//func TestDebugger(t *testing.T) { -// c := getInitializedCommand(t) -// c.Run([]string{"-auto-approve", "-f=../../config.yaml"}) -//} - func TestDeletePVCs(t *testing.T) { c := getInitializedCommand(t) c.kubernetes = fake.NewSimpleClientset() @@ -61,55 +55,73 @@ func TestDeletePVCs(t *testing.T) { }, }, } - c.kubernetes.CoreV1().PersistentVolumeClaims("default").Create(context.TODO(), pvc, metav1.CreateOptions{}) - c.kubernetes.CoreV1().PersistentVolumeClaims("default").Create(context.TODO(), pvc2, metav1.CreateOptions{}) - err := c.deletePVCs("consul", "default") + _, err := c.kubernetes.CoreV1().PersistentVolumeClaims("default").Create(context.TODO(), pvc, metav1.CreateOptions{}) + require.NoError(t, err) + _, err = c.kubernetes.CoreV1().PersistentVolumeClaims("default").Create(context.TODO(), pvc2, metav1.CreateOptions{}) + require.NoError(t, err) + err = c.deletePVCs("consul", "default") require.NoError(t, err) pvcs, err := c.kubernetes.CoreV1().PersistentVolumeClaims("default").List(context.TODO(), metav1.ListOptions{}) require.NoError(t, err) require.Len(t, pvcs.Items, 0) +} - // Clear out the client and make sure the check now passes. - //c.kubernetes = fake.NewSimpleClientset() - //err = c.checkForPreviousPVCs() - //require.NoError(t, err) - - // Add a new irrelevant PVC and make sure the check continues to pass. - //pvc = &v1.PersistentVolumeClaim{ - // ObjectMeta: metav1.ObjectMeta{ - // Name: "irrelevant-pvc", - // }, - //} - //c.kubernetes.CoreV1().PersistentVolumeClaims("default").Create(context.TODO(), pvc, metav1.CreateOptions{}) - //err = c.checkForPreviousPVCs() - //require.NoError(t, err) +func TestDeleteSecrets(t *testing.T) { + c := getInitializedCommand(t) + c.kubernetes = fake.NewSimpleClientset() + secret := &v1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-test-secret1", + Labels: map[string]string{ + "release": "consul", + }, + }, + } + secret2 := &v1.PersistentVolumeClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-test-secret2", + Labels: map[string]string{ + "release": "consul", + }, + }, + } + _, err := c.kubernetes.CoreV1().Secrets("default").Create(context.TODO(), secret, metav1.CreateOptions{}) + require.NoError(t, err) + _, err = c.kubernetes.CoreV1().Secrets("default").Create(context.TODO(), secret2, metav1.CreateOptions{}) + require.NoError(t, err) + err = c.deleteSecrets("consul", "default") + require.NoError(t, err) + secrets, err := c.kubernetes.CoreV1().Secrets("default").List(context.TODO(), metav1.ListOptions{}) + require.NoError(t, err) + require.Len(t, secrets.Items, 0) } -//func TestCheckForPreviousSecrets(t *testing.T) { -// c := getInitializedCommand(t) -// c.kubernetes = fake.NewSimpleClientset() -// secret := &v1.Secret{ -// ObjectMeta: metav1.ObjectMeta{ -// Name: "test-consul-bootstrap-acl-token", -// }, -// } -// c.kubernetes.CoreV1().Secrets("default").Create(context.TODO(), secret, metav1.CreateOptions{}) -// err := c.checkForPreviousSecrets() -// require.Error(t, err) -// require.Contains(t, err.Error(), "found consul-acl-bootstrap-token secret from previous installations: \"test-consul-bootstrap-acl-token\" in namespace \"default\". To delete, run kubectl delete secret test-consul-bootstrap-acl-token --namespace default") -// -// // Clear out the client and make sure the check now passes. -// c.kubernetes = fake.NewSimpleClientset() -// err = c.checkForPreviousSecrets() -// require.NoError(t, err) -// -// // Add a new irrelevant secret and make sure the check continues to pass. -// secret = &v1.Secret{ -// ObjectMeta: metav1.ObjectMeta{ -// Name: "irrelevant-secret", -// }, -// } -// c.kubernetes.CoreV1().Secrets("default").Create(context.TODO(), secret, metav1.CreateOptions{}) -// err = c.checkForPreviousSecrets() -// require.NoError(t, err) -//} +func TestDeleteServiceAccounts(t *testing.T) { + c := getInitializedCommand(t) + c.kubernetes = fake.NewSimpleClientset() + sa := &v1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-test-sa1", + Labels: map[string]string{ + "release": "consul", + }, + }, + } + sa2 := &v1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: "consul-test-sa2", + Labels: map[string]string{ + "release": "consul", + }, + }, + } + _, err := c.kubernetes.CoreV1().ServiceAccounts("default").Create(context.TODO(), sa, metav1.CreateOptions{}) + require.NoError(t, err) + _, err = c.kubernetes.CoreV1().ServiceAccounts("default").Create(context.TODO(), sa2, metav1.CreateOptions{}) + require.NoError(t, err) + err = c.deleteServiceAccounts("consul", "default") + require.NoError(t, err) + sas, err := c.kubernetes.CoreV1().ServiceAccounts("default").List(context.TODO(), metav1.ListOptions{}) + require.NoError(t, err) + require.Len(t, sas.Items, 0) +}