Skip to content

Commit

Permalink
refactor: First refactor iteration
Browse files Browse the repository at this point in the history
Move logic out of main.go

Signed-off-by: Sergiy Kulanov <[email protected]>
  • Loading branch information
SergK committed Oct 4, 2023
1 parent 11cf9a2 commit 1a7bf98
Show file tree
Hide file tree
Showing 5 changed files with 249 additions and 46 deletions.
25 changes: 22 additions & 3 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ builds:
- linux
- windows
- darwin
goarch:
- amd64
- arm64
- s390x
- ppc64le
mod_timestamp: '{{ .CommitTimestamp }}'
flags:
# trims path
Expand All @@ -22,8 +27,21 @@ builds:
# only needed if you actually use those things in your main package, otherwise can be ignored.
- -s -w -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{ .CommitDate }}

archives:
- name_template: >-
{{- .Binary }}_
{{- .Version }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else if eq .Arch "arm64" }}aarch64
{{- else }}{{ .Arch }}{{ end }}
format_overrides:
- goos: windows
format: zip

checksum:
name_template: "{{ .ProjectName }}_checksums.txt"
name_template: "checksums.txt"

snapshot:
name_template: "{{ incpatch .Version }}-snapshot"
Expand Down Expand Up @@ -53,9 +71,10 @@ changelog:
order: 999
filters:
exclude:
- '^docs:'
- '^test:'
- '^.*?Bump(\([[:word:]]+\))?.+$'
- '^.*?[Bot](\([[:word:]]+\))?.+$'
- Merge pull request
- Merge branch

release:
name_template: 'v{{ .Version }}'
Expand Down
59 changes: 16 additions & 43 deletions cmd/graph/main.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
package main

import (
"context"
"fmt"
"log"
"os"
"path/filepath"

"github.com/sergk/tkn-graph/pkg/client"
"github.com/sergk/tkn-graph/pkg/taskgraph"
"github.com/spf13/cobra"
"github.com/tektoncd/pipeline/pkg/client/clientset/versioned"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)

