From c5dfb08f81ed04beefece78061f5fce9c4ee2497 Mon Sep 17 00:00:00 2001 From: Gunjan Vyas Date: Mon, 10 Jan 2022 20:28:59 +0530 Subject: [PATCH] Added completion for revision resource (#1560) * Added completion for revision resource * Added unit tests for revision autocompletion * Fixed merge conflicts --- pkg/kn/commands/completion_helper.go | 31 ++++++- pkg/kn/commands/completion_helper_test.go | 103 ++++++++++++++++++++++ pkg/kn/commands/revision/delete.go | 1 + pkg/kn/commands/revision/describe.go | 5 +- 4 files changed, 136 insertions(+), 4 deletions(-) diff --git a/pkg/kn/commands/completion_helper.go b/pkg/kn/commands/completion_helper.go index 639dabb6b..bdc18ca02 100644 --- a/pkg/kn/commands/completion_helper.go +++ b/pkg/kn/commands/completion_helper.go @@ -29,8 +29,9 @@ type completionConfig struct { var ( resourceToFuncMap = map[string]func(config *completionConfig) []string{ - "broker": completeBroker, - "service": completeService, + "service": completeService, + "revision": completeRevision, + "broker": completeBroker, } ) @@ -150,3 +151,29 @@ func completeBroker(config *completionConfig) (suggestions []string) { } return } + +func completeRevision(config *completionConfig) (suggestions []string) { + suggestions = make([]string, 0) + if len(config.args) != 0 { + return + } + namespace, err := config.params.GetNamespace(config.command) + if err != nil { + return + } + client, err := config.params.NewServingClient(namespace) + if err != nil { + return + } + revisionList, err := client.ListRevisions(config.command.Context()) + if err != nil { + return + } + for _, sug := range revisionList.Items { + if !strings.HasPrefix(sug.Name, config.toComplete) { + continue + } + suggestions = append(suggestions, sug.Name) + } + return +} diff --git a/pkg/kn/commands/completion_helper_test.go b/pkg/kn/commands/completion_helper_test.go index 5bc133494..6efae820c 100644 --- a/pkg/kn/commands/completion_helper_test.go +++ b/pkg/kn/commands/completion_helper_test.go @@ -120,6 +120,31 @@ func initialiseKnParams() *KnParams { } } +var ( + testRev1 = servingv1.Revision{ + TypeMeta: metav1.TypeMeta{ + Kind: "Revision", + APIVersion: "serving.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-rev-1", Namespace: testNs}, + } + testRev2 = servingv1.Revision{ + TypeMeta: metav1.TypeMeta{ + Kind: "Revision", + APIVersion: "serving.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-rev-2", Namespace: testNs}, + } + testRev3 = servingv1.Revision{ + TypeMeta: metav1.TypeMeta{ + Kind: "Revision", + APIVersion: "serving.knative.dev/v1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test-rev-3", Namespace: testNs}, + } + testNsRevs = []servingv1.Revision{testRev1, testRev2, testRev3} +) + func TestResourceNameCompletionFuncService(t *testing.T) { completionFunc := ResourceNameCompletionFunc(knParams) @@ -283,6 +308,84 @@ func TestResourceNameCompletionFuncBroker(t *testing.T) { } } +func TestResourceNameCompletionFuncRevision(t *testing.T) { + completionFunc := ResourceNameCompletionFunc(knParams) + + fakeServing.AddReactor("list", "revisions", + func(a clienttesting.Action) (bool, runtime.Object, error) { + if a.GetNamespace() == errorNs { + return true, nil, errors.NewInternalError(fmt.Errorf("unable to list revisions")) + } + return true, &servingv1.RevisionList{Items: testNsRevs}, nil + }) + + tests := []testType{ + { + "Empty suggestions when non-zero args", + testNs, + knParams, + []string{"xyz"}, + "", + "revision", + }, + { + "Empty suggestions when no namespace flag", + "", + knParams, + nil, + "", + "revision", + }, + { + "Suggestions when test-ns namespace set", + testNs, + knParams, + nil, + "", + "revision", + }, + { + "Empty suggestions when toComplete is not a prefix", + testNs, + knParams, + nil, + "xyz", + "revision", + }, + { + "Empty suggestions when error during list operation", + errorNs, + knParams, + nil, + "", + "revision", + }, + } + for _, tt := range tests { + cmd := getResourceCommandWithTestSubcommand(tt.resource, tt.namespace != "", tt.resource != "no-parent") + t.Run(tt.name, func(t *testing.T) { + config := &completionConfig{ + params: tt.p, + command: cmd, + args: tt.args, + toComplete: tt.toComplete, + } + expectedFunc := resourceToFuncMap[tt.resource] + if expectedFunc == nil { + expectedFunc = func(config *completionConfig) []string { + return []string{} + } + } + cmd.Flags().Set("namespace", tt.namespace) + actualSuggestions, actualDirective := completionFunc(cmd, tt.args, tt.toComplete) + expectedSuggestions := expectedFunc(config) + expectedDirective := cobra.ShellCompDirectiveNoFileComp + assert.DeepEqual(t, actualSuggestions, expectedSuggestions) + assert.Equal(t, actualDirective, expectedDirective) + }) + } +} + func TestResourceNameCompletionFuncGitOps(t *testing.T) { tempDir := setupTempDir(t) assert.Assert(t, tempDir != "") diff --git a/pkg/kn/commands/revision/delete.go b/pkg/kn/commands/revision/delete.go index 61b6df1cf..db9b6c998 100644 --- a/pkg/kn/commands/revision/delete.go +++ b/pkg/kn/commands/revision/delete.go @@ -45,6 +45,7 @@ func NewRevisionDeleteCommand(p *commands.KnParams) *cobra.Command { # Delete all unreferenced revisions for a given service 'mysvc' kn revision delete --prune mysvc`, + ValidArgsFunction: commands.ResourceNameCompletionFunc(p), RunE: func(cmd *cobra.Command, args []string) error { prune := cmd.Flags().Changed("prune") argsLen := len(args) diff --git a/pkg/kn/commands/revision/describe.go b/pkg/kn/commands/revision/describe.go index 599dca125..8c8acab0d 100644 --- a/pkg/kn/commands/revision/describe.go +++ b/pkg/kn/commands/revision/describe.go @@ -42,8 +42,9 @@ func NewRevisionDescribeCommand(p *commands.KnParams) *cobra.Command { machineReadablePrintFlags := genericclioptions.NewPrintFlags("") command := &cobra.Command{ - Use: "describe NAME", - Short: "Show details of a revision", + Use: "describe NAME", + Short: "Show details of a revision", + ValidArgsFunction: commands.ResourceNameCompletionFunc(p), RunE: func(cmd *cobra.Command, args []string) error { if len(args) != 1 { return errors.New("'kn revision describe' requires name of the revision as single argument")