Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions pkg/kn/commands/flags/listprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@
package flags

import (
"io"

"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/cli-runtime/pkg/genericclioptions"

"knative.dev/client/pkg/kn/commands"
hprinters "knative.dev/client/pkg/printers"
"knative.dev/client/pkg/util"
)

// ListFlags composes common printer flag structs
Expand Down Expand Up @@ -56,6 +60,24 @@ func (f *ListPrintFlags) ToPrinter() (hprinters.ResourcePrinter, error) {
return p, nil
}

// Print is to print an Object to a Writer
func (f *ListPrintFlags) Print(obj runtime.Object, w io.Writer) error {
printer, err := f.ToPrinter()
if err != nil {
return err
}

if f.GenericPrintFlags.OutputFlagSpecified() {
unstructuredList, err := util.ToUnstructuredList(obj)
if err != nil {
return err
}
return printer.PrintObj(unstructuredList, w)
}

return printer.PrintObj(obj, w)
}

// AddFlags receives a *cobra.Command reference and binds
// flags related to humanreadable and template printing.
func (f *ListPrintFlags) AddFlags(cmd *cobra.Command) {
Expand Down
15 changes: 15 additions & 0 deletions pkg/kn/commands/flags/listprint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,18 @@ func TestListPrintFlags(t *testing.T) {
_, ok := p.(hprinters.ResourcePrinter)
assert.Check(t, ok == true)
}

func TestListPrintFlagsPrint(t *testing.T) {
var cmd *cobra.Command
flags := NewListPrintFlags(func(h hprinters.PrintHandler) {})

cmd = &cobra.Command{}
flags.AddFlags(cmd)

pr, err := flags.ToPrinter()
assert.NilError(t, err)
assert.Assert(t, pr != nil)

err = flags.Print(nil, cmd.OutOrStdout())
assert.NilError(t, err)
}
11 changes: 1 addition & 10 deletions pkg/kn/commands/service/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,6 @@ func NewServiceListCommand(p *commands.KnParams) *cobra.Command {
serviceListFlags.EnsureWithNamespace()
}

printer, err := serviceListFlags.ToPrinter()
if err != nil {
return err
}

// Sort serviceList by namespace and name (in this order)
sort.SliceStable(serviceList.Items, func(i, j int) bool {
a := serviceList.Items[i]
Expand All @@ -81,11 +76,7 @@ func NewServiceListCommand(p *commands.KnParams) *cobra.Command {
return a.ObjectMeta.Name < b.ObjectMeta.Name
})

err = printer.PrintObj(serviceList, cmd.OutOrStdout())
if err != nil {
return err
}
return nil
return serviceListFlags.Print(serviceList, cmd.OutOrStdout())
},
}
commands.AddNamespaceFlags(serviceListCommand.Flags(), true)
Expand Down
4 changes: 4 additions & 0 deletions pkg/printers/tableprinter.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ func NewTablePrinter(options PrintOptions) *HumanReadablePrinter {

// PrintObj prints the obj in a human-friendly format according to the type of the obj.
func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) error {
if obj == nil {
return nil
}

w, found := output.(*tabwriter.Writer)
if !found {
w = NewTabWriter(output)
Expand Down
64 changes: 64 additions & 0 deletions pkg/util/unstructured.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright © 2020 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 util

import (
"encoding/json"

"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
)

// ToUnstructuredList is to converts an object to unstructured.UnstructuredList.
// If the object is not a list type, it will convert to a single item UnstructuredList.
func ToUnstructuredList(obj runtime.Object) (*unstructured.UnstructuredList, error) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is the unit test for this ToUnstructuredList exported function?

Copy link
Copy Markdown
Author

@daisy-ycguo daisy-ycguo Apr 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see TestToUnstructuredList in pkg/util/unstructured_test.go

unstructuredList := &unstructured.UnstructuredList{}
if meta.IsListType(obj) {
unstructuredList.SetGroupVersionKind(obj.GetObjectKind().GroupVersionKind())
items, err := meta.ExtractList(obj)
if err != nil {
return nil, err
}
for _, obji := range items {
ud, err := toUnstructured(obji)
if err != nil {
return nil, err
}
unstructuredList.Items = append(unstructuredList.Items, *ud)
}

} else {
ud, err := toUnstructured(obj)
if err != nil {
return nil, err
}
unstructuredList.Items = append(unstructuredList.Items, *ud)
}
return unstructuredList, nil

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

}

func toUnstructured(obj runtime.Object) (*unstructured.Unstructured, error) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Author

@daisy-ycguo daisy-ycguo Apr 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the information.
I checked pkg/apis/duck, and noticed it only accepts an interface duck.OneOfOurs. Unfortunately, neither servingv1.ServiceList nor servingv1.Service , runtime.Object cannot satisfy that interface. So I cannot leverage it.

b, err := json.Marshal(obj)
if err != nil {
return nil, err
}
ud := &unstructured.Unstructured{}
if err := json.Unmarshal(b, ud); err != nil {
return nil, err
}
return ud, nil
}
86 changes: 86 additions & 0 deletions pkg/util/unstructured_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Copyright © 2019 The Knative Authors
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Copyright © 2019 The Knative Authors
// Copyright © 2020 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 util

