Skip to content

Commit

Permalink
Added --no-headers flag for resource listing
Browse files Browse the repository at this point in the history
  • Loading branch information
Komal Dhull authored and Komal Dhull committed Jul 15, 2019
1 parent 9fb6a43 commit f64fa50
Show file tree
Hide file tree
Showing 16 changed files with 343 additions and 7 deletions.
1 change: 1 addition & 0 deletions docs/cmd/kn_revision_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ kn revision list [name] [flags]
--allow-missing-template-keys If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats. (default true)
-h, --help help for list
-n, --namespace string List the requested object(s) in given namespace.
--no-headers Remove column headers in default table output.
-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].
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_route.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ kn route [flags]
### SEE ALSO

* [kn](kn.md) - Knative client
* [kn route describe](kn_route_describe.md) - Describe available route.
* [kn route list](kn_route_list.md) - List available routes.

33 changes: 33 additions & 0 deletions docs/cmd/kn_route_describe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
## kn route describe

Describe available route.

### Synopsis

Describe available route.

```
kn route describe NAME [flags]
```

### Options

```
--allow-missing-template-keys If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats. (default true)
-h, --help help for describe
-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. (default "yaml")
--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].
```

### Options inherited from parent commands

```
--config string config file (default is $HOME/.kn/config.yaml)
--kubeconfig string kubectl config file (default is $HOME/.kube/config)
```

### SEE ALSO

* [kn route](kn_route.md) - Route command group

1 change: 1 addition & 0 deletions docs/cmd/kn_route_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ kn route list NAME [flags]
--allow-missing-template-keys If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats. (default true)
-h, --help help for list
-n, --namespace string List the requested object(s) in given namespace.
--no-headers Remove column headers in default table output.
-o, --output string Output format. One of: json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-file.
--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].
```
Expand Down
1 change: 1 addition & 0 deletions docs/cmd/kn_service_list.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ kn service list [name] [flags]
--allow-missing-template-keys If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats. (default true)
-h, --help help for list
-n, --namespace string List the requested object(s) in given namespace.
--no-headers Remove column headers in default table output.
-o, --output string Output format. One of: json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-file.
--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].
```
Expand Down
6 changes: 4 additions & 2 deletions pkg/kn/commands/human_readable_flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,26 +30,28 @@ import (
// Given the following flag values, a printer can be requested that knows
// how to handle printing based on these values.
type HumanPrintFlags struct {
NoHeaders bool
//TODO: Add more flags as required
}

// AllowedFormats returns more customized formating options
func (f *HumanPrintFlags) AllowedFormats() []string {
// TODO: Add more formats eg: wide
return []string{""}
return []string{"no-headers"}
}

// ToPrinter receives returns a printer capable of
// handling human-readable output.
func (f *HumanPrintFlags) ToPrinter(getHandlerFunc func(h hprinters.PrintHandler)) (hprinters.ResourcePrinter, error) {
p := hprinters.NewTablePrinter(hprinters.PrintOptions{})
p := hprinters.NewTablePrinter(hprinters.PrintOptions{NoHeaders: f.NoHeaders})
getHandlerFunc(p)
return p, nil
}

// AddFlags receives a *cobra.Command reference and binds
// flags related to human-readable printing to it
func (f *HumanPrintFlags) AddFlags(c *cobra.Command) {
c.Flags().BoolVar(&f.NoHeaders, "no-headers", false, "Remove column headers in default table output.")
//TODO: Add more flags as required
}

Expand Down
17 changes: 17 additions & 0 deletions pkg/kn/commands/revision/revision_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,23 @@ func TestRevisionListDefaultOutput(t *testing.T) {
assert.Check(t, util.ContainsAll(output[2], "bar-wxyz", "bar"))
}

func TestRevisionListDefaultOutputNoHeaders(t *testing.T) {
revision1 := createMockRevisionWithParams("foo-abcd", "foo")
revision2 := createMockRevisionWithParams("bar-wxyz", "bar")
RevisionList := &v1alpha1.RevisionList{Items: []v1alpha1.Revision{*revision1, *revision2}}
action, output, err := fakeRevisionList([]string{"revision", "list", "--no-headers"}, 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)
}
assert.Check(t, util.ContainsAll(output[0], "foo-abcd", "foo"))
assert.Check(t, util.ContainsAll(output[1], "bar-wxyz", "bar"))
}

