Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add kn route list command #202

Merged
merged 1 commit into from
Jul 2, 2019
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
1 change: 1 addition & 0 deletions docs/cmd/kn.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Eventing: Manage event subscriptions and channels. Connect up event sources.

* [kn completion](kn_completion.md) - Output shell completion code (default Bash)
* [kn revision](kn_revision.md) - Revision command group
* [kn route](kn_route.md) - Route command group
* [kn service](kn_service.md) - Service command group
* [kn version](kn_version.md) - Prints the client version

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

Route command group

### Synopsis

Route command group

### Options

```
-h, --help help for route
```

### 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](kn.md) - Knative client
* [kn route list](kn_route_list.md) - List available routes.

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

List available routes.

### Synopsis

List available routes.

```
kn route list NAME [flags]
```

### Examples

```

# List all routes
kn route list

# List route 'web' in namespace 'dev'
kn route list web -n dev

# List all routes in yaml format
kn route list -o yaml
```

### Options

```
--all-namespaces If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.
--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.
-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].
```

### 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

82 changes: 82 additions & 0 deletions pkg/kn/commands/route/human_readable_flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// 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 (
"fmt"

"github.com/knative/client/pkg/kn/commands"
hprinters "github.com/knative/client/pkg/printers"
servingv1alpha1 "github.com/knative/serving/pkg/apis/serving/v1alpha1"
metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
"k8s.io/apimachinery/pkg/runtime"
)

// RouteListHandlers adds print handlers for route list command
func RouteListHandlers(h hprinters.PrintHandler) {
kRouteColumnDefinitions := []metav1beta1.TableColumnDefinition{
{Name: "Name", Type: "string", Description: "Name of the Knative route."},
{Name: "URL", Type: "string", Description: "URL of the Knative route."},
{Name: "Age", Type: "string", Description: "Age of the Knative route."},
{Name: "Conditions", Type: "string", Description: "Conditions describing statuses of route components."},
{Name: "Traffic", Type: "integer", Description: "Traffic configured for route."},
}
h.TableHandler(kRouteColumnDefinitions, printRoute)
h.TableHandler(kRouteColumnDefinitions, printKRouteList)
}

// printKRouteList populates the Knative route list table rows
func printKRouteList(kRouteList *servingv1alpha1.RouteList, options hprinters.PrintOptions) ([]metav1beta1.TableRow, error) {
rows := make([]metav1beta1.TableRow, 0, len(kRouteList.Items))
for _, ksvc := range kRouteList.Items {
r, err := printRoute(&ksvc, options)
if err != nil {
return nil, err
}
rows = append(rows, r...)
}
return rows, nil
}

// printRoute populates the Knative route table rows
func printRoute(route *servingv1alpha1.Route, options hprinters.PrintOptions) ([]metav1beta1.TableRow, error) {
name := route.Name
url := route.Status.URL
age := commands.TranslateTimestampSince(route.CreationTimestamp)
conditions := commands.ConditionsValue(route.Status.Conditions)
traffic := calculateTraffic(route.Status.Traffic)
row := metav1beta1.TableRow{
Object: runtime.RawExtension{Object: route},
}
row.Cells = append(row.Cells,
name,
url,
age,
conditions,
traffic)
return []metav1beta1.TableRow{row}, nil
}

func calculateTraffic(targets []servingv1alpha1.TrafficTarget) string {
var traffic string

Choose a reason for hiding this comment

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

it seems that we keep on overriding traffic string in a loop?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@cppforlife: yup but with an update only if there is traffic split available

traffic = fmt.Sprintf("%s, %d%% -> %s", traffic, target.Percent, target.RevisionName)

Choose a reason for hiding this comment

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

im still confused, should it be traffic += ... instead since there could be multiple targets?

for _, target := range targets {
if len(traffic) > 0 {
traffic = fmt.Sprintf("%s, %d%% -> %s", traffic, target.Percent, target.RevisionName)
} else {
traffic = fmt.Sprintf("%d%% -> %s", target.Percent, target.RevisionName)
}
}
return traffic
}
87 changes: 87 additions & 0 deletions pkg/kn/commands/route/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// 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 (
"fmt"

"github.com/knative/client/pkg/kn/commands"
"github.com/spf13/cobra"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime/schema"
)