type Options struct {
Namespace string
ObjectKind string
TektonKind string
OutputFormat string
OutputDir string
WithTaskRef bool
Expand All @@ -28,81 +24,58 @@ func main() {

// Define the root command
rootCmd := &cobra.Command{
Use: "graph",
Use: "tkn-graph",
Short: "Generate a graph of a Tekton object",
Long: "graph is a command-line tool for generating graphs from Tekton kind: Pipelines and kind: PipelineRuns.",
Long: "tkn-graph is a command-line tool for generating graphs from Tekton kind: Pipelines and kind: PipelineRuns.",
Example: ` graph --namespace my-namespace --kind Pipeline --output-format dot
graph --namespace my-namespace --kind PipelineRun --output-format puml
graph --namespace my-namespace --kind Pipeline --output-format mmd --output-dir /tmp/output`,
Run: func(cmd *cobra.Command, args []string) {
// Create the Kubernetes client
config, err := rest.InClusterConfig()
if err != nil {
kubeconfig := os.Getenv("KUBECONFIG")
if kubeconfig == "" {
kubeconfig = clientcmd.RecommendedHomeFile
}
config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
log.Fatalf("Failed to get Kubernetes configuration: %v", err)
}
}
tektonClient, err := versioned.NewForConfig(config)
tektonClient, err := client.NewClient()
if err != nil {
log.Fatalf("Failed to create Tekton client: %v", err)
}

// Get the namespace to use
if options.Namespace == "" {
namespace, _, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
clientcmd.NewDefaultClientConfigLoadingRules(),
&clientcmd.ConfigOverrides{},
).Namespace()
namespace, err := tektonClient.GetNamespace()
if err != nil {
log.Fatalf("Failed to get namespace from kubeconfig: %v", err)
}
if namespace == "" {
namespace = "default"
log.Fatalf("Failed to get namespace: %v", err)
}
options.Namespace = namespace
}

// Build the list of task graphs
var graphs []*taskgraph.TaskGraph

switch options.ObjectKind {
switch options.TektonKind {
case "Pipeline":
pipelines, err := tektonClient.TektonV1().Pipelines(options.Namespace).List(context.TODO(), v1.ListOptions{})
pipelines, err := tektonClient.GetPipelines(options.Namespace)
if err != nil {
log.Fatalf("Failed to get Pipelines: %v", err)
}
if len(pipelines.Items) == 0 {
log.Fatalf("No Pipelines found in namespace %s", options.Namespace)
}
for i := range pipelines.Items {
pipeline := &pipelines.Items[i]
for i := range pipelines {
pipeline := &pipelines[i]
graph := taskgraph.BuildTaskGraph(pipeline.Spec.Tasks)
graph.PipelineName = pipeline.Name
graphs = append(graphs, graph)
}

case "PipelineRun":
pipelineRuns, err := tektonClient.TektonV1().PipelineRuns(options.Namespace).List(context.TODO(), v1.ListOptions{})
pipelineRuns, err := tektonClient.GetPipelineRuns(options.Namespace)
if err != nil {
log.Fatalf("Failed to get PipelineRuns: %v", err)
}
if len(pipelineRuns.Items) == 0 {
log.Fatalf("No PipelineRuns found in namespace %s", options.Namespace)
}
for i := range pipelineRuns.Items {
pipelineRun := &pipelineRuns.Items[i]
for i := range pipelineRuns {
pipelineRun := &pipelineRuns[i]
graph := taskgraph.BuildTaskGraph(pipelineRun.Status.PipelineSpec.Tasks)
graph.PipelineName = pipelineRun.Name
graphs = append(graphs, graph)
}

default:
log.Fatalf("Invalid kind type: %s", options.ObjectKind)
log.Fatalf("Invalid kind type: %s", options.TektonKind)
}

// Generate graph for each object
Expand Down Expand Up @@ -137,7 +110,7 @@ func main() {
rootCmd.Flags().StringVar(
&options.Namespace, "namespace", "", "the Kubernetes namespace to use. Will try to get namespace from KUBECONFIG if not specified then fallback to 'default'")
rootCmd.Flags().StringVar(
&options.ObjectKind, "kind", "Pipeline", "the kind of the Tekton object to parse (Pipeline or PipelineRun)")
&options.TektonKind, "kind", "Pipeline", "the kind of the Tekton object to parse (Pipeline or PipelineRun)")
rootCmd.Flags().StringVar(
&options.OutputFormat, "output-format", "dot", "the output format (dot - DOT, puml - PlantUML or mmd - Mermaid)")
rootCmd.Flags().StringVar(
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ require (
github.com/blendle/zapdriver v1.3.1 // indirect
github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
github.com/go-kit/log v0.2.0 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
Expand Down
76 changes: 76 additions & 0 deletions pkg/client/tekton.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package client

import (
"context"
"fmt"
"os"

v1pipeline "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
"github.com/tektoncd/pipeline/pkg/client/clientset/versioned"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
)

type Client struct {
tektonClient versioned.Interface
}

func NewClient() (*Client, error) {
// Create the Kubernetes client
config, err := rest.InClusterConfig()
if err != nil {
kubeconfig := os.Getenv("KUBECONFIG")
if kubeconfig == "" {
kubeconfig = clientcmd.RecommendedHomeFile
}
config, err = clientcmd.BuildConfigFromFlags("", kubeconfig)
if err != nil {
return nil, fmt.Errorf("failed to get Kubernetes configuration: %w", err)
}
}
tektonClient, err := versioned.NewForConfig(config)
if err != nil {
return nil, fmt.Errorf("failed to create Tekton client: %w", err)
}

return &Client{
tektonClient: tektonClient,
}, nil
}

func (c *Client) GetNamespace() (string, error) {
namespace, _, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
clientcmd.NewDefaultClientConfigLoadingRules(),
&clientcmd.ConfigOverrides{},
).Namespace()
if err != nil {
return "", fmt.Errorf("failed to get namespace from kubeconfig: %w", err)
}
if namespace == "" {
namespace = "default"
}
return namespace, nil
}

func (c *Client) GetPipelines(namespace string) ([]v1pipeline.Pipeline, error) {
pipelines, err := c.tektonClient.TektonV1().Pipelines(namespace).List(context.TODO(), v1.ListOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get Pipelines: %w", err)
}
if len(pipelines.Items) == 0 {
return nil, fmt.Errorf("no Pipelines found in namespace %s", namespace)
}
return pipelines.Items, nil
}

func (c *Client) GetPipelineRuns(namespace string) ([]v1pipeline.PipelineRun, error) {
pipelineRuns, err := c.tektonClient.TektonV1().PipelineRuns(namespace).List(context.TODO(), v1.ListOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get PipelineRuns: %w", err)
}
if len(pipelineRuns.Items) == 0 {
return nil, fmt.Errorf("no PipelineRuns found in namespace %s", namespace)
}
return pipelineRuns.Items, nil
}
134 changes: 134 additions & 0 deletions pkg/client/tekton_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package client

import (
"context"
"testing"

"github.com/stretchr/testify/assert"
v1pipeline "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
fakeclient "github.com/tektoncd/pipeline/pkg/client/clientset/versioned/fake"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
namespace = "test-namespace"
pipelineName = "test-pipeline"
)

func TestClient_GetPipelinesNoError(t *testing.T) {
// Create a fake Tekton clientset for testing
fakeClient := fakeclient.NewSimpleClientset()

expectedPipelines := []v1pipeline.Pipeline{
{
ObjectMeta: v1.ObjectMeta{
Name: "pipeline-1",
Namespace: namespace,
},
},
{
ObjectMeta: v1.ObjectMeta{
Name: "pipeline-2",
Namespace: namespace,
},
},
}

// Add the test pipelines to the fake clientset
for i := range expectedPipelines {
_, err := fakeClient.TektonV1().Pipelines(namespace).Create(context.TODO(), &expectedPipelines[i], v1.CreateOptions{})
if err != nil {
t.Fatalf("failed to create Pipeline: %v", err)
}
}

// Create a client instance with the fake clientset
client := &Client{
tektonClient: fakeClient,
}

pipelines, err := client.GetPipelines(namespace)
assert.NoError(t, err)
// assert number of pipelines
assert.Equal(t, 2, len(pipelines))
// Assert that the returned pipelines are the same as the expected pipelines
assert.Equal(t, expectedPipelines, pipelines)
}

func TestClient_GetPipelinesError(t *testing.T) {
// Create a fake Tekton clientset for testing
fakeClient := fakeclient.NewSimpleClientset()

// Create a client instance with the fake clientset
client := &Client{
tektonClient: fakeClient,
}

// Call the GetPipelines function
_, err := client.GetPipelines(namespace)

// Assert that error occurred
assert.Error(t, err)

// Assert that the error message is as expected
assert.Equal(t, "no Pipelines found in namespace test-namespace", err.Error())
}

func TestClient_GetPipelineRunsNoError(t *testing.T) {
// Create a fake Tekton clientset for testing
fakeClient := fakeclient.NewSimpleClientset()

expectedPipelineRuns := []v1pipeline.PipelineRun{
{
ObjectMeta: v1.ObjectMeta{
Name: "pipeline-1",
Namespace: namespace,
},
},
{
ObjectMeta: v1.ObjectMeta{
Name: "pipeline-2",
Namespace: namespace,
},
},
}

// Add the test pipelineruns to the fake clientset
for i := range expectedPipelineRuns {
_, err := fakeClient.TektonV1().PipelineRuns(namespace).Create(context.TODO(), &expectedPipelineRuns[i], v1.CreateOptions{})
if err != nil {
t.Fatalf("failed to create PipelineRun: %v", err)
}
}

// Create a client instance with the fake clientset
client := &Client{
tektonClient: fakeClient,
}

pipelineruns, err := client.GetPipelineRuns(namespace)
assert.NoError(t, err)
// assert number of pipelineruns
assert.Equal(t, 2, len(pipelineruns))
// Assert that the returned pipelineruns are the same as the expected pipelineruns
assert.Equal(t, expectedPipelineRuns, pipelineruns)
}

func TestClient_GetPipelineRunsError(t *testing.T) {
// Create a fake Tekton clientset for testing
fakeClient := fakeclient.NewSimpleClientset()

// Create a client instance with the fake clientset
client := &Client{
tektonClient: fakeClient,
}

// Call the GetPipelines function
_, err := client.GetPipelineRuns(namespace)

// Assert that error occurred
assert.Error(t, err)

// Assert that the error message is as expected
assert.Equal(t, "no PipelineRuns found in namespace test-namespace", err.Error())
}

0 comments on commit 1a7bf98

Please sign in to comment.