From e5e06783bdccef36b4717b588bac9c339bfcbf3d Mon Sep 17 00:00:00 2001
From: stefanprodan <stefan.prodan@gmail.com>
Date: Wed, 29 Apr 2020 17:25:42 +0300
Subject: [PATCH] Implement ks delete, suspend, resume commands - add delete
 kustomization command with confirmation and warning id not suspended - add
 suspend kustomization command - add resume kustomization command - add
 suspend/resume/delete e2e tests

---
 .github/workflows/e2e.yaml      |  9 ++++
 cmd/tk/delete.go                | 21 ++++++++++
 cmd/tk/delete_kustomization.go  | 74 +++++++++++++++++++++++++++++++++
 cmd/tk/get_kustomization.go     |  4 ++
 cmd/tk/resume.go                | 14 +++++++
 cmd/tk/resume_kustomization.go  | 59 ++++++++++++++++++++++++++
 cmd/tk/suspend.go               | 14 +++++++
 cmd/tk/suspend_kustomization.go | 55 ++++++++++++++++++++++++
 8 files changed, 250 insertions(+)
 create mode 100644 cmd/tk/delete.go
 create mode 100644 cmd/tk/delete_kustomization.go
 create mode 100644 cmd/tk/resume.go
 create mode 100644 cmd/tk/resume_kustomization.go
 create mode 100644 cmd/tk/suspend.go
 create mode 100644 cmd/tk/suspend_kustomization.go

diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml
index 1c3e3cf6..99fd2160 100644
--- a/.github/workflows/e2e.yaml
+++ b/.github/workflows/e2e.yaml
@@ -74,6 +74,15 @@ jobs:
       - name: tk get kustomizations
         run: |
           ./bin/tk get kustomizations
+      - name: tk suspend kustomization
+        run: |
+          ./bin/tk suspend kustomization podinfo
+      - name: tk resume kustomization
+        run: |
+          ./bin/tk resume kustomization podinfo
+      - name: tk delete kustomization
+        run: |
+          ./bin/tk delete kustomization podinfo --silent
       - name: tk check
         run: |
           ./bin/tk check
