Repository: helm/helm
Pull request #8364 (2020-06-29 01:50:33)

Complete revision parameter for rollback command
**What this PR does / why we need it**:

This PR adds completion for the optional second parameter of the `rollback` command.
```
$ helm rollback nginx <TAB>
12  13  14  15  16  17  18  19  20  21
$ helm rollback nginx 2<TAB>
20  21
```

**Special notes for your reviewer**:
Although the revision parameter is optional for the `rollback` command, if the user presses `<TAB>` the completion will always suggest a revision.  I don't have a good way to indicate that the parameter is optional using completion, but I think it is still better to show the completion options in this case.

**If applicable**:
- [x] this PR contains unit tests
- [x] this PR has been tested for backwards compatibility with go tests and the acceptance-testing repo



Patch:
diff --git a/cmd/helm/get_all.go b/cmd/helm/get_all.go
index 1ebc7e387..a5037e4df 100644
--- a/cmd/helm/get_all.go
+++ b/cmd/helm/get_all.go
@@ -67,7 +67,7 @@ func newGetAllCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 	f.IntVar(&client.Version, "revision", 0, "get the named release with revision")
 	err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
 		if len(args) == 1 {
-			return compListRevisions(cfg, args[0])
+			return compListRevisions(toComplete, cfg, args[0])
 		}
 		return nil, cobra.ShellCompDirectiveNoFileComp
 	})
diff --git a/cmd/helm/get_hooks.go b/cmd/helm/get_hooks.go
index 6453c30eb..8b78653b5 100644
--- a/cmd/helm/get_hooks.go
+++ b/cmd/helm/get_hooks.go
@@ -62,7 +62,7 @@ func newGetHooksCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 	cmd.Flags().IntVar(&client.Version, "revision", 0, "get the named release with revision")
 	err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
 		if len(args) == 1 {
-			return compListRevisions(cfg, args[0])
+			return compListRevisions(toComplete, cfg, args[0])
 		}
 		return nil, cobra.ShellCompDirectiveNoFileComp
 	})
diff --git a/cmd/helm/get_manifest.go b/cmd/helm/get_manifest.go
index 048bf36b0..8ffeb3676 100644
--- a/cmd/helm/get_manifest.go
+++ b/cmd/helm/get_manifest.go
@@ -62,7 +62,7 @@ func newGetManifestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
 	cmd.Flags().IntVar(&client.Version, "revision", 0, "get the named release with revision")
 	err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
 		if len(args) == 1 {
-			return compListRevisions(cfg, args[0])
+			return compListRevisions(toComplete, cfg, args[0])
 		}
 		return nil, cobra.ShellCompDirectiveNoFileComp
 	})
diff --git a/cmd/helm/get_notes.go b/cmd/helm/get_notes.go
index 88d494fc4..a9d29ce49 100644
--- a/cmd/helm/get_notes.go
+++ b/cmd/helm/get_notes.go
@@ -61,7 +61,7 @@ func newGetNotesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 	f.IntVar(&client.Version, "revision", 0, "get the named release with revision")
 	err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
 		if len(args) == 1 {
-			return compListRevisions(cfg, args[0])
+			return compListRevisions(toComplete, cfg, args[0])
 		}
 		return nil, cobra.ShellCompDirectiveNoFileComp
 	})
diff --git a/cmd/helm/get_values.go b/cmd/helm/get_values.go
index fa7b10fed..c8c87c033 100644
--- a/cmd/helm/get_values.go
+++ b/cmd/helm/get_values.go
@@ -65,7 +65,7 @@ func newGetValuesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 	f.IntVar(&client.Version, "revision", 0, "get the named release with revision")
 	err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
 		if len(args) == 1 {
-			return compListRevisions(cfg, args[0])
+			return compListRevisions(toComplete, cfg, args[0])
 		}
 		return nil, cobra.ShellCompDirectiveNoFileComp
 	})
