From e5e06783bdccef36b4717b588bac9c339bfcbf3d Mon Sep 17 00:00:00 2001 From: stefanprodan 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 +}