func TestRevisionListForService(t *testing.T) {
revision1 := createMockRevisionWithParams("foo-abcd", "svc1")
revision2 := createMockRevisionWithParams("bar-wxyz", "svc1")
Expand Down
17 changes: 17 additions & 0 deletions pkg/kn/commands/route/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,23 @@ func TestRouteListDefaultOutput(t *testing.T) {
assert.Check(t, util.ContainsAll(output[2], "bar", "100% -> bar-98765"))
}

func TestRouteListDefaultOutputNoHeaders(t *testing.T) {
route1 := createMockRouteSingleTarget("foo", "foo-01234", 100)
route2 := createMockRouteSingleTarget("bar", "bar-98765", 100)
routeList := &v1alpha1.RouteList{Items: []v1alpha1.Route{*route1, *route2}}
action, output, err := fakeRouteList([]string{"route", "list", "--no-headers"}, routeList)
if err != nil {
t.Fatal(err)
}
if action == nil {
t.Errorf("No action")
} else if !action.Matches("list", "routes") {
t.Errorf("Bad action %v", action)
}
assert.Check(t, util.ContainsAll(output[0], "foo", "100% -> foo-01234"))
assert.Check(t, util.ContainsAll(output[1], "bar", "100% -> bar-98765"))
}

func TestRouteListWithTwoTargetsOutput(t *testing.T) {
route := createMockRouteTwoTarget("foo", "foo-01234", "foo-98765", 20, 80)
routeList := &v1alpha1.RouteList{Items: []v1alpha1.Route{*route}}
Expand Down
1 change: 1 addition & 0 deletions pkg/kn/commands/route/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ func NewRouteCommand(p *commands.KnParams) *cobra.Command {
Short: "Route command group",
}
routeCmd.AddCommand(NewRouteListCommand(p))
routeCmd.AddCommand(NewRouteDescribeCommand(p))
return routeCmd
}
64 changes: 64 additions & 0 deletions pkg/kn/commands/route/route_describe.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// 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 implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package route

import (
"errors"

"github.com/knative/client/pkg/kn/commands"
"github.com/spf13/cobra"
"k8s.io/cli-runtime/pkg/genericclioptions"
)

// NewRouteDescribeCommand represents 'kn route describe' command
func NewRouteDescribeCommand(p *commands.KnParams) *cobra.Command {
routeDescribePrintFlags := genericclioptions.NewPrintFlags("").WithDefaultOutput("yaml")
routeDescribeCommand := &cobra.Command{
Use: "describe NAME",
Short: "Describe available route.",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("requires the route name.")
}
namespace, err := p.GetNamespace(cmd)
if err != nil {
return err
}

client, err := p.NewClient(namespace)
if err != nil {
return err
}

describeRoute, err := client.GetRoute(args[0])
if err != nil {
return err
}

printer, err := routeDescribePrintFlags.ToPrinter()
if err != nil {
return err
}
err = printer.PrintObj(describeRoute, cmd.OutOrStdout())
if err != nil {
return err
}
return nil
},
}
commands.AddNamespaceFlags(routeDescribeCommand.Flags(), false)
routeDescribePrintFlags.AddFlags(routeDescribeCommand)
return routeDescribeCommand
}
130 changes: 130 additions & 0 deletions pkg/kn/commands/route/route_describe_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
// 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 implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package route

import (
"encoding/json"
"strings"
"testing"

"github.com/knative/client/pkg/kn/commands"
"github.com/knative/serving/pkg/apis/serving/v1alpha1"
"gotest.tools/assert"
"k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
client_testing "k8s.io/client-go/testing"
"sigs.k8s.io/yaml"
)

func fakeRouteDescribe(args []string, response *v1alpha1.Route) (action client_testing.Action, output string, err error) {
knParams := &commands.KnParams{}
cmd, fakeRoute, buf := commands.CreateTestKnCommand(NewRouteCommand(knParams), knParams)
fakeRoute.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 = buf.String()
return
}

