diff --git a/cmd/argocd/commands/app.go b/cmd/argocd/commands/app.go index 29b3ee2df27da..042459e6f05e3 100644 --- a/cmd/argocd/commands/app.go +++ b/cmd/argocd/commands/app.go @@ -35,6 +35,7 @@ import ( "sigs.k8s.io/yaml" "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/headless" + "github.com/argoproj/argo-cd/v2/cmd/argocd/commands/utils" cmdutil "github.com/argoproj/argo-cd/v2/cmd/util" "github.com/argoproj/argo-cd/v2/controller" argocdclient "github.com/argoproj/argo-cd/v2/pkg/apiclient" @@ -1435,8 +1436,6 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra. conn, appIf := acdClient.NewApplicationClientOrDie() defer argoio.Close(conn) var isTerminal bool = isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) - var isConfirmAll bool = false - numOfApps := len(args) promptFlag := c.Flag("yes") if promptFlag.Changed && promptFlag.Value.String() == "true" { noPrompt = true @@ -1449,6 +1448,16 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra. appNames = args } + numOfApps := len(appNames) + + // This is for backward compatibility, + // before we showed the prompts only when condition cascade && isTerminal && !noPrompt is true + promptUtil := utils.NewPrompt(cascade && isTerminal && !noPrompt) + var ( + confirmAll = false + confirm = false + ) + for _, appFullName := range appNames { appName, appNs := argo.ParseFromQualifiedName(appFullName, appNamespace) appDeleteReq := application.ApplicationDeleteRequest{ @@ -1461,38 +1470,21 @@ func NewApplicationDeleteCommand(clientOpts *argocdclient.ClientOptions) *cobra. if c.Flag("propagation-policy").Changed { appDeleteReq.PropagationPolicy = &propagationPolicy } - if cascade && isTerminal && !noPrompt { - var lowercaseAnswer string - if numOfApps == 1 { - lowercaseAnswer = cli.AskToProceedS("Are you sure you want to delete '" + appFullName + "' and all its resources? [y/n] ") - } else { - if !isConfirmAll { - lowercaseAnswer = cli.AskToProceedS("Are you sure you want to delete '" + appFullName + "' and all its resources? [y/n/A] where 'A' is to delete all specified apps and their resources without prompting ") - if lowercaseAnswer == "a" { - lowercaseAnswer = "y" - isConfirmAll = true - } - } else { - lowercaseAnswer = "y" - } - } - if lowercaseAnswer == "y" { - _, err := appIf.Delete(ctx, &appDeleteReq) - errors.CheckError(err) - if wait { - checkForDeleteEvent(ctx, acdClient, appFullName) - } - fmt.Printf("application '%s' deleted\n", appFullName) - } else { - fmt.Println("The command to delete '" + appFullName + "' was cancelled.") - } - } else { + messageForSingle := "Are you sure you want to delete '" + appFullName + "' and all its resources? [y/n] " + messageForAll := "Are you sure you want to delete '" + appFullName + "' and all its resources? [y/n/a] where 'a' is to delete all specified apps and their resources without prompting " + + if !confirmAll { + confirm, confirmAll = promptUtil.ConfirmBaseOnCount(messageForSingle, messageForAll, numOfApps) + } + if confirm || confirmAll { _, err := appIf.Delete(ctx, &appDeleteReq) errors.CheckError(err) - if wait { checkForDeleteEvent(ctx, acdClient, appFullName) } + fmt.Printf("application '%s' deleted\n", appFullName) + } else { + fmt.Println("The command to delete '" + appFullName + "' was cancelled.") } } }, diff --git a/cmd/argocd/commands/utils/prompt.go b/cmd/argocd/commands/utils/prompt.go index 5c47750e3ee6c..2aa016ef1e0ff 100644 --- a/cmd/argocd/commands/utils/prompt.go +++ b/cmd/argocd/commands/utils/prompt.go @@ -21,3 +21,40 @@ func (p *Prompt) Confirm(message string) bool { return cli.AskToProceed(message) } + +// ConfirmAll asks the user to confirm an action. If prompts are disabled, it will return true. +// support y/n and A, which means all +// return if confirm and if all +func (p *Prompt) ConfirmAll(message string) (bool, bool) { + if !p.enabled { + return true, true + } + + result := cli.AskToProceedS(message) + + if result == "a" { + return true, true + } + + if result == "y" { + return true, false + } + + return false, false +} + +func (p *Prompt) ConfirmBaseOnCount(messageForSingle string, messageForArray string, count int) (bool, bool) { + if !p.enabled { + return true, true + } + + if count == 0 { + return true, true + } + + if count == 1 { + return p.Confirm(messageForSingle), true + } + + return p.ConfirmAll(messageForArray) +} diff --git a/cmd/argocd/commands/utils/prompt_test.go b/cmd/argocd/commands/utils/prompt_test.go index 1da0c66cb901f..b758f2b2f1c9a 100644 --- a/cmd/argocd/commands/utils/prompt_test.go +++ b/cmd/argocd/commands/utils/prompt_test.go @@ -20,3 +20,30 @@ func TestConfirm_PromptsEnabled_False(t *testing.T) { prompt := NewPrompt(false) assert.True(t, prompt.Confirm("Are you sure you want to run this command? (y/n) ")) } + +// Returns true, true when prompt is disabled +func TestConfirmAllPromptDisabled(t *testing.T) { + p := &Prompt{enabled: false} + result1, result2 := p.ConfirmAll("Proceed?") + if result1 != true || result2 != true { + t.Errorf("Expected (true, true), got (%v, %v)", result1, result2) + } +} + +func TestConfirmBaseOnCountPromptDisabled(t *testing.T) { + p := &Prompt{enabled: false} + result1, result2 := p.ConfirmBaseOnCount("Proceed?", "Process all?", 2) + + if result1 != true || result2 != true { + t.Errorf("Expected (true, true), got (%v, %v)", result1, result2) + } +} + +func TestConfirmBaseOnCountZeroApps(t *testing.T) { + p := &Prompt{enabled: true} + result1, result2 := p.ConfirmBaseOnCount("Proceed?", "Process all?", 0) + + if result1 != true || result2 != true { + t.Errorf("Expected (true, true), got (%v, %v)", result1, result2) + } +}