diff --git a/cmd/apply.go b/cmd/apply.go index 46dbc51..3dcdb3f 100644 --- a/cmd/apply.go +++ b/cmd/apply.go @@ -54,9 +54,9 @@ func getFinalOutputDir(outputDir string, isolated bool) string { return outputDir } -func applyAppGroup(group string, namespace string, outputDir string, appFilter string, lint bool, dryRun bool) error { +func applyAppGroup(group string, namespace string, outputDir string, appFilter string, lint bool, dryRun bool, askUser bool) error { - log.V(0).Info("applying", "group", group, "namespace", namespace, "app", appFilter, "lint", lint, "dry", dryRun) + log.V(0).Info("applying", "group", group, "namespace", namespace, "app", appFilter, "lint", lint, "dry", dryRun, "ask", askUser) namespaceDir := utils.GetNamespaceDir(namespace) if _, err := os.Stat(namespaceDir); os.IsNotExist(err) { return fmt.Errorf("%s directory does not exist", namespaceDir) @@ -132,7 +132,19 @@ var applyCmd = &cobra.Command{ isolated, _ := cmd.Flags().GetBool("isolate") - err := applyAppGroup(args[0], namespace, getFinalOutputDir(outputDir, isolated), appFilter, lint, dryRun) + askForConfirmation, _ := cmd.Flags().GetBool("ask-for-confirmation") + + if !askForConfirmation { + toContinue, errAsking := delYN(os.Stdin) + switch { + case errAsking != nil: + return errAsking + case !toContinue: + os.Exit(0) + } + } + + err := applyAppGroup(args[0], namespace, getFinalOutputDir(outputDir, isolated), appFilter, lint, dryRun, askForConfirmation) if err != nil { return err } @@ -145,7 +157,7 @@ func init() { rootCmd.AddCommand(applyCmd) applyCmd.Flags().BoolP("lint", "l", false, "Lint temlate") - applyCmd.Flags().BoolP("dry-run", "d", false, "Dry Run") + applyCmd.Flags().StringP("namespace", "n", "", "Target namespace") applyCmd.MarkFlagRequired("namespace") diff --git a/cmd/applyNamespace.go b/cmd/applyNamespace.go index a81c188..7aaef92 100644 --- a/cmd/applyNamespace.go +++ b/cmd/applyNamespace.go @@ -40,6 +40,8 @@ var namespaceCmd = &cobra.Command{ isolated, _ := cmd.Flags().GetBool("isolate") + askForConfirmation, _ := cmd.Flags().GetBool("ask-for-confirmation") + namespace := args[0] namespaceDir := utils.GetNamespaceDir(namespace) @@ -51,7 +53,7 @@ var namespaceCmd = &cobra.Command{ if info.IsDir() && path != namespaceDir { group := filepath.Base(path) - err := applyAppGroup(group, namespace, getFinalOutputDir(outputDir, isolated), appFilter, lint, dryRun) + err := applyAppGroup(group, namespace, getFinalOutputDir(outputDir, isolated), appFilter, lint, dryRun, askForConfirmation) if err != nil { return err } diff --git a/cmd/delete.go b/cmd/delete.go index 647b397..d0b93d6 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -16,7 +16,11 @@ limitations under the License. package cmd import ( + "bufio" "fmt" + "io" + "os" + "unicode" "github.com/spf13/cobra" ) @@ -37,12 +41,48 @@ func deleteAppGroup(group string, namespace string, appFilter string) error { return nil } +const warningText = `Warning! +This action could delete some resources like PVs, which can be in use from another party` + +func delYN(in io.Reader) (bool, error) { + r := bufio.NewReader(in) + fmt.Println(warningText) + for { + fmt.Print("Do you want to continue [y/n]") + response, err := r.ReadString('\n') + if err != nil { + return false, err + } + switch { + case len(response) == 2 && byte(unicode.ToLower(rune(response[0]))) == 'y': + return true, nil + + case len(response) == 2 && byte(unicode.ToLower(rune(response[0]))) == 'n': + return false, nil + } + } + +} + // deleteCmd represents the delete command var deleteCmd = &cobra.Command{ Use: "delete", Short: "Delete app group or app", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + + askForConfirmation, _ := cmd.Flags().GetBool("ask-for-confirmation") + + if !askForConfirmation { + toContinue, errAsking := delYN(os.Stdin) + switch { + case errAsking != nil: + return errAsking + case !toContinue: + os.Exit(0) + } + } + group := args[0] namespace, _ := cmd.Flags().GetString("namespace") diff --git a/cmd/deleteNamespace.go b/cmd/deleteNamespace.go index de1c884..172c540 100644 --- a/cmd/deleteNamespace.go +++ b/cmd/deleteNamespace.go @@ -30,6 +30,19 @@ var deleteNamespaceCmd = &cobra.Command{ Short: "Namespace apply", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { + + askForConfirmation, _ := cmd.Flags().GetBool("ask-for-confirmation") + + if !askForConfirmation { + toContinue, errAsking := delYN(os.Stdin) + switch { + case errAsking != nil: + return errAsking + case !toContinue: + os.Exit(0) + } + } + appFilter, _ := cmd.Flags().GetString("app") namespace := args[0] diff --git a/cmd/root.go b/cmd/root.go index 750b127..23ad10e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -52,9 +52,12 @@ var rootCmd = &cobra.Command{ skipDeps, _ := cmd.Flags().GetBool("skip-deps") diffRun, _ := cmd.Flags().GetBool("diff-run") + askForConfirmation, _ := cmd.Flags().GetBool("ask-for-confirmation") + kappEngine = exec.NewKappEngine(exec.Opts{ - DiffRun: diffRun, - ExtraArgs: execExtraArgs, + DiffRun: diffRun, + ExtraArgs: execExtraArgs, + AskForConfirmation: askForConfirmation, }, log) helmfileEngine = template.NewHelmFileEngine(template.Opts{ @@ -96,6 +99,8 @@ func init() { rootCmd.PersistentFlags().StringArray("kapp-args", []string{}, "Execution engine extra args") rootCmd.PersistentFlags().StringArray("helmfile-args", []string{}, "Template engine extra args") + rootCmd.PersistentFlags().BoolP("ask-for-confirmation", "c", true, "Ask the user to confirm the operation before kapp execution") + rootCmd.MarkFlagRequired("environment") } diff --git a/pkg/exec/interfaces.go b/pkg/exec/interfaces.go index a298c27..e414ae0 100644 --- a/pkg/exec/interfaces.go +++ b/pkg/exec/interfaces.go @@ -23,6 +23,7 @@ type ExecEngine interface { } type Opts struct { - DiffRun bool - ExtraArgs []string + DiffRun bool + ExtraArgs []string + AskForConfirmation bool } diff --git a/pkg/exec/kapp.go b/pkg/exec/kapp.go index 9ae48eb..9ff289b 100644 --- a/pkg/exec/kapp.go +++ b/pkg/exec/kapp.go @@ -28,76 +28,44 @@ type KappEngine struct { log logr.Logger } +var inputArgs []string + func NewKappEngine(opts Opts, log logr.Logger) *KappEngine { + if !opts.AskForConfirmation { + inputArgs = append(inputArgs, "-y") + } return &KappEngine{opts, log} } func (e *KappEngine) DeleteApp(app string, namespace string) error { - - inputArgs := append([]string{"-y", "delete", "-a", app, "-n", namespace, fmt.Sprintf("--diff-run=%t", e.Opts.DiffRun)}, e.Opts.ExtraArgs...) - - cmd := exec.Command("kapp", inputArgs...) - print := func(in string) { - e.log.Info(in) - } - - err := utils.ExecWithOutput(cmd, print, print) - - if err != nil { - return err - } - - return nil - + return e.exec(append(inputArgs, []string{"delete", "-a", app, "-n", namespace, fmt.Sprintf("--diff-run=%t", e.Opts.DiffRun)}...)) } func (e *KappEngine) DeleteGroup(group string, namespace string) error { - - inputArgs := append([]string{"-y", "app-group", "delete", "-n", namespace, "-g", group, fmt.Sprintf("--diff-run=%t", e.Opts.DiffRun)}, e.Opts.ExtraArgs...) - - cmd := exec.Command("kapp", inputArgs...) - print := func(in string) { - e.log.Info(in) - } - - err := utils.ExecWithOutput(cmd, print, print) - - if err != nil { - return err - } - - return nil + return e.exec(append(inputArgs, []string{"app-group", "delete", "-n", namespace, "-g", group, fmt.Sprintf("--diff-run=%t", e.Opts.DiffRun)}...)) } func (e *KappEngine) DeployApp(app string, appDir string, namespace string) error { - - inputArgs := append([]string{"-y", "deploy", "-a", app, "-f", appDir, "-n", namespace, fmt.Sprintf("--diff-run=%t", e.Opts.DiffRun)}, e.Opts.ExtraArgs...) - - cmd := exec.Command("kapp", inputArgs...) - print := func(in string) { - e.log.Info(in) - } - - err := utils.ExecWithOutput(cmd, print, print) - - if err != nil { - return err - } - - return nil - + return e.exec(append(inputArgs, []string{"deploy", "-a", app, "-f", appDir, "-n", namespace, fmt.Sprintf("--diff-run=%t", e.Opts.DiffRun)}...)) } func (e *KappEngine) DeployGroup(group string, appGroupDir string, namespace string) error { + return e.exec(append(inputArgs, []string{"app-group", "deploy", "-d", appGroupDir, "-n", namespace, "-g", group, fmt.Sprintf("--diff-run=%t", e.Opts.DiffRun)}...)) +} - inputArgs := append([]string{"-y", "app-group", "deploy", "-d", appGroupDir, "-n", namespace, "-g", group, fmt.Sprintf("--diff-run=%t", e.Opts.DiffRun)}, e.Opts.ExtraArgs...) +func (e *KappEngine) exec(inputArgs []string) error { - cmd := exec.Command("kapp", inputArgs...) + cmd := exec.Command("kapp", append(inputArgs, e.Opts.ExtraArgs...)...) print := func(in string) { e.log.Info(in) } - err := utils.ExecWithOutput(cmd, print, print) + var err error + if e.Opts.AskForConfirmation { + err = utils.ExecWithStdInOut(cmd) + } else { + err = utils.ExecWithOutput(cmd, print, print) + } if err != nil { return err diff --git a/pkg/utils/exec.go b/pkg/utils/exec.go index f473b5e..f97f648 100644 --- a/pkg/utils/exec.go +++ b/pkg/utils/exec.go @@ -80,3 +80,10 @@ func ExecWithOutput(cmd *exec.Cmd, outHook func(string), errorHook func(string)) return nil } + +func ExecWithStdInOut(cmd *exec.Cmd) error { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.Stdin = os.Stdin + return cmd.Run() +}