func TestCompletion(t *testing.T) {
var expectedRoute v1alpha1.Route

setup := func(t *testing.T) {
expectedRoute = v1alpha1.Route{
TypeMeta: metav1.TypeMeta{
Kind: "Route",
APIVersion: "knative.dev/v1alpha1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Namespace: "default",
},
}
}

t.Run("requires the route name", func(t *testing.T) {
_, _, err := fakeRouteDescribe([]string{"route", "describe"}, &v1alpha1.Route{})
assert.Assert(t, err != nil)
assert.Assert(t, strings.Contains(err.Error(), "requires the route name."))
})

t.Run("describe a valid route with default output", func(t *testing.T) {
setup(t)

action, output, err := fakeRouteDescribe([]string{"route", "describe", "foo"}, &expectedRoute)
assert.Assert(t, err == nil)
assert.Assert(t, action != nil)
assert.Assert(t, action.Matches("get", "routes"))

jsonData, err := yaml.YAMLToJSON([]byte(output))
assert.Assert(t, err == nil)

var returnedRoute v1alpha1.Route
err = json.Unmarshal(jsonData, &returnedRoute)
assert.Assert(t, err == nil)
assert.Assert(t, equality.Semantic.DeepEqual(expectedRoute, returnedRoute))
})

t.Run("describe a valid route with special output", func(t *testing.T) {
t.Run("yaml", func(t *testing.T) {
setup(t)

action, output, err := fakeRouteDescribe([]string{"route", "describe", "foo", "-oyaml"}, &expectedRoute)
assert.Assert(t, err == nil)
assert.Assert(t, action != nil)
assert.Assert(t, action.Matches("get", "routes"))

jsonData, err := yaml.YAMLToJSON([]byte(output))
assert.Assert(t, err == nil)

var returnedRoute v1alpha1.Route
err = json.Unmarshal(jsonData, &returnedRoute)
assert.Assert(t, err == nil)
assert.Assert(t, equality.Semantic.DeepEqual(expectedRoute, returnedRoute))
})

t.Run("json", func(t *testing.T) {
setup(t)

action, output, err := fakeRouteDescribe([]string{"route", "describe", "foo", "-ojson"}, &expectedRoute)
assert.Assert(t, err == nil)
assert.Assert(t, action != nil)
assert.Assert(t, action.Matches("get", "routes"))

var returnedRoute v1alpha1.Route
err = json.Unmarshal([]byte(output), &returnedRoute)
assert.Assert(t, err == nil)
assert.Assert(t, equality.Semantic.DeepEqual(expectedRoute, returnedRoute))
})

t.Run("name", func(t *testing.T) {
setup(t)

action, output, err := fakeRouteDescribe([]string{"route", "describe", "foo", "-oname"}, &expectedRoute)
assert.Assert(t, err == nil)
assert.Assert(t, action != nil)
assert.Assert(t, action.Matches("get", "routes"))
assert.Assert(t, strings.Contains(output, expectedRoute.Name))
})
})
}
17 changes: 17 additions & 0 deletions pkg/kn/commands/service/service_list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,23 @@ func TestServiceListDefaultOutput(t *testing.T) {
assert.Check(t, util.ContainsAll(output[2], "bar", "bar.default.example.com", "2"))
}

func TestServiceListDefaultOutputNoHeaders(t *testing.T) {
service1 := createMockServiceWithParams("foo", "http://foo.default.example.com", 1)
service2 := createMockServiceWithParams("bar", "http://bar.default.example.com", 2)
serviceList := &v1alpha1.ServiceList{Items: []v1alpha1.Service{*service1, *service2}}
action, output, err := fakeServiceList([]string{"service", "list", "--no-headers"}, serviceList)
if err != nil {
t.Fatal(err)
}
if action == nil {
t.Errorf("No action")
} else if !action.Matches("list", "services") {
t.Errorf("Bad action %v", action)
}
assert.Check(t, util.ContainsAll(output[0], "foo", "foo.default.example.com", "1"))
assert.Check(t, util.ContainsAll(output[1], "bar", "bar.default.example.com", "2"))
}

func TestServiceGetOneOutput(t *testing.T) {
service := createMockServiceWithParams("foo", "foo.default.example.com", 1)
serviceList := &v1alpha1.ServiceList{Items: []v1alpha1.Service{*service}}
Expand Down
1 change: 1 addition & 0 deletions pkg/printers/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,6 @@ func (fn ResourcePrinterFunc) PrintObj(obj runtime.Object, w io.Writer) error {

// PrintOptions for different table printing options
type PrintOptions struct {
NoHeaders bool
//TODO: Add options for eg: with-kind, server-printing, wide etc
}
11 changes: 6 additions & 5 deletions pkg/printers/tableprinter.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,13 @@ func printRowsForHandlerEntry(output io.Writer, handler *handlerEntry, obj runti
if !results[1].IsNil() {
return results[1].Interface().(error)
}

var headers []string
for _, column := range handler.columnDefinitions {
headers = append(headers, strings.ToUpper(column.Name))
if !options.NoHeaders {
var headers []string
for _, column := range handler.columnDefinitions {
headers = append(headers, strings.ToUpper(column.Name))
}
printHeader(headers, output)
}
printHeader(headers, output)

if results[1].IsNil() {
rows := results[0].Interface().([]metav1beta1.TableRow)
Expand Down
Loading

0 comments on commit f64fa50

Please sign in to comment.