From bf059a4add97687f09079f0a17241846a46c7cdd Mon Sep 17 00:00:00 2001 From: vinamra28 Date: Tue, 1 Sep 2020 11:04:27 +0530 Subject: [PATCH 1/2] Enable auto-select support in ClusterTaskDescribe if only one is present When only one clustertask is present then instead of prompting to user to select the clustertask at the time of running the clustertask describe command then clustertask will be autoselected and the output will be displayed. Also extracted to allClusterTaskNames function from pkg/cmd/clustertask/delete.go to pkg/clustertask/clustertask.go in order to maintain code consistency and also added unit tests for the same. Signed-off-by: vinamra28 --- pkg/clustertask/clustertask.go | 18 ++++ pkg/clustertask/clustertask_test.go | 84 +++++++++++++++++++ pkg/cmd/clustertask/delete.go | 14 +--- pkg/cmd/clustertask/describe.go | 23 +++-- pkg/cmd/clustertask/describe_test.go | 57 +++++++++++++ ...houtNameIfOnlyOneClusterTaskPresent.golden | 29 +++++++ ...eIfOnlyOneV1beta1ClusterTaskPresent.golden | 29 +++++++ 7 files changed, 229 insertions(+), 25 deletions(-) create mode 100644 pkg/cmd/clustertask/testdata/TestClusterTaskDescribe_WithoutNameIfOnlyOneClusterTaskPresent.golden create mode 100644 pkg/cmd/clustertask/testdata/TestClusterTaskDescribe_WithoutNameIfOnlyOneV1beta1ClusterTaskPresent.golden diff --git a/pkg/clustertask/clustertask.go b/pkg/clustertask/clustertask.go index 80929c491..338b42c6e 100644 --- a/pkg/clustertask/clustertask.go +++ b/pkg/clustertask/clustertask.go @@ -30,6 +30,24 @@ import ( var clustertaskGroupResource = schema.GroupVersionResource{Group: "tekton.dev", Resource: "clustertasks"} +func GetAllClusterTaskNames(p cli.Params) ([]string, error) { + cs, err := p.Clients() + if err != nil { + return nil, err + } + + clustertasks, err := List(cs, metav1.ListOptions{}) + if err != nil { + return nil, err + } + + ret := []string{} + for _, item := range clustertasks.Items { + ret = append(ret, item.ObjectMeta.Name) + } + return ret, nil +} + func List(c *cli.Clients, opts metav1.ListOptions) (*v1beta1.ClusterTaskList, error) { unstructuredCT, err := actions.List(clustertaskGroupResource, c, "", opts) if err != nil { diff --git a/pkg/clustertask/clustertask_test.go b/pkg/clustertask/clustertask_test.go index 14a9ca7d5..a14675582 100644 --- a/pkg/clustertask/clustertask_test.go +++ b/pkg/clustertask/clustertask_test.go @@ -31,6 +31,90 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +func TestClusterTask_GetAllTaskNames(t *testing.T) { + version := "v1alpha1" + clock := clockwork.NewFakeClock() + ctdata := []*v1alpha1.ClusterTask{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "clustertask", + // created 5 minutes back + CreationTimestamp: metav1.Time{Time: clock.Now().Add(-5 * time.Minute)}, + }, + }, + } + cs, _ := test.SeedTestData(t, pipelinetest.Data{ + ClusterTasks: ctdata, + }) + cs.Pipeline.Resources = cb.APIResourceList(version, []string{"clustertask"}) + tdc := testDynamic.Options{} + dc, err := tdc.Client( + cb.UnstructuredCT(ctdata[0], version), + ) + if err != nil { + t.Errorf("unable to create dynamic client: %v", err) + } + + ctdata2 := []*v1alpha1.ClusterTask{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "clustertask", + // created 5 minutes back + CreationTimestamp: metav1.Time{Time: clock.Now().Add(-5 * time.Minute)}, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "clustertask2", + // created 5 minutes back + CreationTimestamp: metav1.Time{Time: clock.Now().Add(-5 * time.Minute)}, + }, + }, + } + cs2, _ := test.SeedTestData(t, pipelinetest.Data{ + ClusterTasks: ctdata2, + }) + cs2.Pipeline.Resources = cb.APIResourceList(version, []string{"clustertask"}) + tdc2 := testDynamic.Options{} + dc2, err := tdc2.Client( + cb.UnstructuredCT(ctdata2[0], version), + cb.UnstructuredCT(ctdata2[1], version), + ) + if err != nil { + t.Errorf("unable to create dynamic client: %v", err) + } + + p := &test.Params{Tekton: cs.Pipeline, Clock: clock, Kube: cs.Kube, Dynamic: dc} + p2 := &test.Params{Tekton: cs2.Pipeline, Clock: clock, Kube: cs2.Kube, Dynamic: dc2} + + testParams := []struct { + name string + params *test.Params + want []string + }{ + { + name: "Single ClusterTask", + params: p, + want: []string{"clustertask"}, + }, + { + name: "Multi ClusterTasks", + params: p2, + want: []string{"clustertask", "clustertask2"}, + }, + } + + for _, tp := range testParams { + t.Run(tp.name, func(t *testing.T) { + got, err := GetAllClusterTaskNames(tp.params) + if err != nil { + t.Errorf("unexpected Error") + } + test.AssertOutput(t, tp.want, got) + }) + } +} + func TestClusterTask_List(t *testing.T) { version := "v1alpha1" clock := clockwork.NewFakeClock() diff --git a/pkg/cmd/clustertask/delete.go b/pkg/cmd/clustertask/delete.go index 911004428..a142e2c3f 100644 --- a/pkg/cmd/clustertask/delete.go +++ b/pkg/cmd/clustertask/delete.go @@ -86,7 +86,7 @@ func deleteClusterTasks(opts *options.DeleteOptions, s *cli.Stream, p cli.Params }) switch { case opts.DeleteAll: - cts, err := allClusterTaskNames(cs) + cts, err := clustertask.GetAllClusterTaskNames(p) if err != nil { return err } @@ -112,18 +112,6 @@ func deleteClusterTasks(opts *options.DeleteOptions, s *cli.Stream, p cli.Params return d.Errors() } -func allClusterTaskNames(cs *cli.Clients) ([]string, error) { - clusterTasks, err := clustertask.List(cs, metav1.ListOptions{}) - if err != nil { - return nil, err - } - var names []string - for _, ct := range clusterTasks.Items { - names = append(names, ct.Name) - } - return names, nil -} - func taskRunLister(cs *cli.Clients, p cli.Params) func(string) ([]string, error) { return func(taskName string) ([]string, error) { lOpts := metav1.ListOptions{ diff --git a/pkg/cmd/clustertask/describe.go b/pkg/cmd/clustertask/describe.go index a5012d789..d45f618cc 100644 --- a/pkg/cmd/clustertask/describe.go +++ b/pkg/cmd/clustertask/describe.go @@ -165,10 +165,18 @@ or } if len(args) == 0 { - err = askClusterTaskName(opts, p) + clusterTaskNames, err := clustertask.GetAllClusterTaskNames(p) if err != nil { return err } + if len(clusterTaskNames) == 1 { + opts.ClusterTaskName = clusterTaskNames[0] + } else { + err = askClusterTaskName(opts, clusterTaskNames) + if err != nil { + return err + } + } } else { opts.ClusterTaskName = args[0] } @@ -261,20 +269,11 @@ func sortResourcesByTypeAndName(tres []v1beta1.TaskResource) []v1beta1.TaskResou return tres } -func askClusterTaskName(opts *options.DescribeOptions, p cli.Params) error { - cs, err := p.Clients() - if err != nil { - return err - } - clusterTaskNames, err := allClusterTaskNames(cs) - if err != nil { - return err - } +func askClusterTaskName(opts *options.DescribeOptions, clusterTaskNames []string) error { if len(clusterTaskNames) == 0 { return fmt.Errorf("no ClusterTasks found") } - - err = opts.Ask(options.ResourceNameClusterTask, clusterTaskNames) + err := opts.Ask(options.ResourceNameClusterTask, clusterTaskNames) if err != nil { return err } diff --git a/pkg/cmd/clustertask/describe_test.go b/pkg/cmd/clustertask/describe_test.go index d3bd3b97e..2f726d1ee 100644 --- a/pkg/cmd/clustertask/describe_test.go +++ b/pkg/cmd/clustertask/describe_test.go @@ -271,6 +271,34 @@ func Test_ClusterTaskDescribe(t *testing.T) { } } +func TestClusterTaskDescribe_WithoutNameIfOnlyOneClusterTaskPresent(t *testing.T) { + cstasks := []*v1alpha1.ClusterTask{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "task-1", + }, + }, + } + version := "v1alpha1" + tdc := testDynamic.Options{} + dynamic, err := tdc.Client( + cb.UnstructuredCT(cstasks[0], version), + ) + if err != nil { + t.Errorf("unable to create dynamic client: %v", err) + } + + cs, _ := test.SeedTestData(t, pipelinetest.Data{ClusterTasks: cstasks}) + cs.Pipeline.Resources = cb.APIResourceList(version, []string{"clustertask", "taskrun"}) + p := &test.Params{Tekton: cs.Pipeline, Kube: cs.Kube, Dynamic: dynamic} + clusterTask := Command(p) + out, err := test.ExecuteCommand(clusterTask, "desc") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + golden.Assert(t, out, fmt.Sprintf("%s.golden", t.Name())) +} + func TestClusterTask_custom_output(t *testing.T) { name := "clustertask" expected := "clustertask.tekton.dev/" + name @@ -467,3 +495,32 @@ func TestClusterTaskDescribe_With_Workspaces(t *testing.T) { } golden.Assert(t, out, fmt.Sprintf("%s.golden", t.Name())) } + +func TestClusterTaskDescribe_WithoutNameIfOnlyOneV1beta1ClusterTaskPresent(t *testing.T) { + cttasks := []*v1beta1.ClusterTask{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "task-1", + }, + }, + } + + version := "v1beta1" + tdc := testDynamic.Options{} + dynamic, err := tdc.Client( + cb.UnstructuredV1beta1CT(cttasks[0], version), + ) + if err != nil { + t.Errorf("unable to create dynamic client: %v", err) + } + + cs, _ := test.SeedV1beta1TestData(t, pipelinev1beta1test.Data{ClusterTasks: cttasks}) + cs.Pipeline.Resources = cb.APIResourceList(version, []string{"clustertask", "taskrun"}) + p := &test.Params{Tekton: cs.Pipeline, Kube: cs.Kube, Dynamic: dynamic} + cttask := Command(p) + out, err := test.ExecuteCommand(cttask, "desc") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + golden.Assert(t, out, fmt.Sprintf("%s.golden", t.Name())) +} diff --git a/pkg/cmd/clustertask/testdata/TestClusterTaskDescribe_WithoutNameIfOnlyOneClusterTaskPresent.golden b/pkg/cmd/clustertask/testdata/TestClusterTaskDescribe_WithoutNameIfOnlyOneClusterTaskPresent.golden new file mode 100644 index 000000000..a200d5f15 --- /dev/null +++ b/pkg/cmd/clustertask/testdata/TestClusterTaskDescribe_WithoutNameIfOnlyOneClusterTaskPresent.golden @@ -0,0 +1,29 @@ +Name: task-1 + +Input Resources + + No input resources + +Output Resources + + No output resources + +Params + + No params + +Results + + No results + +Workspaces + + No workspaces + +Steps + + No steps + +Taskruns + + No taskruns diff --git a/pkg/cmd/clustertask/testdata/TestClusterTaskDescribe_WithoutNameIfOnlyOneV1beta1ClusterTaskPresent.golden b/pkg/cmd/clustertask/testdata/TestClusterTaskDescribe_WithoutNameIfOnlyOneV1beta1ClusterTaskPresent.golden new file mode 100644 index 000000000..a200d5f15 --- /dev/null +++ b/pkg/cmd/clustertask/testdata/TestClusterTaskDescribe_WithoutNameIfOnlyOneV1beta1ClusterTaskPresent.golden @@ -0,0 +1,29 @@ +Name: task-1 + +Input Resources + + No input resources + +Output Resources + + No output resources + +Params + + No params + +Results + + No results + +Workspaces + + No workspaces + +Steps + + No steps + +Taskruns + + No taskruns From 5b2e45937b7c44f99f5c966f7b46563092db25b2 Mon Sep 17 00:00:00 2001 From: vinamra28 Date: Wed, 9 Sep 2020 13:37:37 +0530 Subject: [PATCH 2/2] Enable auto-select support in TaskRunDescribe if only one is present When only one taskrun is present then instead of prompting to user to select the taskrun at the time of running the taskrun describe command it will be autoselected and the output will be displayed. Signed-off-by: vinamra28 --- pkg/cmd/taskrun/describe.go | 20 +-- pkg/cmd/taskrun/describe_test.go | 125 ++++++++++++++++++ ..._WithoutNameIfOnlyOneTaskRunPresent.golden | 38 ++++++ ...tNameIfOnlyOneV1beta1TaskRunPresent.golden | 38 ++++++ 4 files changed, 212 insertions(+), 9 deletions(-) create mode 100644 pkg/cmd/taskrun/testdata/TestTaskRunDescribe_WithoutNameIfOnlyOneTaskRunPresent.golden create mode 100644 pkg/cmd/taskrun/testdata/TestTaskRunDescribe_WithoutNameIfOnlyOneV1beta1TaskRunPresent.golden diff --git a/pkg/cmd/taskrun/describe.go b/pkg/cmd/taskrun/describe.go index 8624c8ca4..cb3c1a0f2 100644 --- a/pkg/cmd/taskrun/describe.go +++ b/pkg/cmd/taskrun/describe.go @@ -73,13 +73,21 @@ or } if len(args) == 0 { + lOpts := metav1.ListOptions{} if !opts.Last { - err = askTaskRunName(opts, p) + trs, err := trlist.GetAllTaskRuns(p, lOpts, opts.Limit) if err != nil { return err } + if len(trs) == 1 { + opts.TaskrunName = strings.Fields(trs[0])[0] + } else { + err = askTaskRunName(opts, trs) + if err != nil { + return err + } + } } else { - lOpts := metav1.ListOptions{} trs, err := trlist.GetAllTaskRuns(p, lOpts, 1) if err != nil { return err @@ -113,18 +121,12 @@ or return c } -func askTaskRunName(opts *options.DescribeOptions, p cli.Params) error { - lOpts := metav1.ListOptions{} - +func askTaskRunName(opts *options.DescribeOptions, trs []string) error { err := opts.ValidateOpts() if err != nil { return err } - trs, err := trlist.GetAllTaskRuns(opts.Params, lOpts, opts.Limit) - if err != nil { - return err - } if len(trs) == 0 { return fmt.Errorf("no TaskRuns found") } diff --git a/pkg/cmd/taskrun/describe_test.go b/pkg/cmd/taskrun/describe_test.go index 6e3a737fa..5e4c05980 100644 --- a/pkg/cmd/taskrun/describe_test.go +++ b/pkg/cmd/taskrun/describe_test.go @@ -780,6 +780,71 @@ func TestTaskRunWithSpecDescribe_custom_timeout(t *testing.T) { golden.Assert(t, actual, fmt.Sprintf("%s.golden", t.Name())) } +func TestTaskRunDescribe_WithoutNameIfOnlyOneTaskRunPresent(t *testing.T) { + clock := clockwork.NewFakeClock() + + trs := []*v1alpha1.TaskRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "tr-1", + Labels: map[string]string{"tekton.dev/task": "t1"}, + }, + Spec: v1alpha1.TaskRunSpec{ + TaskRef: &v1alpha1.TaskRef{ + Name: "t1", + }, + }, + Status: v1alpha1.TaskRunStatus{ + Status: duckv1beta1.Status{ + Conditions: duckv1beta1.Conditions{ + { + Status: corev1.ConditionTrue, + Reason: v1beta1.TaskRunReasonSuccessful.String(), + }, + }, + }, + TaskRunStatusFields: v1beta1.TaskRunStatusFields{ + StartTime: &metav1.Time{Time: clock.Now().Add(-10 * time.Minute)}, + CompletionTime: &metav1.Time{Time: clock.Now().Add(-5 * time.Minute)}, + }, + }, + }, + } + + cs, _ := test.SeedTestData(t, pipelinetest.Data{ + TaskRuns: trs, + Namespaces: []*corev1.Namespace{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "ns", + }, + }, + }, + }) + + version := "v1alpha1" + tdc := testDynamic.Options{} + dynamic, err := tdc.Client( + cb.UnstructuredTR(trs[0], version), + ) + if err != nil { + t.Errorf("unable to create dynamic client: %v", err) + } + cs.Pipeline.Resources = cb.APIResourceList(version, []string{"taskrun"}) + if err != nil { + fmt.Println(err) + } + p := &test.Params{Tekton: cs.Pipeline, Kube: cs.Kube, Dynamic: dynamic, Clock: clock} + taskrun := Command(p) + clock.Advance(10 * time.Minute) + actual, err := test.ExecuteCommand(taskrun, "desc", "-n", "ns") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + golden.Assert(t, actual, fmt.Sprintf("%s.golden", t.Name())) +} + func TestTaskRunDescribe_lastV1beta1(t *testing.T) { clock := clockwork.NewFakeClock() taskRuns := []*v1beta1.TaskRun{ @@ -1155,3 +1220,63 @@ func TestTaskRunDescribe_With_Workspaces(t *testing.T) { } golden.Assert(t, got, fmt.Sprintf("%s.golden", t.Name())) } + +func TestTaskRunDescribe_WithoutNameIfOnlyOneV1beta1TaskRunPresent(t *testing.T) { + clock := clockwork.NewFakeClock() + taskRuns := []*v1beta1.TaskRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns", + Name: "tr-1", + Labels: map[string]string{"tekton.dev/task": "task-1"}, + }, + Spec: v1beta1.TaskRunSpec{ + TaskRef: &v1beta1.TaskRef{ + Name: "task-1", + }, + }, + Status: v1beta1.TaskRunStatus{ + Status: duckv1beta1.Status{ + Conditions: duckv1beta1.Conditions{ + { + Status: corev1.ConditionFalse, + Reason: v1beta1.TaskRunReasonFailed.String(), + }, + }, + }, + TaskRunStatusFields: v1beta1.TaskRunStatusFields{ + StartTime: &metav1.Time{Time: clock.Now().Add(-10 * time.Minute)}, + CompletionTime: &metav1.Time{Time: clock.Now().Add(-5 * time.Minute)}, + }, + }, + }, + } + + namespaces := []*corev1.Namespace{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "ns", + }, + }, + } + + version := "v1beta1" + tdc := testDynamic.Options{} + dynamic, err := tdc.Client( + cb.UnstructuredV1beta1TR(taskRuns[0], version), + ) + if err != nil { + t.Errorf("unable to create dynamic client: %v", err) + } + + cs, _ := test.SeedV1beta1TestData(t, pipelinev1beta1test.Data{Namespaces: namespaces, TaskRuns: taskRuns}) + cs.Pipeline.Resources = cb.APIResourceList(version, []string{"taskrun"}) + p := &test.Params{Tekton: cs.Pipeline, Kube: cs.Kube, Dynamic: dynamic} + p.SetNamespace("ns") + taskrun := Command(p) + out, err := test.ExecuteCommand(taskrun, "desc") + if err != nil { + t.Errorf("Unexpected error: %v", err) + } + golden.Assert(t, out, fmt.Sprintf("%s.golden", t.Name())) +} diff --git a/pkg/cmd/taskrun/testdata/TestTaskRunDescribe_WithoutNameIfOnlyOneTaskRunPresent.golden b/pkg/cmd/taskrun/testdata/TestTaskRunDescribe_WithoutNameIfOnlyOneTaskRunPresent.golden new file mode 100644 index 000000000..4a142392d --- /dev/null +++ b/pkg/cmd/taskrun/testdata/TestTaskRunDescribe_WithoutNameIfOnlyOneTaskRunPresent.golden @@ -0,0 +1,38 @@ +Name: tr-1 +Namespace: ns +Task Ref: t1 +Labels: + tekton.dev/task=t1 + +Status + +STARTED DURATION STATUS +20 minutes ago 5 minutes Succeeded + +Input Resources + + No input resources + +Output Resources + + No output resources + +Params + + No params + +Results + + No results + +Workspaces + + No workspaces + +Steps + +No steps + +Sidecars + +No sidecars diff --git a/pkg/cmd/taskrun/testdata/TestTaskRunDescribe_WithoutNameIfOnlyOneV1beta1TaskRunPresent.golden b/pkg/cmd/taskrun/testdata/TestTaskRunDescribe_WithoutNameIfOnlyOneV1beta1TaskRunPresent.golden new file mode 100644 index 000000000..3557c8f79 --- /dev/null +++ b/pkg/cmd/taskrun/testdata/TestTaskRunDescribe_WithoutNameIfOnlyOneV1beta1TaskRunPresent.golden @@ -0,0 +1,38 @@ +Name: tr-1 +Namespace: ns +Task Ref: task-1 +Labels: + tekton.dev/task=task-1 + +Status + +STARTED DURATION STATUS +10 minutes ago 5 minutes Failed + +Input Resources + + No input resources + +Output Resources + + No output resources + +Params + + No params + +Results + + No results + +Workspaces + + No workspaces + +Steps + +No steps + +Sidecars + +No sidecars