From 505b2cec0bc778828efa5b71914e024a8385be53 Mon Sep 17 00:00:00 2001 From: Sergiy Kulanov Date: Fri, 13 Oct 2023 15:47:19 +0300 Subject: [PATCH] refactor: Refactor common graph approach both for Pipeline/PipelineRun Add tests baseline Signed-off-by: Sergiy Kulanov --- go.mod | 5 +- go.sum | 1 + .../graphutil.go => common/graph.go} | 51 +++++-- pkg/cmd/common/graph_test.go | 140 ++++++++++++++++++ pkg/cmd/graphutil/graphutil_test.go | 128 ---------------- pkg/cmd/pipeline/fetch.go | 43 ++++++ pkg/cmd/pipeline/fetch_test.go | 55 +++++++ pkg/cmd/pipeline/graph.go | 39 +---- pkg/cmd/pipelinerun/fetch.go | 54 +++++++ pkg/cmd/pipelinerun/fetch_test.go | 86 +++++++++++ pkg/cmd/pipelinerun/graph.go | 43 +----- pkg/test/params.go | 97 ++++++++++++ sonar-project.properties | 2 +- 13 files changed, 528 insertions(+), 216 deletions(-) rename pkg/cmd/{graphutil/graphutil.go => common/graph.go} (54%) create mode 100644 pkg/cmd/common/graph_test.go delete mode 100644 pkg/cmd/graphutil/graphutil_test.go create mode 100644 pkg/cmd/pipeline/fetch.go create mode 100644 pkg/cmd/pipeline/fetch_test.go create mode 100644 pkg/cmd/pipelinerun/fetch.go create mode 100644 pkg/cmd/pipelinerun/fetch_test.go create mode 100644 pkg/test/params.go diff --git a/go.mod b/go.mod index 3ad4e6e..8b4440f 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,10 @@ module github.com/sergk/tkn-graph go 1.21.1 require ( + github.com/jonboulle/clockwork v0.4.0 github.com/stretchr/testify v1.8.4 github.com/tektoncd/cli v0.32.0 + k8s.io/client-go v0.28.2 ) require ( @@ -35,7 +37,6 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jonboulle/clockwork v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -51,6 +52,7 @@ require ( github.com/prometheus/procfs v0.10.1 // indirect github.com/prometheus/statsd_exporter v0.21.0 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect + github.com/stretchr/objx v0.5.0 // indirect github.com/tektoncd/triggers v0.25.0 // indirect go.opencensus.io v0.24.0 // indirect go.uber.org/atomic v1.11.0 // indirect @@ -67,7 +69,6 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.28.2 // indirect k8s.io/apiextensions-apiserver v0.26.5 // indirect - k8s.io/client-go v0.28.2 // indirect k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect knative.dev/pkg v0.0.0-20230718152110-aef227e72ead // indirect ) diff --git a/go.sum b/go.sum index e771b50..3202781 100644 --- a/go.sum +++ b/go.sum @@ -351,6 +351,7 @@ github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/pkg/cmd/graphutil/graphutil.go b/pkg/cmd/common/graph.go similarity index 54% rename from pkg/cmd/graphutil/graphutil.go rename to pkg/cmd/common/graph.go index c929562..66214c7 100644 --- a/pkg/cmd/graphutil/graphutil.go +++ b/pkg/cmd/common/graph.go @@ -1,4 +1,4 @@ -package graphutil +package common import ( "fmt" @@ -11,20 +11,29 @@ import ( v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" ) +// GraphOptions holds the options for the graph command +// OutputFormat: dot, puml, mmd +// OutputDir: the directory to save the output files. Otherwise, the output is printed to the screen +// WithTaskRef: Include TaskRefName information in the output type GraphOptions struct { OutputFormat string OutputDir string WithTaskRef bool } -type GraphData struct { - Name string - Spec v1.PipelineSpec +// Holds the Pipeline name and the Pipeline itself, in case of PipelineRun it holds the PipelineRun name and the Pipeline +type Pipeline struct { + Name string + TektonPipeline v1.Pipeline } -type GraphFetcher func(cs *cli.Clients, name []string, namespace string) ([]GraphData, error) +// GraphFetcher is an interface that defines the methods to fetch the Pipeline +type GraphFetcher interface { + GetByName(cs *cli.Clients, name, namespace string) (*Pipeline, error) + GetAll(cs *cli.Clients, namespace string) ([]Pipeline, error) +} -func NewGraphCommand(p cli.Params, runE func(cmd *cobra.Command, args []string, p cli.Params, opts *GraphOptions) error) *cobra.Command { +func CreateGraphCommand(p cli.Params, fetcher GraphFetcher) *cobra.Command { opts := &GraphOptions{} // Define the root command c := &cobra.Command{ @@ -46,7 +55,7 @@ func NewGraphCommand(p cli.Params, runE func(cmd *cobra.Command, args []string, return prerun.ValidateGraphPreRunE(opts.OutputFormat) }, RunE: func(cmd *cobra.Command, args []string) error { - return runE(cmd, args, p, opts) + return RunGraphCommand(p, opts, fetcher, args) }, } @@ -61,23 +70,35 @@ func NewGraphCommand(p cli.Params, runE func(cmd *cobra.Command, args []string, return c } -func RunGraphCommand(args []string, p cli.Params, opts *GraphOptions, fetcher GraphFetcher) error { +func RunGraphCommand(p cli.Params, opts *GraphOptions, fetcher GraphFetcher, args []string) error { cs, err := p.Clients() if err != nil { return err } var graphs []*taskgraph.TaskGraph - var data []GraphData + var pipelines []Pipeline - data, err = fetcher(cs, args, p.Namespace()) - if err != nil { - return fmt.Errorf("failed to fetch data: %w", err) + switch len(args) { + case 1: + var pipeline *Pipeline + pipeline, err = fetcher.GetByName(cs, args[0], p.Namespace()) + if err != nil { + return fmt.Errorf("failed to run GetByName: %w", err) + } + pipelines = append(pipelines, *pipeline) + case 0: + pipelines, err = fetcher.GetAll(cs, p.Namespace()) + if err != nil { + return fmt.Errorf("failed to run GetAll: %w", err) + } + default: + return fmt.Errorf("too many arguments. Provide either no arguments to get all Pipelines or a single Pipeline name") } - for i := range data { - graph := taskgraph.BuildTaskGraph(data[i].Spec.Tasks) - graph.PipelineName = data[i].Name + for i := range pipelines { + graph := taskgraph.BuildTaskGraph(pipelines[i].TektonPipeline.Spec.Tasks) + graph.PipelineName = pipelines[i].Name graphs = append(graphs, graph) } diff --git a/pkg/cmd/common/graph_test.go b/pkg/cmd/common/graph_test.go new file mode 100644 index 0000000..b6529a1 --- /dev/null +++ b/pkg/cmd/common/graph_test.go @@ -0,0 +1,140 @@ +// pkg/cmd/common/graph_test.go +package common + +import ( + "testing" + + "github.com/sergk/tkn-graph/pkg/test" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/tektoncd/cli/pkg/cli" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" +) + +// MockGraphFetcher is a mock implementation of the GraphFetcher interface +type MockGraphFetcher struct { + mock.Mock +} + +func (m *MockGraphFetcher) GetByName(cs *cli.Clients, name, namespace string) (*Pipeline, error) { + args := m.Called(cs, name, namespace) + return args.Get(0).(*Pipeline), args.Error(1) +} + +func (m *MockGraphFetcher) GetAll(cs *cli.Clients, namespace string) ([]Pipeline, error) { + args := m.Called(cs, namespace) + return args.Get(0).([]Pipeline), args.Error(1) +} + +// TestCreateGraphCommand tests the CreateGraphCommand function +func TestCreateGraphCommand(t *testing.T) { + p := &test.Params{} + fetcher := new(MockGraphFetcher) + + cmd := CreateGraphCommand(p, fetcher) + + assert.Equal(t, "graph", cmd.Use) + assert.Equal(t, []string{"g"}, cmd.Aliases) + assert.Equal(t, "Generates Graph", cmd.Short) + assert.Equal(t, map[string]string{"commandType": "main"}, cmd.Annotations) + assert.True(t, cmd.SilenceUsage) +} + +// TestRunGraphCommand tests the RunGraphCommand function +func TestRunGraphCommand(t *testing.T) { + p := &test.Params{} + p.SetNamespace("default") + + fetcher := new(MockGraphFetcher) + opts := &GraphOptions{ + OutputFormat: "dot", + } + args := []string{"pipeline1"} + + fetcher.On("GetByName", mock.Anything, "pipeline1", "default").Return(&Pipeline{ + Name: "pipeline1", + TektonPipeline: v1.Pipeline{ + Spec: v1.PipelineSpec{ + Tasks: []v1.PipelineTask{ + { + Name: "task1", + TaskRef: &v1.TaskRef{ + Name: "task1", + }, + }, + }, + }, + }, + }, nil) + + err := RunGraphCommand(p, opts, fetcher, args) + + assert.NoError(t, err) + fetcher.AssertExpectations(t) +} + +func TestRunGraphCommandWithGetAll(t *testing.T) { + p := &test.Params{} + p.SetNamespace("default") + + fetcher := new(MockGraphFetcher) + opts := &GraphOptions{ + OutputFormat: "dot", + OutputDir: "/tmp", + } + args := []string{} // Empty args to trigger GetAll + + fetcher.On("GetAll", mock.Anything, "default").Return([]Pipeline{ + { + Name: "pipeline1", + TektonPipeline: v1.Pipeline{ + Spec: v1.PipelineSpec{ + Tasks: []v1.PipelineTask{ + { + Name: "task1", + TaskRef: &v1.TaskRef{ + Name: "task1", + }, + }, + }, + }, + }, + }, + { + Name: "pipeline2", + TektonPipeline: v1.Pipeline{ + Spec: v1.PipelineSpec{ + Tasks: []v1.PipelineTask{ + { + Name: "task2", + TaskRef: &v1.TaskRef{ + Name: "task2", + }, + }, + }, + }, + }, + }, + }, nil) + + err := RunGraphCommand(p, opts, fetcher, args) + + assert.NoError(t, err) + fetcher.AssertExpectations(t) +} + +func TestRunGraphCommandWithTooManyArgs(t *testing.T) { + p := &test.Params{} + p.SetNamespace("default") + + fetcher := new(MockGraphFetcher) + opts := &GraphOptions{ + OutputFormat: "dot", + } + args := []string{"pipeline1", "pipeline2"} // Two arguments to trigger an error + + err := RunGraphCommand(p, opts, fetcher, args) + + assert.Error(t, err) + assert.Equal(t, "too many arguments. Provide either no arguments to get all Pipelines or a single Pipeline name", err.Error()) +} diff --git a/pkg/cmd/graphutil/graphutil_test.go b/pkg/cmd/graphutil/graphutil_test.go deleted file mode 100644 index a8ee877..0000000 --- a/pkg/cmd/graphutil/graphutil_test.go +++ /dev/null @@ -1,128 +0,0 @@ -package graphutil_test - -import ( - "errors" - "testing" - - "github.com/sergk/tkn-graph/pkg/cmd/graphutil" - "github.com/spf13/cobra" - "github.com/tektoncd/cli/pkg/cli" - v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" -) - -func TestNewGraphCommand(t *testing.T) { - p := &cli.TektonParams{} - runE := func(cmd *cobra.Command, args []string, p cli.Params, opts *graphutil.GraphOptions) error { - return nil - } - - cmd := graphutil.NewGraphCommand(p, runE) - - if cmd.Use != "graph" { - t.Errorf("Expected command use to be 'graph', got '%s'", cmd.Use) - } -} - -func TestRunGraphCommand(t *testing.T) { - tests := []struct { - name string - outputFormat string - outputDir string - expectingError bool - }{ - { - name: "valid output format", - outputFormat: "dot", - outputDir: "", - expectingError: false, - }, - { - name: "valid output format", - outputFormat: "mmd", - outputDir: "/tmp", - expectingError: false, - }, - { - name: "invalid output format for print to screen", - outputFormat: "not supported", - outputDir: "/tmp", - expectingError: true, - }, - { - name: "invalid output format for output dir", - outputFormat: "not supported", - outputDir: "", - expectingError: true, - }, - } - - for _, tt := range tests { - tt := tt - t.Run(tt.name, func(t *testing.T) { - t.Parallel() // Run this test in parallel - // Mock the GraphFetcher and cli.Params... - fetcher := func(cs *cli.Clients, name []string, namespace string) ([]graphutil.GraphData, error) { - // Return some mock data... - return []graphutil.GraphData{ - { - Name: "test", - Spec: v1.PipelineSpec{ - Tasks: []v1.PipelineTask{ - { - Name: "task1", - TaskRef: &v1.TaskRef{Name: "task1"}, - }, - { - Name: "task2", - TaskRef: &v1.TaskRef{Name: "task2"}, - }, - }, - }, - }, - }, nil - } - - p := &cli.TektonParams{} - p.SetNamespace("default") - - opts := &graphutil.GraphOptions{ - OutputFormat: tt.outputFormat, - OutputDir: tt.outputDir, - WithTaskRef: false, - } - - err := graphutil.RunGraphCommand([]string{}, p, opts, fetcher) - - if tt.expectingError && err == nil { - t.Errorf("Expected an error, got nil") - } - - if !tt.expectingError && err != nil { - t.Errorf("Expected no error, got '%s'", err) - } - }) - } -} - -func TestRunGraphCommand_ErrorFetcher(t *testing.T) { - // Mock the GraphFetcher to return an error - fetcher := func(cs *cli.Clients, name []string, namespace string) ([]graphutil.GraphData, error) { - // Return an error - return nil, errors.New("fetcher error") - } - - p := &cli.TektonParams{} - p.SetNamespace("default") - - opts := &graphutil.GraphOptions{ - OutputFormat: "dot", - OutputDir: "", - WithTaskRef: false, - } - - err := graphutil.RunGraphCommand([]string{}, p, opts, fetcher) - - if err == nil { - t.Errorf("Expected an error, got nil") - } -} diff --git a/pkg/cmd/pipeline/fetch.go b/pkg/cmd/pipeline/fetch.go new file mode 100644 index 0000000..37e7191 --- /dev/null +++ b/pkg/cmd/pipeline/fetch.go @@ -0,0 +1,43 @@ +package pipeline + +import ( + "fmt" + + common "github.com/sergk/tkn-graph/pkg/cmd/common" + "github.com/tektoncd/cli/pkg/cli" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" +) + +type PipelineFetcher struct { + GetPipelineByNameFunc func(cs *cli.Clients, name, namespace string) (*v1.Pipeline, error) + GetAllPipelinesFunc func(cs *cli.Clients, namespace string) ([]v1.Pipeline, error) +} + +func (f *PipelineFetcher) GetByName(cs *cli.Clients, name, namespace string) (*common.Pipeline, error) { + p, err := f.GetPipelineByNameFunc(cs, name, namespace) + if err != nil { + return nil, fmt.Errorf("failed to get Pipeline by name: %w", err) + } + + return &common.Pipeline{ + Name: name, + TektonPipeline: *p, + }, nil +} + +func (f *PipelineFetcher) GetAll(cs *cli.Clients, namespace string) ([]common.Pipeline, error) { + ps, err := f.GetAllPipelinesFunc(cs, namespace) + if err != nil { + return nil, fmt.Errorf("failed to get all Pipelines: %w", err) + } + + var cp []common.Pipeline + for i := range ps { + cp = append(cp, common.Pipeline{ + Name: ps[i].Name, + TektonPipeline: ps[i], + }) + } + + return cp, nil +} diff --git a/pkg/cmd/pipeline/fetch_test.go b/pkg/cmd/pipeline/fetch_test.go new file mode 100644 index 0000000..6fe5710 --- /dev/null +++ b/pkg/cmd/pipeline/fetch_test.go @@ -0,0 +1,55 @@ +package pipeline + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tektoncd/cli/pkg/cli" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestGetByName(t *testing.T) { + fetcher := &PipelineFetcher{ + GetPipelineByNameFunc: func(cs *cli.Clients, name, namespace string) (*v1.Pipeline, error) { + // Return a dummy pipeline + return &v1.Pipeline{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + }, nil + }, + } + + p, err := fetcher.GetByName(nil, "pipeline1", "default") + + assert.NoError(t, err) + assert.Equal(t, "pipeline1", p.Name) +} + +func TestGetAll(t *testing.T) { + fetcher := &PipelineFetcher{ + GetAllPipelinesFunc: func(cs *cli.Clients, namespace string) ([]v1.Pipeline, error) { + // Return a slice of dummy pipelines + return []v1.Pipeline{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "pipeline1", + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "pipeline2", + }, + }, + }, nil + }, + } + + ps, err := fetcher.GetAll(nil, "default") + + assert.NoError(t, err) + assert.Equal(t, 2, len(ps)) + assert.Equal(t, "pipeline1", ps[0].Name) + assert.Equal(t, "pipeline2", ps[1].Name) +} diff --git a/pkg/cmd/pipeline/graph.go b/pkg/cmd/pipeline/graph.go index c4ae04a..6818923 100644 --- a/pkg/cmd/pipeline/graph.go +++ b/pkg/cmd/pipeline/graph.go @@ -1,44 +1,15 @@ package pipeline import ( - "fmt" - - "github.com/sergk/tkn-graph/pkg/cmd/graphutil" - pipelinepkg "github.com/sergk/tkn-graph/pkg/pipeline" + common "github.com/sergk/tkn-graph/pkg/cmd/common" + "github.com/sergk/tkn-graph/pkg/pipeline" "github.com/spf13/cobra" "github.com/tektoncd/cli/pkg/cli" - v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" ) func graphCommand(p cli.Params) *cobra.Command { - return graphutil.NewGraphCommand(p, func(cmd *cobra.Command, args []string, p cli.Params, opts *graphutil.GraphOptions) error { - return graphutil.RunGraphCommand(args, p, opts, func(cs *cli.Clients, args []string, namespace string) ([]graphutil.GraphData, error) { - var pipelines []v1.Pipeline - var err error - - switch len(args) { - case 1: - var pipeline *v1.Pipeline - pipeline, err = pipelinepkg.GetPipelineByName(cs, args[0], namespace) - if err != nil { - return nil, fmt.Errorf("failed to run GetPipelineByName: %w", err) - } - pipelines = append(pipelines, *pipeline) - case 0: - pipelines, err = pipelinepkg.GetAllPipelines(cs, namespace) - if err != nil { - return nil, fmt.Errorf("failed to run GetAllPipelines: %w", err) - } - default: - return nil, fmt.Errorf("too many arguments. Provide either no arguments to get all Pipelines or a single Pipeline name") - } - - var data []graphutil.GraphData - for i := range pipelines { - data = append(data, graphutil.GraphData{Name: pipelines[i].Name, Spec: pipelines[i].Spec}) - } - - return data, nil - }) + return common.CreateGraphCommand(p, &PipelineFetcher{ + GetPipelineByNameFunc: pipeline.GetPipelineByName, + GetAllPipelinesFunc: pipeline.GetAllPipelines, }) } diff --git a/pkg/cmd/pipelinerun/fetch.go b/pkg/cmd/pipelinerun/fetch.go new file mode 100644 index 0000000..73660c4 --- /dev/null +++ b/pkg/cmd/pipelinerun/fetch.go @@ -0,0 +1,54 @@ +package pipelinerun + +import ( + "fmt" + + common "github.com/sergk/tkn-graph/pkg/cmd/common" + "github.com/tektoncd/cli/pkg/cli" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" +) + +type PipelineRunFetcher struct { + GetPipelineRunByNameFunc func(cs *cli.Clients, name, namespace string) (*v1.PipelineRun, error) + GetAllPipelineRunsFunc func(cs *cli.Clients, namespace string) ([]v1.PipelineRun, error) + GetPipelineByNameFunc func(cs *cli.Clients, name, namespace string) (*v1.Pipeline, error) +} + +func (f *PipelineRunFetcher) GetByName(cs *cli.Clients, name, namespace string) (*common.Pipeline, error) { + pr, err := f.GetPipelineRunByNameFunc(cs, name, namespace) + if err != nil { + return nil, fmt.Errorf("failed to get PipelineRun by name: %w", err) + } + + // Fetch the Pipeline that the PipelineRun is based on + p, err := f.GetPipelineByNameFunc(cs, pr.Spec.PipelineRef.Name, namespace) + if err != nil { + return nil, fmt.Errorf("failed to get Pipeline by name: %w", err) + } + + return &common.Pipeline{ + Name: name, + TektonPipeline: *p, + }, nil +} + +func (f *PipelineRunFetcher) GetAll(cs *cli.Clients, namespace string) ([]common.Pipeline, error) { + prs, err := f.GetAllPipelineRunsFunc(cs, namespace) + if err != nil { + return nil, fmt.Errorf("failed to get all PipelineRuns: %w", err) + } + + var cp []common.Pipeline + for i := range prs { + pipeline, err := f.GetPipelineByNameFunc(cs, prs[i].Spec.PipelineRef.Name, namespace) + if err != nil { + return nil, fmt.Errorf("failed to get Pipeline by name: %w", err) + } + cp = append(cp, common.Pipeline{ + Name: prs[i].Name, + TektonPipeline: *pipeline, + }) + } + + return cp, nil +} diff --git a/pkg/cmd/pipelinerun/fetch_test.go b/pkg/cmd/pipelinerun/fetch_test.go new file mode 100644 index 0000000..a356b28 --- /dev/null +++ b/pkg/cmd/pipelinerun/fetch_test.go @@ -0,0 +1,86 @@ +package pipelinerun + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tektoncd/cli/pkg/cli" + v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestGetByName(t *testing.T) { + fetcher := &PipelineRunFetcher{ + GetPipelineRunByNameFunc: func(cs *cli.Clients, name, namespace string) (*v1.PipelineRun, error) { + // Return a dummy pipeline run + return &v1.PipelineRun{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Spec: v1.PipelineRunSpec{ + PipelineRef: &v1.PipelineRef{ + Name: "pipeline1", + }, + }, + }, nil + }, + GetPipelineByNameFunc: func(cs *cli.Clients, name, namespace string) (*v1.Pipeline, error) { + // Return a dummy pipeline + return &v1.Pipeline{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + }, nil + }, + } + + p, err := fetcher.GetByName(nil, "pipelinerun1", "default") + + assert.NoError(t, err) + assert.Equal(t, "pipelinerun1", p.Name) +} + +func TestGetAll(t *testing.T) { + fetcher := &PipelineRunFetcher{ + GetAllPipelineRunsFunc: func(cs *cli.Clients, namespace string) ([]v1.PipelineRun, error) { + // Return a slice of dummy pipeline runs + return []v1.PipelineRun{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "pipelinerun1", + }, + Spec: v1.PipelineRunSpec{ + PipelineRef: &v1.PipelineRef{ + Name: "pipeline1", + }, + }, + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "pipelinerun2", + }, + Spec: v1.PipelineRunSpec{ + PipelineRef: &v1.PipelineRef{ + Name: "pipeline2", + }, + }, + }, + }, nil + }, + GetPipelineByNameFunc: func(cs *cli.Clients, name, namespace string) (*v1.Pipeline, error) { + // Return a dummy pipeline + return &v1.Pipeline{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + }, nil + }, + } + + ps, err := fetcher.GetAll(nil, "default") + + assert.NoError(t, err) + assert.Equal(t, 2, len(ps)) + assert.Equal(t, "pipelinerun1", ps[0].Name) + assert.Equal(t, "pipelinerun2", ps[1].Name) +} diff --git a/pkg/cmd/pipelinerun/graph.go b/pkg/cmd/pipelinerun/graph.go index e8f59d0..272627b 100644 --- a/pkg/cmd/pipelinerun/graph.go +++ b/pkg/cmd/pipelinerun/graph.go @@ -1,46 +1,17 @@ package pipelinerun import ( - "fmt" - - "github.com/sergk/tkn-graph/pkg/cmd/graphutil" - pipelinerunpkg "github.com/sergk/tkn-graph/pkg/pipelinerun" + common "github.com/sergk/tkn-graph/pkg/cmd/common" + "github.com/sergk/tkn-graph/pkg/pipeline" + "github.com/sergk/tkn-graph/pkg/pipelinerun" "github.com/spf13/cobra" "github.com/tektoncd/cli/pkg/cli" - v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" ) func graphCommand(p cli.Params) *cobra.Command { - return graphutil.NewGraphCommand(p, func(cmd *cobra.Command, args []string, p cli.Params, opts *graphutil.GraphOptions) error { - return graphutil.RunGraphCommand(args, p, opts, func(cs *cli.Clients, args []string, namespace string) ([]graphutil.GraphData, error) { - var pipelineruns []v1.PipelineRun - var err error - - switch len(args) { - case 1: - var pipelinerun *v1.PipelineRun - pipelinerun, err = pipelinerunpkg.GetPipelineRunsByName(cs, args[0], namespace) - if err != nil { - return nil, fmt.Errorf("failed to run GetPipelineRunByName: %w", err) - } - pipelineruns = append(pipelineruns, *pipelinerun) - case 0: - pipelineruns, err = pipelinerunpkg.GetAllPipelineRuns(cs, namespace) - if err != nil { - return nil, fmt.Errorf("failed to run GetAllPipelineRuns: %w", err) - } - default: - return nil, fmt.Errorf("too many arguments. Provide either no arguments to get all PipelineRuns or a single PipelineRun name") - } - - var data []graphutil.GraphData - for i := range pipelineruns { - if pipelineruns[i].Status.PipelineSpec != nil { - data = append(data, graphutil.GraphData{Name: pipelineruns[i].Name, Spec: *pipelineruns[i].Status.PipelineSpec}) - } - } - - return data, nil - }) + return common.CreateGraphCommand(p, &PipelineRunFetcher{ + GetPipelineRunByNameFunc: pipelinerun.GetPipelineRunsByName, + GetAllPipelineRunsFunc: pipelinerun.GetAllPipelineRuns, + GetPipelineByNameFunc: pipeline.GetPipelineByName, }) } diff --git a/pkg/test/params.go b/pkg/test/params.go new file mode 100644 index 0000000..68378bf --- /dev/null +++ b/pkg/test/params.go @@ -0,0 +1,97 @@ +// 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 test + +import ( + "time" + + "github.com/jonboulle/clockwork" + "github.com/tektoncd/cli/pkg/cli" + "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" + k8s "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" +) + +type Params struct { + ns, kubeCfg, kubeCtx string + Tekton versioned.Interface + Kube k8s.Interface + Clock clockwork.Clock + Cls *cli.Clients +} + +func (p *Params) SetNamespace(ns string) { + p.ns = ns +} +func (p *Params) Namespace() string { + return p.ns +} + +func (p *Params) SetNoColour(_ bool) { +} + +func (p *Params) SetKubeConfigPath(path string) { + p.kubeCfg = path +} + +func (p *Params) SetKubeContext(context string) { + p.kubeCtx = context +} + +func (p *Params) KubeConfigPath() string { + return p.kubeCfg +} + +func (p *Params) tektonClient() (versioned.Interface, error) { + return p.Tekton, nil +} + +func (p *Params) KubeClient() (k8s.Interface, error) { + return p.Kube, nil +} + +func (p *Params) Clients(_ ...*rest.Config) (*cli.Clients, error) { + if p.Cls != nil { + return p.Cls, nil + } + + tekton, err := p.tektonClient() + if err != nil { + return nil, err + } + + kube, err := p.KubeClient() + if err != nil { + return nil, err + } + + p.Cls = &cli.Clients{ + Tekton: tekton, + Kube: kube, + } + + return p.Cls, nil +} + +func (p *Params) Time() clockwork.Clock { + if p.Clock == nil { + p.Clock = FakeClock() + } + return p.Clock +} + +func FakeClock() clockwork.FakeClock { + return clockwork.NewFakeClockAt(time.Date(1984, time.April, 4, 0, 0, 0, 0, time.UTC)) +} diff --git a/sonar-project.properties b/sonar-project.properties index 45268a4..94901a5 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -14,4 +14,4 @@ sonar.sourceEncoding=UTF-8 sonar.go.coverage.reportPaths=coverage.out sonar.test.inclusions=**/*_test.go -sonar.exclusions=**/.chglog/**,**/cmd/graph/main.go +sonar.exclusions=**/.chglog/**,**/cmd/graph/main.go,**/pkg/test/**