import (
"testing"

"gotest.tools/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
servingv1 "knative.dev/serving/pkg/apis/serving/v1"
)

func TestToUnstructuredList(t *testing.T) {
serviceList := servingv1.ServiceList{
TypeMeta: metav1.TypeMeta{
APIVersion: "v1",
Kind: "List",
},
Items: []servingv1.Service{createService("s1"), createService("s2")},
}
expectedList := &unstructured.UnstructuredList{
Object: map[string]interface{}{
"apiVersion": string("v1"),
"kind": string("List"),
},
}
expectedList.Items = []unstructured.Unstructured{createUnstructured("s1"), createUnstructured("s2")}
unstructedList, err := ToUnstructuredList(&serviceList)
assert.NilError(t, err)
assert.DeepEqual(t, unstructedList, expectedList)

service1 := createService("s3")
expectedList = &unstructured.UnstructuredList{}
expectedList.Items = []unstructured.Unstructured{createUnstructured("s3")}
unstructedList, err = ToUnstructuredList(&service1)
assert.NilError(t, err)
assert.DeepEqual(t, unstructedList, expectedList)
}

func createService(name string) servingv1.Service {
service := servingv1.Service{
TypeMeta: metav1.TypeMeta{
Kind: "Service",
APIVersion: "serving.knative.dev/v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: "default",
},
}
return service
}

func createUnstructured(name string) unstructured.Unstructured {
return unstructured.Unstructured{
Object: map[string]interface{}{
"apiVersion": "serving.knative.dev/v1",
"kind": "Service",
"metadata": map[string]interface{}{
"namespace": "default",
"name": name,
"creationTimestamp": nil,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it require to set creationTimeStamp field ?

},
"spec": map[string]interface{}{
"template": map[string]interface{}{
"metadata": map[string]interface{}{"creationTimestamp": nil},
"spec": map[string]interface{}{"containers": nil},
},
},
"status": map[string]interface{}{},
},
}
}
9 changes: 9 additions & 0 deletions test/e2e/basic_workflow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ func TestBasicWorkflow(t *testing.T) {
serviceList(r, "hello")
serviceDescribe(r, "hello")

t.Log("return list --output name about hello service")
serviceListOutput(r, "hello")

t.Log("update hello service's configuration and return no error")
serviceUpdate(r, "hello", "--env", "TARGET=kn", "--port", "8888")

Expand Down Expand Up @@ -113,6 +116,12 @@ func serviceDescribe(r *test.KnRunResultCollector, serviceName string) {
assert.Assert(r.T(), util.ContainsAll(out.Stdout, "Name", "Namespace", "URL", "Age", "Revisions"))
}

func serviceListOutput(r *test.KnRunResultCollector, serviceName string) {
out := r.KnTest().Kn().Run("service", "list", serviceName, "--output", "name")
r.AssertNoError(out)
assert.Check(r.T(), util.ContainsAll(out.Stdout, serviceName, "service.serving.knative.dev"))
}

func serviceUpdate(r *test.KnRunResultCollector, serviceName string, args ...string) {
fullArgs := append([]string{}, "service", "update", serviceName)
fullArgs = append(fullArgs, args...)
Expand Down