// NewrouteListCommand represents 'kn route list' command
func NewRouteListCommand(p *commands.KnParams) *cobra.Command {
routeListFlags := NewRouteListFlags()
routeListCommand := &cobra.Command{
Use: "list NAME",
Short: "List available routes.",
Example: `
# List all routes
kn route list

# List route 'web' in namespace 'dev'
kn route list web -n dev

# List all routes in yaml format
kn route list -o yaml`,
RunE: func(cmd *cobra.Command, args []string) error {
client, err := p.ServingFactory()
if err != nil {
return err
}
namespace, err := p.GetNamespace(cmd)
if err != nil {
return err
}
var listOptions v1.ListOptions
switch len(args) {
case 0:
listOptions = v1.ListOptions{}
case 1:
listOptions.FieldSelector = fields.Set(map[string]string{"metadata.name": args[0]}).String()
default:
return fmt.Errorf("'kn route list' accepts maximum 1 argument.")
}
route, err := client.Routes(namespace).List(listOptions)
if err != nil {
return err
}
if len(route.Items) == 0 {
fmt.Fprintf(cmd.OutOrStdout(), "No resources found.\n")
return nil
}
route.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{
Group: "knative.dev",
Version: "v1alpha1",
Kind: "route",
})
printer, err := routeListFlags.ToPrinter()
if err != nil {
return err
}
err = printer.PrintObj(route, cmd.OutOrStdout())
if err != nil {
return err
}
return nil
},
}
commands.AddNamespaceFlags(routeListCommand.Flags(), true)
routeListFlags.AddFlags(routeListCommand)
return routeListCommand
}
71 changes: 71 additions & 0 deletions pkg/kn/commands/route/list_flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// 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 route

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

// RouteListFlags composes common printer flag structs
// used in the 'kn route list' command.
type RouteListFlags struct {
GenericPrintFlags *genericclioptions.PrintFlags
HumanReadableFlags *commands.HumanPrintFlags
}

// AllowedFormats is the list of formats in which data can be displayed
func (f *RouteListFlags) AllowedFormats() []string {
formats := f.GenericPrintFlags.AllowedFormats()
formats = append(formats, f.HumanReadableFlags.AllowedFormats()...)
return formats
}

// ToPrinter attempts to find a composed set of RouteListFlags suitable for
// returning a printer based on current flag values.
func (f *RouteListFlags) 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(RouteListHandlers)
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 *RouteListFlags) AddFlags(cmd *cobra.Command) {
f.GenericPrintFlags.AddFlags(cmd)
f.HumanReadableFlags.AddFlags(cmd)
}

// NewRouteListFlags returns flags associated with humanreadable,
// template, and "name" printing, with default values set.
func NewRouteListFlags() *RouteListFlags {
return &RouteListFlags{
GenericPrintFlags: genericclioptions.NewPrintFlags(""),
HumanReadableFlags: commands.NewHumanPrintFlags(),
}
}
47 changes: 47 additions & 0 deletions pkg/kn/commands/route/list_flags_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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 route

import (
"reflect"
"testing"

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

func TestRoutListFlags(t *testing.T) {

Choose a reason for hiding this comment

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

i dont think we need this test. it's too close to implementation so it doesnt add confidence that this object is correct. additionally route list flags object is used in the command test, so it gets tested there indirectly in a more resilient manner.

Copy link
Collaborator Author

@navidshaikh navidshaikh Jul 1, 2019

Choose a reason for hiding this comment

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

I added this file's test, as without tests pk/kn/commands/route/list_flags.go was failing CI go test coverage threshold (must be > 50%).

should I be testing this in some other way?

testObject := createMockRouteMeta("foo")
knParams := &commands.KnParams{}
cmd, _, buf := commands.CreateTestKnCommand(NewRouteCommand(knParams), knParams)
routeListFlags := NewRouteListFlags()
routeListFlags.AddFlags(cmd)
printer, err := routeListFlags.ToPrinter()
if genericclioptions.IsNoCompatiblePrinterError(err) {
t.Fatalf("Expected to match human readable printer.")
}
if err != nil {
t.Fatalf("Failed to find a proper printer.")
}
err = printer.PrintObj(testObject, buf)
if err != nil {
t.Fatalf("Failed to print the object.")
}
actualFormats := routeListFlags.AllowedFormats()
expectedFormats := []string{"json", "yaml", "name", "go-template", "go-template-file", "template", "templatefile", "jsonpath", "jsonpath-file"}
if reflect.DeepEqual(actualFormats, expectedFormats) {
t.Fatalf("Expecting allowed formats:\n%s\nFound:\n%s\n", expectedFormats, actualFormats)
}
}
Loading