diff --git a/pkg/kn/commands/human_readable_flags.go b/pkg/kn/commands/human_readable_flags.go index f7e16d897..762dfc0a4 100644 --- a/pkg/kn/commands/human_readable_flags.go +++ b/pkg/kn/commands/human_readable_flags.go @@ -36,8 +36,10 @@ func (f *HumanPrintFlags) AllowedFormats() []string { // handling human-readable output. func (f *HumanPrintFlags) ToPrinter() (hprinters.ResourcePrinter, error) { p := hprinters.NewTablePrinter(hprinters.PrintOptions{}) - // Add the column definitions and respective functions + // Add the column definitions and respective printing functions for service get command ServiceGetHandlers(p) + // Add the column definitions and respective printing functions for revision get command + RevisionGetHandlers(p) return p, nil } diff --git a/pkg/kn/commands/revision.go b/pkg/kn/commands/revision.go index 78e7da225..d4746165d 100644 --- a/pkg/kn/commands/revision.go +++ b/pkg/kn/commands/revision.go @@ -23,7 +23,7 @@ func NewRevisionCommand(p *KnParams) *cobra.Command { Use: "revision", Short: "Revision command group", } - revisionCmd.AddCommand(NewRevisionListCommand(p)) + revisionCmd.AddCommand(NewRevisionGetCommand(p)) revisionCmd.AddCommand(NewRevisionDescribeCommand(p)) return revisionCmd } diff --git a/pkg/kn/commands/revision_list.go b/pkg/kn/commands/revision_get.go similarity index 63% rename from pkg/kn/commands/revision_list.go rename to pkg/kn/commands/revision_get.go index a3f41af8b..33d9f8b96 100644 --- a/pkg/kn/commands/revision_list.go +++ b/pkg/kn/commands/revision_get.go @@ -1,4 +1,4 @@ -// Copyright © 2018 The Knative Authors +// Copyright © 2019 The Knative Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,21 +15,20 @@ package commands import ( + "fmt" + "github.com/spf13/cobra" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/cli-runtime/pkg/genericclioptions" ) -var revisionListPrintFlags *genericclioptions.PrintFlags +// NewRevisionGetCommand represents 'kn revision get' command +func NewRevisionGetCommand(p *KnParams) *cobra.Command { + revisionGetFlags := NewRevisionGetFlags() -// listCmd represents the list command -func NewRevisionListCommand(p *KnParams) *cobra.Command { - revisionListPrintFlags = genericclioptions.NewPrintFlags("").WithDefaultOutput( - "jsonpath={range .items[*]}{.metadata.name}{\"\\n\"}{end}") - revisionListCmd := &cobra.Command{ - Use: "list", - Short: "List available revisions.", + revisionGetCommand := &cobra.Command{ + Use: "get", + Short: "Get available revisions.", RunE: func(cmd *cobra.Command, args []string) error { client, err := p.ServingFactory() if err != nil { @@ -43,15 +42,18 @@ func NewRevisionListCommand(p *KnParams) *cobra.Command { if err != nil { return err } - - printer, err := revisionListPrintFlags.ToPrinter() - if err != nil { - return err + if len(revision.Items) == 0 { + fmt.Fprintf(cmd.OutOrStdout(), "No resources found.\n") + return nil } revision.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{ Group: "knative.dev", Version: "v1alpha1", - Kind: "Revision"}) + Kind: "revision"}) + printer, err := revisionGetFlags.ToPrinter() + if err != nil { + return err + } err = printer.PrintObj(revision, cmd.OutOrStdout()) if err != nil { return err @@ -59,7 +61,7 @@ func NewRevisionListCommand(p *KnParams) *cobra.Command { return nil }, } - AddNamespaceFlags(revisionListCmd.Flags(), true) - revisionListPrintFlags.AddFlags(revisionListCmd) - return revisionListCmd + AddNamespaceFlags(revisionGetCommand.Flags(), true) + revisionGetFlags.AddFlags(revisionGetCommand) + return revisionGetCommand } diff --git a/pkg/kn/commands/revision_get_flags.go b/pkg/kn/commands/revision_get_flags.go new file mode 100644 index 000000000..ae7ddfab0 --- /dev/null +++ b/pkg/kn/commands/revision_get_flags.go @@ -0,0 +1,122 @@ +// Copyright © 2019 The Knative Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or im +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + hprinters "github.com/knative/client/pkg/printers" + serving "github.com/knative/serving/pkg/apis/serving" + servingv1alpha1 "github.com/knative/serving/pkg/apis/serving/v1alpha1" + "github.com/spf13/cobra" + metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/cli-runtime/pkg/genericclioptions" +) + +// RevisionGetFlags composes common printer flag structs +// used in the Get command. +type RevisionGetFlags struct { + GenericPrintFlags *genericclioptions.PrintFlags + HumanReadableFlags *HumanPrintFlags +} + +// AllowedFormats is the list of formats in which data can be displayed +func (f *RevisionGetFlags) AllowedFormats() []string { + formats := f.GenericPrintFlags.AllowedFormats() + formats = append(formats, f.HumanReadableFlags.AllowedFormats()...) + return formats +} + +// ToPrinter attempts to find a composed set of RevisionGetFlags suitable for +// returning a printer based on current flag values. +func (f *RevisionGetFlags) ToPrinter() (hprinters.ResourcePrinter, error) { + // if there are flags specified for generic printing + if f.GenericPrintFlags.OutputFlagSpecified() { + p, err := f.GenericPrintFlags.ToPrinter() + if err != nil { + return nil, err + } + return p, nil + } + // if no flags specified, use the table printing + p, err := f.HumanReadableFlags.ToPrinter() + if err != nil { + return nil, err + } + return p, nil +} + +// AddFlags receives a *cobra.Command reference and binds +// flags related to humanreadable and template printing. +func (f *RevisionGetFlags) AddFlags(cmd *cobra.Command) { + f.GenericPrintFlags.AddFlags(cmd) + f.HumanReadableFlags.AddFlags(cmd) +} + +// NewGetPrintFlags returns flags associated with humanreadable, +// template, and "name" printing, with default values set. +func NewRevisionGetFlags() *RevisionGetFlags { + return &RevisionGetFlags{ + GenericPrintFlags: genericclioptions.NewPrintFlags(""), + HumanReadableFlags: NewHumanPrintFlags(), + } +} + +// RevisionGetHandlers adds print handlers for revision get command +func RevisionGetHandlers(h hprinters.PrintHandler) { + RevisionColumnDefinitions := []metav1beta1.TableColumnDefinition{ + {Name: "Service", Type: "string", Description: "Name of the knative service."}, + {Name: "Name", Type: "string", Description: "Name of the revision."}, + {Name: "Age", Type: "string", Description: "Age of the revision."}, + {Name: "Conditions", Type: "string", Description: "Conditions describing statuses of revision."}, + {Name: "Ready", Type: "string", Description: "Ready condition status of the revision."}, + {Name: "Reason", Type: "string", Description: "Reason for non-ready condition of the revision."}, + } + h.TableHandler(RevisionColumnDefinitions, printRevision) + h.TableHandler(RevisionColumnDefinitions, printRevisionList) +} + +// printRevisionList populates the knative revision list table rows +func printRevisionList(revisionList *servingv1alpha1.RevisionList, options hprinters.PrintOptions) ([]metav1beta1.TableRow, error) { + rows := make([]metav1beta1.TableRow, 0, len(revisionList.Items)) + for _, rev := range revisionList.Items { + r, err := printRevision(&rev, options) + if err != nil { + return nil, err + } + rows = append(rows, r...) + } + return rows, nil +} + +// printRevision populates the knative revision table rows +func printRevision(revision *servingv1alpha1.Revision, options hprinters.PrintOptions) ([]metav1beta1.TableRow, error) { + service := revision.Labels[serving.ConfigurationLabelKey] + name := revision.Name + age := translateTimestampSince(revision.CreationTimestamp) + conditions := conditionsValue(revision.Status.Conditions) + ready := readyCondition(revision.Status.Conditions) + reason := nonReadyConditionReason(revision.Status.Conditions) + row := metav1beta1.TableRow{ + Object: runtime.RawExtension{Object: revision}, + } + row.Cells = append(row.Cells, + service, + name, + age, + conditions, + ready, + reason) + return []metav1beta1.TableRow{row}, nil +} diff --git a/pkg/kn/commands/revision_get_test.go b/pkg/kn/commands/revision_get_test.go new file mode 100644 index 000000000..9ebab3835 --- /dev/null +++ b/pkg/kn/commands/revision_get_test.go @@ -0,0 +1,98 @@ +// Copyright © 2018 The Knative Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "bytes" + "strings" + "testing" + + serving "github.com/knative/serving/pkg/apis/serving" + v1alpha1 "github.com/knative/serving/pkg/apis/serving/v1alpha1" + servingclient "github.com/knative/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1" + "github.com/knative/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + client_testing "k8s.io/client-go/testing" +) + +func fakeRevisionGet(args []string, response *v1alpha1.RevisionList) (action client_testing.Action, output []string, err error) { + buf := new(bytes.Buffer) + fakeServing := &fake.FakeServingV1alpha1{&client_testing.Fake{}} + cmd := NewKnCommand(KnParams{ + Output: buf, + ServingFactory: func() (servingclient.ServingV1alpha1Interface, error) { return fakeServing, nil }, + }) + fakeServing.AddReactor("*", "*", + func(a client_testing.Action) (bool, runtime.Object, error) { + action = a + return true, response, nil + }) + cmd.SetArgs(args) + err = cmd.Execute() + if err != nil { + return + } + output = strings.Split(buf.String(), "\n") + return +} + +func TestRevisionGetEmpty(t *testing.T) { + action, output, err := fakeRevisionGet([]string{"revision", "get"}, &v1alpha1.RevisionList{}) + if err != nil { + t.Error(err) + return + } + if action == nil { + t.Errorf("No action") + } else if !action.Matches("list", "revisions") { + t.Errorf("Bad action %v", action) + } else if output[0] != "No resources found." { + t.Errorf("Bad output %s", output[0]) + } +} + +func TestRevisionGetDefaultOutput(t *testing.T) { + revision1 := createMockRevisionWithParams("foo-abcd", "foo") + revision2 := createMockRevisionWithParams("bar-wxyz", "bar") + RevisionList := &v1alpha1.RevisionList{Items: []v1alpha1.Revision{*revision1, *revision2}} + action, output, err := fakeRevisionGet([]string{"revision", "get"}, 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{"foo", "foo-abcd"}, "value") + testContains(t, output[2], []string{"bar", "bar-wxyz"}, "value") +} + +func createMockRevisionWithParams(name, svcName string) *v1alpha1.Revision { + revision := &v1alpha1.Revision{ + TypeMeta: metav1.TypeMeta{ + Kind: "Revision", + APIVersion: "knative.dev/v1alpha1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "default", + Labels: map[string]string{serving.ConfigurationLabelKey: svcName}, + }, + } + return revision +} diff --git a/pkg/kn/commands/service_get_test.go b/pkg/kn/commands/service_get_test.go index 2dd559247..ace3210a1 100644 --- a/pkg/kn/commands/service_get_test.go +++ b/pkg/kn/commands/service_get_test.go @@ -19,22 +19,21 @@ import ( "strings" "testing" - //servinglib "github.com/knative/client/pkg/serving" duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" - "github.com/knative/serving/pkg/apis/serving/v1alpha1" - serving "github.com/knative/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1" + v1alpha1 "github.com/knative/serving/pkg/apis/serving/v1alpha1" + servingclient "github.com/knative/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1" "github.com/knative/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" client_testing "k8s.io/client-go/testing" ) -func fakeGet(args []string, response *v1alpha1.ServiceList) (action client_testing.Action, output []string, err error) { +func fakeServiceGet(args []string, response *v1alpha1.ServiceList) (action client_testing.Action, output []string, err error) { buf := new(bytes.Buffer) fakeServing := &fake.FakeServingV1alpha1{&client_testing.Fake{}} cmd := NewKnCommand(KnParams{ Output: buf, - ServingFactory: func() (serving.ServingV1alpha1Interface, error) { return fakeServing, nil }, + ServingFactory: func() (servingclient.ServingV1alpha1Interface, error) { return fakeServing, nil }, }) fakeServing.AddReactor("*", "*", func(a client_testing.Action) (bool, runtime.Object, error) { @@ -51,7 +50,7 @@ func fakeGet(args []string, response *v1alpha1.ServiceList) (action client_testi } func TestGetEmpty(t *testing.T) { - action, output, err := fakeGet([]string{"service", "get"}, &v1alpha1.ServiceList{}) + action, output, err := fakeServiceGet([]string{"service", "get"}, &v1alpha1.ServiceList{}) if err != nil { t.Error(err) return @@ -65,11 +64,11 @@ func TestGetEmpty(t *testing.T) { } } -func TestListDefaultOutput(t *testing.T) { - service1 := createMockServiceWithParams(t, "foo", "foo.default.example.com", 1) - service2 := createMockServiceWithParams(t, "bar", "bar.default.example.com", 2) +func TestServiceGetDefaultOutput(t *testing.T) { + service1 := createMockServiceWithParams("foo", "foo.default.example.com", 1) + service2 := createMockServiceWithParams("bar", "bar.default.example.com", 2) serviceList := &v1alpha1.ServiceList{Items: []v1alpha1.Service{*service1, *service2}} - action, output, err := fakeGet([]string{"service", "get"}, serviceList) + action, output, err := fakeServiceGet([]string{"service", "get"}, serviceList) if err != nil { t.Fatal(err) } @@ -91,7 +90,7 @@ func testContains(t *testing.T, output string, sub []string, element string) { } } -func createMockServiceWithParams(t *testing.T, name, domain string, generation int64) *v1alpha1.Service { +func createMockServiceWithParams(name, domain string, generation int64) *v1alpha1.Service { service := &v1alpha1.Service{ TypeMeta: metav1.TypeMeta{ Kind: "Service", diff --git a/vendor/modules.txt b/vendor/modules.txt index 07b2cdaf1..561760900 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -70,13 +70,13 @@ github.com/knative/pkg/kmeta github.com/knative/pkg/kmp github.com/knative/pkg/configmap # github.com/knative/serving v0.5.2 +github.com/knative/serving/pkg/apis/serving github.com/knative/serving/pkg/apis/serving/v1alpha1 github.com/knative/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1 github.com/knative/serving/pkg/apis/autoscaling github.com/knative/serving/pkg/apis/config github.com/knative/serving/pkg/apis/networking github.com/knative/serving/pkg/apis/networking/v1alpha1 -github.com/knative/serving/pkg/apis/serving github.com/knative/serving/pkg/client/clientset/versioned/scheme github.com/knative/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1/fake github.com/knative/serving/pkg/apis/autoscaling/v1alpha1