diff --git a/cmd/tk/delete.go b/cmd/tk/delete.go
new file mode 100644
index 00000000..61fa99c0
--- /dev/null
+++ b/cmd/tk/delete.go
@@ -0,0 +1,21 @@
+package main
+
+import (
+	"github.com/spf13/cobra"
+)
+
+var deleteCmd = &cobra.Command{
+	Use:   "delete",
+	Short: "Delete commands",
+}
+
+var (
+	deleteSilent bool
+)
+
+func init() {
+	deleteCmd.PersistentFlags().BoolVarP(&deleteSilent, "silent", "", false,
+		"delete resource without asking for confirmation")
+
+	rootCmd.AddCommand(deleteCmd)
+}
diff --git a/cmd/tk/delete_kustomization.go b/cmd/tk/delete_kustomization.go
new file mode 100644
index 00000000..684c7e74
--- /dev/null
+++ b/cmd/tk/delete_kustomization.go
@@ -0,0 +1,74 @@
+package main
+
+import (
+	"context"
+	"fmt"
+
+	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
+	"github.com/manifoldco/promptui"
+	"github.com/spf13/cobra"
+	"k8s.io/apimachinery/pkg/types"
+)
+
+var deleteKsCmd = &cobra.Command{
+	Use:     "kustomization [name]",
+	Aliases: []string{"ks"},
+	Short:   "Delete kustomization",
+	RunE:    deleteKsCmdRun,
+}
+
+func init() {
+	deleteCmd.AddCommand(deleteKsCmd)
+}
+
+func deleteKsCmdRun(cmd *cobra.Command, args []string) error {
+	if len(args) < 1 {
+		return fmt.Errorf("kustomization name is required")
+	}
+	name := args[0]
+
+	ctx, cancel := context.WithTimeout(context.Background(), timeout)
+	defer cancel()
+
+	kubeClient, err := utils.kubeClient(kubeconfig)
+	if err != nil {
+		return err
+	}
+
+	namespacedName := types.NamespacedName{
+		Namespace: namespace,
+		Name:      name,
+	}
+
+	var kustomization kustomizev1.Kustomization
+	err = kubeClient.Get(ctx, namespacedName, &kustomization)
+	if err != nil {
+		return err
+	}
+
+	if !deleteSilent {
+		warning := "This action will remove the Kubernetes objects previously applied by this kustomization. "
+		if kustomization.Spec.Suspend {
+			warning = ""
+		}
+		prompt := promptui.Prompt{
+			Label: fmt.Sprintf(
+				"%sAre you sure you want to delete the %s kustomization from the %s namespace",
+				warning, name, namespace,
+			),
+			IsConfirm: true,
+		}
+		if _, err := prompt.Run(); err != nil {
+			return fmt.Errorf("aborting")
+		}
+	}
+
+	logAction("deleting kustomization %s in %s namespace", name, namespace)
+	err = kubeClient.Delete(ctx, &kustomization)
+	if err != nil {
+		return err
+	}
+	logSuccess("kustomization deleted")
+
+	return nil
+}
diff --git a/cmd/tk/get_kustomization.go b/cmd/tk/get_kustomization.go
index c3101f36..9aa33c09 100644
--- a/cmd/tk/get_kustomization.go
+++ b/cmd/tk/get_kustomization.go
@@ -43,6 +43,10 @@ func getKsCmdRun(cmd *cobra.Command, args []string) error {
 	}
 
 	for _, kustomization := range list.Items {
+		if kustomization.Spec.Suspend {
+			logSuccess("%s is suspended", kustomization.GetName())
+			break
+		}
 		isInitialized := false
 		for _, condition := range kustomization.Status.Conditions {
 			if condition.Type == kustomizev1.ReadyCondition {
diff --git a/cmd/tk/resume.go b/cmd/tk/resume.go
new file mode 100644
index 00000000..ca1c39a3
--- /dev/null
+++ b/cmd/tk/resume.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+	"github.com/spf13/cobra"
+)
+
+var resumeCmd = &cobra.Command{
+	Use:   "resume",
+	Short: "Resume commands",
+}
+
+func init() {
+	rootCmd.AddCommand(resumeCmd)
+}
diff --git a/cmd/tk/resume_kustomization.go b/cmd/tk/resume_kustomization.go
new file mode 100644
index 00000000..fc2afda7
--- /dev/null
+++ b/cmd/tk/resume_kustomization.go
@@ -0,0 +1,59 @@
+package main
+
+import (
+	"context"
+	"fmt"
+	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
+	"github.com/spf13/cobra"
+	"k8s.io/apimachinery/pkg/types"
+)
+
+var resumeKsCmd = &cobra.Command{
+	Use:     "kustomization [name]",
+	Aliases: []string{"ks"},
+	Short:   "Resume kustomization",
+	Long:    "The resume command marks a previously suspended Kustomization resource for reconciliation and waits for it to finish the apply.",
+	RunE:    resumeKsCmdRun,
+}
+
+func init() {
+	resumeCmd.AddCommand(resumeKsCmd)
+}
+
+func resumeKsCmdRun(cmd *cobra.Command, args []string) error {
+	if len(args) < 1 {
+		return fmt.Errorf("kustomization name is required")
+	}
+	name := args[0]
+
+	ctx, cancel := context.WithTimeout(context.Background(), timeout)
+	defer cancel()
+
+	kubeClient, err := utils.kubeClient(kubeconfig)
+	if err != nil {
+		return err
+	}
+
+	namespacedName := types.NamespacedName{
+		Namespace: namespace,
+		Name:      name,
+	}
+	var kustomization kustomizev1.Kustomization
+	err = kubeClient.Get(ctx, namespacedName, &kustomization)
+	if err != nil {
+		return err
+	}
+
+	logAction("resuming kustomization %s in %s namespace", name, namespace)
+	kustomization.Spec.Suspend = false
+	if err := kubeClient.Update(ctx, &kustomization); err != nil {
+		return err
+	}
+	logSuccess("kustomization resumed")
+
+	if err := syncKsCmdRun(nil, []string{name}); err != nil {
+		return err
+	}
+
+	return nil
+}
diff --git a/cmd/tk/suspend.go b/cmd/tk/suspend.go
new file mode 100644
index 00000000..f3cebe58
--- /dev/null
+++ b/cmd/tk/suspend.go
@@ -0,0 +1,14 @@
+package main
+
+import (
+	"github.com/spf13/cobra"
+)
+
+var suspendCmd = &cobra.Command{
+	Use:   "suspend",
+	Short: "Suspend commands",
+}
+
+func init() {
+	rootCmd.AddCommand(suspendCmd)
+}
diff --git a/cmd/tk/suspend_kustomization.go b/cmd/tk/suspend_kustomization.go
new file mode 100644
index 00000000..07841f1a
--- /dev/null
+++ b/cmd/tk/suspend_kustomization.go
@@ -0,0 +1,55 @@
+package main
+
+import (
+	"context"
+	"fmt"
+	kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1alpha1"
+	"github.com/spf13/cobra"
+	"k8s.io/apimachinery/pkg/types"
+)
+
+var suspendKsCmd = &cobra.Command{
+	Use:     "kustomization [name]",
+	Aliases: []string{"ks"},
+	Short:   "Suspend kustomization",
+	Long:    "The suspend command disables the reconciliation of a Kustomization resource.",
+	RunE:    suspendKsCmdRun,
+}
+
+func init() {
+	suspendCmd.AddCommand(suspendKsCmd)
+}
+
+func suspendKsCmdRun(cmd *cobra.Command, args []string) error {
+	if len(args) < 1 {
+		return fmt.Errorf("kustomization name is required")
+	}
+	name := args[0]
+
+	ctx, cancel := context.WithTimeout(context.Background(), timeout)
+	defer cancel()
+
+	kubeClient, err := utils.kubeClient(kubeconfig)
+	if err != nil {
+		return err
+	}
+
+	namespacedName := types.NamespacedName{
+		Namespace: namespace,
+		Name:      name,
+	}
+	var kustomization kustomizev1.Kustomization
+	err = kubeClient.Get(ctx, namespacedName, &kustomization)
+	if err != nil {
+		return err
+	}
+
+	logAction("suspending kustomization %s in %s namespace", name, namespace)
+	kustomization.Spec.Suspend = true
+	if err := kubeClient.Update(ctx, &kustomization); err != nil {
+		return err
+	}
+	logSuccess("kustomization suspended")
+
+	return nil
+}
-- 
GitLab