diff --git a/cmd/helm/history.go b/cmd/helm/history.go
index b49c52990..f55eea9fd 100644
--- a/cmd/helm/history.go
+++ b/cmd/helm/history.go
@@ -20,6 +20,7 @@ import (
 	"fmt"
 	"io"
 	"strconv"
+	"strings"
 	"time"
 
 	"github.com/gosuri/uitable"
@@ -184,15 +185,18 @@ func min(x, y int) int {
 	return y
 }
 
-func compListRevisions(cfg *action.Configuration, releaseName string) ([]string, cobra.ShellCompDirective) {
+func compListRevisions(toComplete string, cfg *action.Configuration, releaseName string) ([]string, cobra.ShellCompDirective) {
 	client := action.NewHistory(cfg)
 
 	var revisions []string
 	if hist, err := client.Run(releaseName); err == nil {
 		for _, release := range hist {
-			revisions = append(revisions, strconv.Itoa(release.Version))
+			version := strconv.Itoa(release.Version)
+			if strings.HasPrefix(version, toComplete) {
+				revisions = append(revisions, version)
+			}
 		}
-		return revisions, cobra.ShellCompDirectiveDefault
+		return revisions, cobra.ShellCompDirectiveNoFileComp
 	}
 	return nil, cobra.ShellCompDirectiveError
 }
diff --git a/cmd/helm/rollback.go b/cmd/helm/rollback.go
index 3b336d0ff..4849913a1 100644
--- a/cmd/helm/rollback.go
+++ b/cmd/helm/rollback.go
@@ -47,10 +47,15 @@ func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 		Long:  rollbackDesc,
 		Args:  require.MinimumNArgs(1),
 		ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
-			if len(args) != 0 {
-				return nil, cobra.ShellCompDirectiveNoFileComp
+			if len(args) == 0 {
+				return compListReleases(toComplete, cfg)
 			}
-			return compListReleases(toComplete, cfg)
+
+			if len(args) == 1 {
+				return compListRevisions(toComplete, cfg, args[0])
+			}
+
+			return nil, cobra.ShellCompDirectiveNoFileComp
 		},
 		RunE: func(cmd *cobra.Command, args []string) error {
 			if len(args) > 1 {
diff --git a/cmd/helm/status.go b/cmd/helm/status.go
index 5913d08df..abd32a9c2 100644
--- a/cmd/helm/status.go
+++ b/cmd/helm/status.go
@@ -78,7 +78,7 @@ func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
 
 	err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
 		if len(args) == 1 {
-			return compListRevisions(cfg, args[0])
+			return compListRevisions(toComplete, cfg, args[0])
 		}
 		return nil, cobra.ShellCompDirectiveNoFileComp
 	})


Test patch:
diff --git a/cmd/helm/rollback_test.go b/cmd/helm/rollback_test.go
index fdc627b5f..c11a7fca6 100644
--- a/cmd/helm/rollback_test.go
+++ b/cmd/helm/rollback_test.go
@@ -68,3 +68,39 @@ func TestRollbackCmd(t *testing.T) {
 	}}
 	runTestCmd(t, tests)
 }
+
+func TestRollbackRevisionCompletion(t *testing.T) {
+	mk := func(name string, vers int, status release.Status) *release.Release {
+		return release.Mock(&release.MockReleaseOptions{
+			Name:    name,
+			Version: vers,
+			Status:  status,
+		})
+	}
+
+	releases := []*release.Release{
+		mk("musketeers", 11, release.StatusDeployed),
+		mk("musketeers", 10, release.StatusSuperseded),
+		mk("musketeers", 9, release.StatusSuperseded),
+		mk("musketeers", 8, release.StatusSuperseded),
+		mk("carabins", 1, release.StatusSuperseded),
+	}
+
+	tests := []cmdTestCase{{
+		name:   "completion for release parameter",
+		cmd:    "__complete rollback ''",
+		rels:   releases,
+		golden: "output/rollback-comp.txt",
+	}, {
+		name:   "completion for revision parameter",
+		cmd:    "__complete rollback musketeers ''",
+		rels:   releases,
+		golden: "output/revision-comp.txt",
+	}, {
+		name:   "completion for with too many args",
+		cmd:    "__complete rollback musketeers 11 ''",
+		rels:   releases,
+		golden: "output/rollback-wrong-args-comp.txt",
+	}}
+	runTestCmd(t, tests)
+}
diff --git a/cmd/helm/testdata/output/revision-comp.txt b/cmd/helm/testdata/output/revision-comp.txt
index d06947d9d..50f7a9092 100644
--- a/cmd/helm/testdata/output/revision-comp.txt
+++ b/cmd/helm/testdata/output/revision-comp.txt
@@ -2,5 +2,5 @@
 9
 10
 11
-:0
-Completion ended with directive: ShellCompDirectiveDefault
+:4
+Completion ended with directive: ShellCompDirectiveNoFileComp
diff --git a/cmd/helm/testdata/output/rollback-comp.txt b/cmd/helm/testdata/output/rollback-comp.txt
new file mode 100644
index 000000000..f7741af12
--- /dev/null
+++ b/cmd/helm/testdata/output/rollback-comp.txt
@@ -0,0 +1,4 @@
+carabins
+musketeers
+:4
+Completion ended with directive: ShellCompDirectiveNoFileComp
diff --git a/cmd/helm/testdata/output/rollback-wrong-args-comp.txt b/cmd/helm/testdata/output/rollback-wrong-args-comp.txt
new file mode 100644
index 000000000..8d9fad576
--- /dev/null
+++ b/cmd/helm/testdata/output/rollback-wrong-args-comp.txt
@@ -0,0 +1,2 @@
+:4
+Completion ended with directive: ShellCompDirectiveNoFileComp