Skip to content

Commit

Permalink
refactor: Refactor common graph approach both for Pipeline/PipelineRun
Browse files Browse the repository at this point in the history
Add tests baseline

Signed-off-by: Sergiy Kulanov <[email protected]>
  • Loading branch information
SergK committed Oct 13, 2023
1 parent d47766c commit 505b2ce
Show file tree
Hide file tree
Showing 13 changed files with 528 additions and 216 deletions.
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
)
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
51 changes: 36 additions & 15 deletions pkg/cmd/graphutil/graphutil.go → pkg/cmd/common/graph.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package graphutil
package common

import (
"fmt"
Expand All @@ -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{
Expand All @@ -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)
},
}

Expand All @@ -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)
}

Expand Down
140 changes: 140 additions & 0 deletions pkg/cmd/common/graph_test.go
Original file line number Diff line number Diff line change
@@ -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())
}
Loading

0 comments on commit 505b2ce

Please sign in to comment.