From 375ecbd61f37c7acc74fe87c6de9d08bb65d2b6b Mon Sep 17 00:00:00 2001 From: Navid Shaikh Date: Fri, 21 Jun 2019 01:59:00 +0530 Subject: [PATCH] List revisions for a given service (#194) * Lists revisions for a given service Fixes #127 * Adds unit tests for listing revisions of a service * Adds integration tests for listing revisions of a service * Updates docs for listing revisions of a service * Updates vendor/modules.txt --- docs/cmd/kn_revision_list.md | 14 +++++- pkg/kn/commands/revision/revision_list.go | 22 ++++++++- .../commands/revision/revision_list_flags.go | 16 ++++++- .../commands/revision/revision_list_test.go | 46 ++++++++++++++++++- test/e2e/basic_workflow_test.go | 29 ++++++++++++ vendor/modules.txt | 2 +- 6 files changed, 124 insertions(+), 5 deletions(-) diff --git a/docs/cmd/kn_revision_list.md b/docs/cmd/kn_revision_list.md index 9dcd9beb0b..59b0d8afb8 100644 --- a/docs/cmd/kn_revision_list.md +++ b/docs/cmd/kn_revision_list.md @@ -4,12 +4,23 @@ List available revisions. ### Synopsis -List available revisions. +List revisions for a given service. ``` kn revision list [flags] ``` +### Examples + +``` + + # List all revisions + kn revision list + + # List revisions for a service 'svc1' in namespace 'myapp' + kn revision list -s svc1 -n myapp +``` + ### Options ``` @@ -18,6 +29,7 @@ kn revision list [flags] -h, --help help for list -n, --namespace string List the requested object(s) in given namespace. -o, --output string Output format. One of: json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-file. + -s, --service string Service name --template string Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview]. ``` diff --git a/pkg/kn/commands/revision/revision_list.go b/pkg/kn/commands/revision/revision_list.go index b9036a5624..aee81e358f 100644 --- a/pkg/kn/commands/revision/revision_list.go +++ b/pkg/kn/commands/revision/revision_list.go @@ -18,8 +18,10 @@ import ( "fmt" "github.com/knative/client/pkg/kn/commands" + "github.com/knative/serving/pkg/apis/serving" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime/schema" ) @@ -30,6 +32,13 @@ func NewRevisionListCommand(p *commands.KnParams) *cobra.Command { revisionListCommand := &cobra.Command{ Use: "list", Short: "List available revisions.", + Long: "List revisions for a given service.", + Example: ` + # List all revisions + kn revision list + + # List revisions for a service 'svc1' in namespace 'myapp' + kn revision list -s svc1 -n myapp`, RunE: func(cmd *cobra.Command, args []string) error { client, err := p.ServingFactory() if err != nil { @@ -39,7 +48,18 @@ func NewRevisionListCommand(p *commands.KnParams) *cobra.Command { if err != nil { return err } - revision, err := client.Revisions(namespace).List(v1.ListOptions{}) + listOptions := v1.ListOptions{} + if cmd.Flags().Changed("service") { + service := cmd.Flag("service").Value.String() + // Ensure requested service exist + _, err := client.Services(namespace).Get(service, v1.GetOptions{}) + if err != nil { + return err + } + listOptions.LabelSelector = labels.Set( + map[string]string{serving.ConfigurationLabelKey: service}).String() + } + revision, err := client.Revisions(namespace).List(listOptions) if err != nil { return err } diff --git a/pkg/kn/commands/revision/revision_list_flags.go b/pkg/kn/commands/revision/revision_list_flags.go index c848e8300e..aee0c66a99 100644 --- a/pkg/kn/commands/revision/revision_list_flags.go +++ b/pkg/kn/commands/revision/revision_list_flags.go @@ -26,6 +26,7 @@ import ( type RevisionListFlags struct { GenericPrintFlags *genericclioptions.PrintFlags HumanReadableFlags *commands.HumanPrintFlags + ServiceRefFlags ServiceReferenceFlags } // AllowedFormats is the list of formats in which data can be displayed @@ -55,10 +56,12 @@ func (f *RevisionListFlags) ToPrinter() (hprinters.ResourcePrinter, error) { } // AddFlags receives a *cobra.Command reference and binds -// flags related to humanreadable and template printing. +// flags related to humanreadable and template printing +// as well as to reference a service func (f *RevisionListFlags) AddFlags(cmd *cobra.Command) { f.GenericPrintFlags.AddFlags(cmd) f.HumanReadableFlags.AddFlags(cmd) + f.ServiceRefFlags.SetOptional(cmd) } // NewRevisionListFlags returns flags associated with humanreadable, @@ -69,3 +72,14 @@ func NewRevisionListFlags() *RevisionListFlags { HumanReadableFlags: commands.NewHumanPrintFlags(), } } + +// ServiceReferenceFlags compose a set of flag(s) to reference a service +type ServiceReferenceFlags struct { + Name string +} + +// SetOptional receives a *cobra.Command reference and +// adds the ServiceReferenceFlags flags as optional flags +func (s *ServiceReferenceFlags) SetOptional(cmd *cobra.Command) { + cmd.Flags().StringVarP(&s.Name, "service", "s", "", "Service name") +} diff --git a/pkg/kn/commands/revision/revision_list_test.go b/pkg/kn/commands/revision/revision_list_test.go index 005e09ced5..dce0c6ad11 100644 --- a/pkg/kn/commands/revision/revision_list_test.go +++ b/pkg/kn/commands/revision/revision_list_test.go @@ -29,7 +29,7 @@ import ( func fakeRevisionList(args []string, response *v1alpha1.RevisionList) (action client_testing.Action, output []string, err error) { knParams := &commands.KnParams{} cmd, fakeServing, buf := commands.CreateTestKnCommand(NewRevisionCommand(knParams), knParams) - fakeServing.AddReactor("*", "*", + fakeServing.AddReactor("list", "*", func(a client_testing.Action) (bool, runtime.Object, error) { action = a return true, response, nil @@ -76,6 +76,50 @@ func TestRevisionListDefaultOutput(t *testing.T) { testContains(t, output[2], []string{"bar", "bar-wxyz"}, "value") } +func TestRevisionListForService(t *testing.T) { + revision1 := createMockRevisionWithParams("foo-abcd", "svc1") + revision2 := createMockRevisionWithParams("bar-wxyz", "svc1") + revision3 := createMockRevisionWithParams("foo-abcd", "svc2") + revision4 := createMockRevisionWithParams("bar-wxyz", "svc2") + RevisionList := &v1alpha1.RevisionList{Items: []v1alpha1.Revision{*revision1, *revision2, *revision3, *revision4}} + action, output, err := fakeRevisionList([]string{"revision", "list", "-s", "svc1"}, RevisionList) + if err != nil { + t.Fatal(err) + } + if action == nil { + t.Errorf("No action") + } else if !action.Matches("list", "revisions") { + t.Errorf("Bad action %v", action) + } + testContains(t, output[0], []string{"SERVICE", "NAME", "AGE", "CONDITIONS", "READY", "REASON"}, "column header") + testContains(t, output[1], []string{"svc1", "foo-abcd"}, "value") + testContains(t, output[2], []string{"svc1", "bar-wxyz"}, "value") + action, output, err = fakeRevisionList([]string{"revision", "list", "-s", "svc2"}, RevisionList) + if err != nil { + t.Fatal(err) + } + if action == nil { + t.Errorf("No action") + } else if !action.Matches("list", "revisions") { + t.Errorf("Bad action %v", action) + } + testContains(t, output[0], []string{"SERVICE", "NAME", "AGE", "CONDITIONS", "READY", "REASON"}, "column header") + testContains(t, output[1], []string{"svc2", "foo-abcd"}, "value") + testContains(t, output[2], []string{"svc2", "bar-wxyz"}, "value") + //test for non existent service + action, output, err = fakeRevisionList([]string{"revision", "list", "-s", "svc3"}, RevisionList) + if err != nil { + t.Fatal(err) + } + if action == nil { + t.Errorf("No action") + } else if !action.Matches("list", "revisions") { + t.Errorf("Bad action %v", action) + } else if !strings.Contains(output[0], "No resources found.") { + t.Errorf("Bad output %s", output[0]) + } +} + func testContains(t *testing.T, output string, sub []string, element string) { for _, each := range sub { if !strings.Contains(output, each) { diff --git a/test/e2e/basic_workflow_test.go b/test/e2e/basic_workflow_test.go index 8837d2d6ed..78e0e4a79e 100644 --- a/test/e2e/basic_workflow_test.go +++ b/test/e2e/basic_workflow_test.go @@ -50,7 +50,12 @@ func TestBasicWorkflow(t *testing.T) { testServiceCreate(t, k, "hello") testServiceList(t, k, "hello") testServiceDescribe(t, k, "hello") + testServiceUpdate(t, k, "hello", []string{"--env", "TARGET=kn"}) + testServiceCreate(t, k, "svc2") + testRevisionListForService(t, k, "hello") + testRevisionListForService(t, k, "svc2") testServiceDelete(t, k, "hello") + testServiceDelete(t, k, "svc2") testServiceListEmpty(t, k) } @@ -92,6 +97,19 @@ func testServiceList(t *testing.T, k kn, serviceName string) { } } +func testRevisionListForService(t *testing.T, k kn, serviceName string) { + out, err := k.RunWithOpts([]string{"revision", "list", "-s", serviceName}, runOpts{NoNamespace: false}) + if err != nil { + t.Fatalf(fmt.Sprintf("Error executing 'kn revision list -s %s' command. Error: %s", serviceName, err.Error())) + } + outputLines := strings.Split(out, "\n") + for _, line := range outputLines[1:] { + if len(line) > 1 && !strings.HasPrefix(line, serviceName) { + t.Fatalf(fmt.Sprintf("Expected output incorrect, expecting line to start with service name: %s\nFound: %s", serviceName, line)) + } + } +} + func testServiceDescribe(t *testing.T, k kn, serviceName string) { out, err := k.RunWithOpts([]string{"service", "describe", serviceName}, runOpts{NoNamespace: false}) if err != nil { @@ -114,6 +132,17 @@ metadata:` } } +func testServiceUpdate(t *testing.T, k kn, serviceName string, args []string) { + out, err := k.RunWithOpts(append([]string{"service", "update", serviceName}, args...), runOpts{NoNamespace: false}) + if err != nil { + t.Fatalf(fmt.Sprintf("Error executing 'kn service update' command. Error: %s", err.Error())) + } + expectedOutput := fmt.Sprintf("Service '%s' updated", serviceName) + if !strings.Contains(out, expectedOutput) { + t.Fatalf(fmt.Sprintf("Expected output incorrect, expecting to include:\n%s\nFound:\n%s\n", expectedOutput, out)) + } +} + func testServiceDelete(t *testing.T, k kn, serviceName string) { out, err := k.RunWithOpts([]string{"service", "delete", serviceName}, runOpts{NoNamespace: false}) if err != nil { diff --git a/vendor/modules.txt b/vendor/modules.txt index 8ffceafca7..0b6613f8fa 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -200,6 +200,7 @@ k8s.io/api/storage/v1beta1 k8s.io/apimachinery/pkg/apis/meta/v1 k8s.io/apimachinery/pkg/util/duration k8s.io/apimachinery/pkg/apis/meta/v1beta1 +k8s.io/apimachinery/pkg/labels k8s.io/apimachinery/pkg/runtime k8s.io/apimachinery/pkg/runtime/schema k8s.io/apimachinery/pkg/api/resource @@ -210,7 +211,6 @@ k8s.io/apimachinery/pkg/api/validation k8s.io/apimachinery/pkg/runtime/serializer k8s.io/apimachinery/pkg/types k8s.io/apimachinery/pkg/watch -k8s.io/apimachinery/pkg/labels k8s.io/apimachinery/pkg/conversion k8s.io/apimachinery/pkg/fields k8s.io/apimachinery/pkg/selection