From d9abf1bcffd6f73050bd152c1f2cbac0ba09b7fa Mon Sep 17 00:00:00 2001 From: Piyush Garg Date: Tue, 24 Dec 2019 14:50:07 +0530 Subject: [PATCH] Add eventlistener list This will add the feature to list eventlisteners. Add docs and tests Fix #530 --- README.md | 1 + docs/cmd/tkn.md | 1 + docs/cmd/tkn_eventlistener.md | 25 ++++ docs/cmd/tkn_eventlistener_list.md | 49 ++++++++ docs/man/man1/tkn-eventlistener-list.1 | 86 +++++++++++++ docs/man/man1/tkn-eventlistener.1 | 45 +++++++ docs/man/man1/tkn.1 | 2 +- pkg/cmd/eventlistener/eventlistener.go | 42 +++++++ pkg/cmd/eventlistener/eventlistener_test.go | 30 +++++ pkg/cmd/eventlistener/list.go | 131 ++++++++++++++++++++ pkg/cmd/eventlistener/list_test.go | 128 +++++++++++++++++++ pkg/cmd/root.go | 2 + pkg/test/builder/eventlistener.go | 30 +++++ 13 files changed, 571 insertions(+), 1 deletion(-) create mode 100644 docs/cmd/tkn_eventlistener.md create mode 100644 docs/cmd/tkn_eventlistener_list.md create mode 100644 docs/man/man1/tkn-eventlistener-list.1 create mode 100644 docs/man/man1/tkn-eventlistener.1 create mode 100644 pkg/cmd/eventlistener/eventlistener.go create mode 100644 pkg/cmd/eventlistener/eventlistener_test.go create mode 100644 pkg/cmd/eventlistener/list.go create mode 100644 pkg/cmd/eventlistener/list_test.go create mode 100644 pkg/test/builder/eventlistener.go diff --git a/README.md b/README.md index cc5fdc641..a56bf1883 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,7 @@ The following commands help you understand and effectively use the Tekton CLI: * [`tkn clustertask:`](docs/cmd/tkn_clustertask.md) Parent command of the ClusterTask command group. * [`tkn completion:`](docs/cmd/tkn_completion.md) Outputs a BASH or ZSH completion script for `tkn` to allow command completion with Tab. * [`tkn condition:`](docs/cmd/tkn_condition.md) Parent command of the Condition command group. + * [`tkn eventlistener:`](docs/cmd/tkn_eventlistener.md) Parent command of the Eventlistener command group. * [`tkn pipeline:`](docs/cmd/tkn_pipeline.md) Parent command of the Pipeline command group. * [`tkn pipelinerun:`](docs/cmd/tkn_pipelinerun.md) Parent command of the Pipelinerun command group. * [`tkn resource:`](docs/cmd/tkn_resource.md) Parent command of the Resource command group. diff --git a/docs/cmd/tkn.md b/docs/cmd/tkn.md index 344e94658..d114a3974 100644 --- a/docs/cmd/tkn.md +++ b/docs/cmd/tkn.md @@ -17,6 +17,7 @@ CLI for tekton pipelines * [tkn clustertask](tkn_clustertask.md) - Manage clustertasks * [tkn completion](tkn_completion.md) - Prints shell completion scripts * [tkn condition](tkn_condition.md) - Manage conditions +* [tkn eventlistener](tkn_eventlistener.md) - Manage eventlisteners * [tkn pipeline](tkn_pipeline.md) - Manage pipelines * [tkn pipelinerun](tkn_pipelinerun.md) - Manage pipelineruns * [tkn resource](tkn_resource.md) - Manage pipeline resources diff --git a/docs/cmd/tkn_eventlistener.md b/docs/cmd/tkn_eventlistener.md new file mode 100644 index 000000000..0cd8783cf --- /dev/null +++ b/docs/cmd/tkn_eventlistener.md @@ -0,0 +1,25 @@ +## tkn eventlistener + +Manage eventlisteners + +***Aliases**: el,eventlisteners* + +### Synopsis + +Manage eventlisteners + +### Options + +``` + -c, --context string name of the kubeconfig context to use (default: kubectl config current-context) + -h, --help help for eventlistener + -k, --kubeconfig string kubectl config file (default: $HOME/.kube/config) + -n, --namespace string namespace to use (default: from $KUBECONFIG) + -C, --nocolour disable colouring (default: false) +``` + +### SEE ALSO + +* [tkn](tkn.md) - CLI for tekton pipelines +* [tkn eventlistener list](tkn_eventlistener_list.md) - Lists eventlisteners in a namespace + diff --git a/docs/cmd/tkn_eventlistener_list.md b/docs/cmd/tkn_eventlistener_list.md new file mode 100644 index 000000000..a484d61cc --- /dev/null +++ b/docs/cmd/tkn_eventlistener_list.md @@ -0,0 +1,49 @@ +## tkn eventlistener list + +Lists eventlisteners in a namespace + +***Aliases**: ls* + +### Usage + +``` +tkn eventlistener list +``` + +### Synopsis + +Lists eventlisteners in a namespace + +### Examples + +List all eventlisteners in namespace 'bar': + + tkn eventlistener list -n bar + +or + + tkn el ls -n bar + + +### 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 list + -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 + +``` + -c, --context string name of the kubeconfig context to use (default: kubectl config current-context) + -k, --kubeconfig string kubectl config file (default: $HOME/.kube/config) + -n, --namespace string namespace to use (default: from $KUBECONFIG) + -C, --nocolour disable colouring (default: false) +``` + +### SEE ALSO + +* [tkn eventlistener](tkn_eventlistener.md) - Manage eventlisteners + diff --git a/docs/man/man1/tkn-eventlistener-list.1 b/docs/man/man1/tkn-eventlistener-list.1 new file mode 100644 index 000000000..7782eb645 --- /dev/null +++ b/docs/man/man1/tkn-eventlistener-list.1 @@ -0,0 +1,86 @@ +.TH "TKN\-EVENTLISTENER\-LIST" "1" "" "Auto generated by spf13/cobra" "" +.nh +.ad l + + +.SH NAME +.PP +tkn\-eventlistener\-list \- Lists eventlisteners in a namespace + + +.SH SYNOPSIS +.PP +\fBtkn eventlistener list\fP + + +.SH DESCRIPTION +.PP +Lists eventlisteners in a namespace + + +.SH OPTIONS +.PP +\fB\-\-allow\-missing\-template\-keys\fP[=true] + 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. + +.PP +\fB\-h\fP, \fB\-\-help\fP[=false] + help for list + +.PP +\fB\-o\fP, \fB\-\-output\fP="" + Output format. One of: json|yaml|name|go\-template|go\-template\-file|template|templatefile|jsonpath|jsonpath\-file. + +.PP +\fB\-\-template\fP="" + Template string or path to template file to use when \-o=go\-template, \-o=go\-template\-file. The template format is golang templates [ +\[la]http://golang.org/pkg/text/template/#pkg-overview\[ra]]. + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +.PP +\fB\-c\fP, \fB\-\-context\fP="" + name of the kubeconfig context to use (default: kubectl config current\-context) + +.PP +\fB\-k\fP, \fB\-\-kubeconfig\fP="" + kubectl config file (default: $HOME/.kube/config) + +.PP +\fB\-n\fP, \fB\-\-namespace\fP="" + namespace to use (default: from $KUBECONFIG) + +.PP +\fB\-C\fP, \fB\-\-nocolour\fP[=false] + disable colouring (default: false) + + +.SH EXAMPLE +.PP +List all eventlisteners in namespace 'bar': + +.PP +.RS + +.nf +tkn eventlistener list \-n bar + +.fi +.RE + +.PP +or + +.PP +.RS + +.nf +tkn el ls \-n bar + +.fi +.RE + + +.SH SEE ALSO +.PP +\fBtkn\-eventlistener(1)\fP diff --git a/docs/man/man1/tkn-eventlistener.1 b/docs/man/man1/tkn-eventlistener.1 new file mode 100644 index 000000000..f982630e6 --- /dev/null +++ b/docs/man/man1/tkn-eventlistener.1 @@ -0,0 +1,45 @@ +.TH "TKN\-EVENTLISTENER" "1" "" "Auto generated by spf13/cobra" "" +.nh +.ad l + + +.SH NAME +.PP +tkn\-eventlistener \- Manage eventlisteners + + +.SH SYNOPSIS +.PP +\fBtkn eventlistener\fP + + +.SH DESCRIPTION +.PP +Manage eventlisteners + + +.SH OPTIONS +.PP +\fB\-c\fP, \fB\-\-context\fP="" + name of the kubeconfig context to use (default: kubectl config current\-context) + +.PP +\fB\-h\fP, \fB\-\-help\fP[=false] + help for eventlistener + +.PP +\fB\-k\fP, \fB\-\-kubeconfig\fP="" + kubectl config file (default: $HOME/.kube/config) + +.PP +\fB\-n\fP, \fB\-\-namespace\fP="" + namespace to use (default: from $KUBECONFIG) + +.PP +\fB\-C\fP, \fB\-\-nocolour\fP[=false] + disable colouring (default: false) + + +.SH SEE ALSO +.PP +\fBtkn(1)\fP, \fBtkn\-eventlistener\-list(1)\fP diff --git a/docs/man/man1/tkn.1 b/docs/man/man1/tkn.1 index 388f45e0b..cdb01ae9c 100644 --- a/docs/man/man1/tkn.1 +++ b/docs/man/man1/tkn.1 @@ -26,4 +26,4 @@ CLI for tekton pipelines .SH SEE ALSO .PP -\fBtkn\-clustertask(1)\fP, \fBtkn\-completion(1)\fP, \fBtkn\-condition(1)\fP, \fBtkn\-pipeline(1)\fP, \fBtkn\-pipelinerun(1)\fP, \fBtkn\-resource(1)\fP, \fBtkn\-task(1)\fP, \fBtkn\-taskrun(1)\fP, \fBtkn\-triggerbinding(1)\fP, \fBtkn\-triggertemplate(1)\fP, \fBtkn\-version(1)\fP +\fBtkn\-clustertask(1)\fP, \fBtkn\-completion(1)\fP, \fBtkn\-condition(1)\fP, \fBtkn\-eventlistener(1)\fP, \fBtkn\-pipeline(1)\fP, \fBtkn\-pipelinerun(1)\fP, \fBtkn\-resource(1)\fP, \fBtkn\-task(1)\fP, \fBtkn\-taskrun(1)\fP, \fBtkn\-triggerbinding(1)\fP, \fBtkn\-triggertemplate(1)\fP, \fBtkn\-version(1)\fP diff --git a/pkg/cmd/eventlistener/eventlistener.go b/pkg/cmd/eventlistener/eventlistener.go new file mode 100644 index 000000000..4c255c9ab --- /dev/null +++ b/pkg/cmd/eventlistener/eventlistener.go @@ -0,0 +1,42 @@ +// Copyright © 2019 The Tekton 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 eventlistener + +import ( + "github.com/spf13/cobra" + "github.com/tektoncd/cli/pkg/cli" + "github.com/tektoncd/cli/pkg/flags" +) + +func Command(p cli.Params) *cobra.Command { + cmd := &cobra.Command{ + Use: "eventlistener", + Aliases: []string{"el", "eventlisteners"}, + Short: "Manage eventlisteners", + Annotations: map[string]string{ + "commandType": "main", + }, + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + return flags.InitParams(p, cmd) + }, + } + + flags.AddTektonOptions(cmd) + cmd.AddCommand( + listCommand(p), + ) + + return cmd +} diff --git a/pkg/cmd/eventlistener/eventlistener_test.go b/pkg/cmd/eventlistener/eventlistener_test.go new file mode 100644 index 000000000..058318c3f --- /dev/null +++ b/pkg/cmd/eventlistener/eventlistener_test.go @@ -0,0 +1,30 @@ +// Copyright © 2019 The Tekton 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 eventlistener + +import ( + "testing" + + "github.com/tektoncd/cli/pkg/test" +) + +func TestEventListener_invalid(t *testing.T) { + p := &test.Params{} + el := Command(p) + out, err := test.ExecuteCommand(el, "foobar") + if err == nil { + t.Errorf("No errors was defined. Output: %s", out) + } +} diff --git a/pkg/cmd/eventlistener/list.go b/pkg/cmd/eventlistener/list.go new file mode 100644 index 000000000..470c4efd7 --- /dev/null +++ b/pkg/cmd/eventlistener/list.go @@ -0,0 +1,131 @@ +// Copyright © 2019 The Tekton 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 eventlistener + +import ( + "errors" + "fmt" + "text/tabwriter" + + "github.com/spf13/cobra" + "github.com/tektoncd/cli/pkg/cli" + "github.com/tektoncd/cli/pkg/formatted" + "github.com/tektoncd/cli/pkg/helper/validate" + "github.com/tektoncd/cli/pkg/printer" + "github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1" + "github.com/tektoncd/triggers/pkg/client/clientset/versioned" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + cliopts "k8s.io/cli-runtime/pkg/genericclioptions" +) + +const ( + emptyMsg = "No eventlisteners found" +) + +func listCommand(p cli.Params) *cobra.Command { + f := cliopts.NewPrintFlags("list") + + eg := `List all eventlisteners in namespace 'bar': + + tkn eventlistener list -n bar + +or + + tkn el ls -n bar +` + + c := &cobra.Command{ + Use: "list", + Aliases: []string{"ls"}, + Short: "Lists eventlisteners in a namespace", + Annotations: map[string]string{ + "commandType": "main", + }, + Example: eg, + RunE: func(cmd *cobra.Command, args []string) error { + if err := validate.NamespaceExists(p); err != nil { + return err + } + + cs, err := p.Clients() + if err != nil { + return err + } + + els, err := list(cs.Triggers, p.Namespace()) + if err != nil { + return fmt.Errorf(`failed to list eventlisteners from %s namespace \n`, p.Namespace()) + } + + output, err := cmd.LocalFlags().GetString("output") + if err != nil { + return errors.New(`output option not set properly \n`) + } + + stream := &cli.Stream{ + Out: cmd.OutOrStdout(), + Err: cmd.OutOrStderr(), + } + + if output != "" { + return printer.PrintObject(stream.Out, els, f) + } + + if err = printFormatted(stream, els, p); err != nil { + return errors.New(`failed to print eventlisteners \n`) + } + return nil + }, + } + + f.AddFlags(c) + return c +} + +func list(client versioned.Interface, namespace string) (*v1alpha1.EventListenerList, error) { + els, err := client.TektonV1alpha1().EventListeners(namespace).List(metav1.ListOptions{}) + if err != nil { + return nil, err + } + + // NOTE: this is required for -o json|yaml to work properly since + // tektoncd go client fails to set these; probably a bug + els.GetObjectKind().SetGroupVersionKind( + schema.GroupVersionKind{ + Version: "tekton.dev/v1alpha1", + Kind: "EventListenerList", + }) + + return els, nil +} + +func printFormatted(s *cli.Stream, els *v1alpha1.EventListenerList, p cli.Params) error { + if len(els.Items) == 0 { + fmt.Fprintln(s.Err, emptyMsg) + return nil + } + + w := tabwriter.NewWriter(s.Out, 0, 5, 3, ' ', tabwriter.TabIndent) + fmt.Fprintln(w, "NAME\tAGE") + for _, el := range els.Items { + fmt.Fprintf(w, "%s\t%s\n", + el.Name, + formatted.Age(&el.CreationTimestamp, p.Time()), + ) + } + + return w.Flush() +} diff --git a/pkg/cmd/eventlistener/list_test.go b/pkg/cmd/eventlistener/list_test.go new file mode 100644 index 000000000..215c46d46 --- /dev/null +++ b/pkg/cmd/eventlistener/list_test.go @@ -0,0 +1,128 @@ +// Copyright © 2019 The Tekton 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 eventlistener + +import ( + "strings" + "testing" + "time" + + "github.com/jonboulle/clockwork" + "github.com/spf13/cobra" + "github.com/tektoncd/cli/pkg/test" + cb "github.com/tektoncd/cli/pkg/test/builder" + "github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1" + triggertest "github.com/tektoncd/triggers/test" + tb "github.com/tektoncd/triggers/test/builder" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestListEventListener(t *testing.T) { + now := time.Now() + + ns := []*corev1.Namespace{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "random", + }, + }, + } + + els := []*v1alpha1.EventListener{ + tb.EventListener("tb1", "foo", cb.EventListenerCreationTime(now.Add(-2*time.Minute))), + tb.EventListener("tb2", "foo", cb.EventListenerCreationTime(now.Add(-30*time.Second))), + tb.EventListener("tb3", "foo", cb.EventListenerCreationTime(now.Add(-200*time.Hour))), + tb.EventListener("tb4", "foo"), + } + + tests := []struct { + name string + command *cobra.Command + args []string + expected []string + wantError bool + }{ + { + name: "Invalid namespace", + command: command(t, els, now, ns), + args: []string{"list", "-n", "default"}, + expected: []string{ + "Error: namespaces \"default\" not found\n", + }, + wantError: true, + }, + { + name: "No EventListener", + command: command(t, els, now, ns), + args: []string{"list", "-n", "random"}, + expected: []string{ + "No eventlisteners found\n", + }, + wantError: false, + }, + { + name: "Multiple EventListener", + command: command(t, els, now, ns), + args: []string{"list", "-n", "foo"}, + expected: []string{ + "NAME AGE", + "tb1 2 minutes ago", + "tb2 30 seconds ago", + "tb3 1 week ago", + "tb4 ---", + "", + }, + wantError: false, + }, + { + name: "Multiple EventListener with output format", + command: command(t, els, now, ns), + args: []string{"list", "-n", "foo", "-o", "jsonpath={range .items[*]}{.metadata.name}{\"\\n\"}{end}"}, + expected: []string{ + "tb1", + "tb2", + "tb3", + "tb4", + "", + }, + wantError: false, + }, + } + + for _, td := range tests { + t.Run(td.name, func(t *testing.T) { + got, err := test.ExecuteCommand(td.command, td.args...) + + if err != nil && !td.wantError { + t.Errorf("Unexpected error: %v", err) + } + test.AssertOutput(t, strings.Join(td.expected, "\n"), got) + }) + } +} + +func command(t *testing.T, els []*v1alpha1.EventListener, now time.Time, ns []*corev1.Namespace) *cobra.Command { + // fake clock advanced by 1 hour + clock := clockwork.NewFakeClockAt(now) + cs := test.SeedTestResources(t, triggertest.Resources{EventListeners: els, Namespaces: ns}) + p := &test.Params{Tekton: cs.Pipeline, Clock: clock, Kube: cs.Kube, Triggers: cs.Triggers} + return Command(p) +} diff --git a/pkg/cmd/root.go b/pkg/cmd/root.go index 435bb9d85..20927cf65 100644 --- a/pkg/cmd/root.go +++ b/pkg/cmd/root.go @@ -23,6 +23,7 @@ import ( "github.com/tektoncd/cli/pkg/cmd/clustertask" "github.com/tektoncd/cli/pkg/cmd/completion" "github.com/tektoncd/cli/pkg/cmd/condition" + "github.com/tektoncd/cli/pkg/cmd/eventlistener" "github.com/tektoncd/cli/pkg/cmd/pipeline" "github.com/tektoncd/cli/pkg/cmd/pipelineresource" "github.com/tektoncd/cli/pkg/cmd/pipelinerun" @@ -80,6 +81,7 @@ func Root(p cli.Params) *cobra.Command { clustertask.Command(p), completion.Command(), condition.Command(p), + eventlistener.Command(p), pipeline.Command(p), pipelineresource.Command(p), pipelinerun.Command(p), diff --git a/pkg/test/builder/eventlistener.go b/pkg/test/builder/eventlistener.go new file mode 100644 index 000000000..af663eded --- /dev/null +++ b/pkg/test/builder/eventlistener.go @@ -0,0 +1,30 @@ +// Copyright © 2019 The Tekton 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 builder + +import ( + "time" + + "github.com/tektoncd/triggers/pkg/apis/triggers/v1alpha1" + tb "github.com/tektoncd/triggers/test/builder" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EventListenerCreationTime sets CreationTimestamp of the EventListener +func EventListenerCreationTime(t time.Time) tb.EventListenerOp { + return func(eventlistener *v1alpha1.EventListener) { + eventlistener.CreationTimestamp = metav1.Time{Time: t} + } +}