From 4d9fee8a7fc27bfa805f76ee51196ec28a629083 Mon Sep 17 00:00:00 2001 From: Pavlos Tzianos Date: Mon, 8 Apr 2024 09:59:18 +0100 Subject: [PATCH] Do not return error when remote file is fetched (Fixes #126) This commit moves some code away from the build command, that manages the loading of the base pipeline, task and the pipeline tasks into a separate piece of code that lives in the same directory as the Tekton backend. Some tests are also added to the codebase testing the correct returning of errors. Finally, it fixes the issue where the httpFileLoader returns an error wrapping a nil error even if the file has been fetched from the remote source correctly. Signed-off-by: Pavlos Tzianos --- cmd/draconctl/pipelines/build.go | 42 +---- go.mod | 7 +- go.sum | 3 - pkg/manifests/httpfileloader.go | 7 +- pkg/pipelines/{tekton.go => tektonV1Beta1.go} | 149 +++++++++++++----- pkg/pipelines/tektonV1Beta1_test.go | 95 +++++++++++ vendor/modules.txt | 2 - 7 files changed, 216 insertions(+), 89 deletions(-) rename pkg/pipelines/{tekton.go => tektonV1Beta1.go} (59%) create mode 100644 pkg/pipelines/tektonV1Beta1_test.go diff --git a/cmd/draconctl/pipelines/build.go b/cmd/draconctl/pipelines/build.go index 4d4ba8c9a..174c01771 100644 --- a/cmd/draconctl/pipelines/build.go +++ b/cmd/draconctl/pipelines/build.go @@ -6,12 +6,10 @@ import ( "path" "strings" - "github.com/ocurity/dracon/pkg/components" "github.com/ocurity/dracon/pkg/manifests" "github.com/ocurity/dracon/pkg/pipelines" "github.com/spf13/cobra" - tektonV1Beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" - "sigs.k8s.io/kustomize/api/types" + kustomizeType "sigs.k8s.io/kustomize/api/types" ) var buildSubCmd = &cobra.Command{ @@ -46,7 +44,7 @@ func buildPipeline(cmd *cobra.Command, args []string) error { } // Parse Pipeline kustomization - kustomization := &types.Kustomization{} + kustomization := &kustomizeType.Kustomization{} if err = kustomization.Unmarshal(fileContents); err != nil { return fmt.Errorf("%s: could not unmarshal YAML file: %w", kustomizationPath, err) } @@ -61,40 +59,10 @@ func buildPipeline(cmd *cobra.Command, args []string) error { return fmt.Errorf("you need to specify the base pipeline and task in the resources field of the kustomization") } - var baseTaskPath string - var basePipeline *tektonV1Beta1.Pipeline - - if basePipeline, err = manifests.LoadTektonV1Beta1Pipeline(cmd.Context(), kustomizationDir, kustomization.Resources[0]); err != nil { - if basePipeline, err = manifests.LoadTektonV1Beta1Pipeline(cmd.Context(), kustomizationDir, kustomization.Resources[1]); err != nil { - return err - } - baseTaskPath = kustomization.Resources[0] - } else { - baseTaskPath = kustomization.Resources[1] - } - - baseTask, err := manifests.LoadTektonV1Beta1Task(cmd.Context(), kustomizationDir, baseTaskPath) + pipelineKustomization := &pipelines.Kustomization{Kustomization: kustomization, KustomizationDir: kustomizationDir} + basePipeline, taskList, err := pipelineKustomization.ResolveKustomizationResources(cmd.Context()) if err != nil { - return err - } - - if len(kustomization.Components) == 0 { - return fmt.Errorf("%s: no components are listed in the kustomization", kustomizationPath) - } - - taskList := []*tektonV1Beta1.Task{baseTask} - for _, pathOrURI := range kustomization.Components { - newTask, err := manifests.LoadTektonV1Beta1Task(cmd.Context(), kustomizationDir, pathOrURI) - if err != nil { - return err - } - - if err = components.ValidateTask(newTask); err != nil { - return fmt.Errorf("%s: invalid task found: %w", newTask.Name, err) - } - - newTask.Namespace = kustomization.Namespace - taskList = append(taskList, newTask) + return fmt.Errorf("%s: could not resolve base pipeline and tasks: %w", kustomizationDir, err) } k8sBackend, err := pipelines.NewTektonV1Beta1Backend(basePipeline, taskList, kustomization.NamePrefix, kustomization.NameSuffix) diff --git a/go.mod b/go.mod index 934314020..71a515b0f 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/avast/retry-go/v4 v4.3.3 github.com/aws/aws-sdk-go v1.17.7 github.com/elastic/go-elasticsearch/v8 v8.3.0 + github.com/go-errors/errors v1.4.2 github.com/golang-migrate/migrate/v4 v4.15.1 github.com/google/uuid v1.6.0 github.com/hairyhenderson/go-codeowners v0.4.0 @@ -56,7 +57,6 @@ require ( github.com/emicklei/go-restful/v3 v3.10.2 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/go-errors/errors v1.4.2 // indirect github.com/go-jose/go-jose/v3 v3.0.1 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect @@ -132,7 +132,6 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240102182953-50ed04b92917 // indirect google.golang.org/grpc v1.61.1 // indirect - google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/klog/v2 v2.110.1 // indirect @@ -167,11 +166,11 @@ require ( github.com/spf13/afero v1.8.2 // indirect github.com/spf13/cast v1.4.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 + github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.2.0 // indirect go.uber.org/atomic v1.10.0 // indirect golang.org/x/sys v0.16.0 // indirect golang.org/x/text v0.14.0 // indirect gopkg.in/ini.v1 v1.66.4 // indirect - gopkg.in/yaml.v3 v3.0.1 + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index d45022aea..46b38fcc5 100644 --- a/go.sum +++ b/go.sum @@ -1757,9 +1757,6 @@ google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzI google.golang.org/grpc v1.61.1 h1:kLAiWrZs7YeDM6MumDe7m3y4aM6wacLzM1Y/wiLP9XY= google.golang.org/grpc v1.61.1/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 h1:rNBFJjBCOgVr9pWD7rs/knKL4FRTKgpZmsRfV214zcA= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0/go.mod h1:Dk1tviKTvMCz5tvh7t+fh94dhmQVHuCt2OzJB3CTW9Y= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.33.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/pkg/manifests/httpfileloader.go b/pkg/manifests/httpfileloader.go index 2c79fbec3..40a62a5db 100644 --- a/pkg/manifests/httpfileloader.go +++ b/pkg/manifests/httpfileloader.go @@ -32,11 +32,14 @@ func (hfl *httpFileLoader) Load(ctx context.Context) (content []byte, err error) }() if resp.StatusCode < 200 || resp.StatusCode > 299 { - return nil, fmt.Errorf("%s: could not fetch file: %w", hfl.uri, err) + return nil, fmt.Errorf("%s: could not fetch file: %d", hfl.uri, resp.StatusCode) } content, err = io.ReadAll(resp.Body) - return content, fmt.Errorf("%s: could not read body of response: %w", hfl.uri, err) + if err != nil { + return nil, fmt.Errorf("%s: could not read body of response: %w", hfl.uri, err) + } + return content, nil } func (hfl *httpFileLoader) Path() string { diff --git a/pkg/pipelines/tekton.go b/pkg/pipelines/tektonV1Beta1.go similarity index 59% rename from pkg/pipelines/tekton.go rename to pkg/pipelines/tektonV1Beta1.go index d7939d043..abc44c994 100644 --- a/pkg/pipelines/tekton.go +++ b/pkg/pipelines/tektonV1Beta1.go @@ -1,20 +1,40 @@ package pipelines import ( - "errors" + "context" "fmt" "slices" + "github.com/go-errors/errors" "github.com/ocurity/dracon/pkg/components" - tektonV1Beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + "github.com/ocurity/dracon/pkg/manifests" + tektonV1Beta1API "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" corev1 "k8s.io/api/core/v1" + kustomizeTypes "sigs.k8s.io/kustomize/api/types" ) -var _ Backend[*tektonV1Beta1.Pipeline] = (*tektonV1Beta1Backend)(nil) +var _ Backend[*tektonV1Beta1API.Pipeline] = (*tektonV1Beta1Backend)(nil) + +var ( + // ErrNoComponentsInKustomization is returned when a kustomization has no components listed + ErrNoComponentsInKustomization = errors.New("no components listed in kustomization") + // ErrKustomizationMissingBaseResources is returned when a kustomization doesn't have 2 base resources + ErrKustomizationMissingBaseResources = errors.New("kustomization must have exactly 2 resources: a base pipeline and a base task") + // ErrNoTasks is returned when no tasks are provided to the Tekton backend + ErrNoTasks = errors.New("no tasks provided") +) + +// Kustomization is a wrapper around the `Kustomization` struct of kustomize that adds some fields +// and methods to the object for parsing. +type Kustomization struct { + *kustomizeTypes.Kustomization + // KustomizationDir is the relative path to the directory where the kustomization lives + KustomizationDir string +} type tektonV1Beta1Backend struct { - pipeline *tektonV1Beta1.Pipeline - tasks []*tektonV1Beta1.Task + pipeline *tektonV1Beta1API.Pipeline + tasks []*tektonV1Beta1API.Task prefix string suffix string } @@ -54,17 +74,17 @@ type tektonV1Beta1Backend struct { // addAnchorResult adds an `anchor` entry to the results section of a Task. This helps reduce the // amount of boilerplate needed to be written by a user to introduce a component. -func addAnchorResult(task *tektonV1Beta1.Task) { +func addAnchorResult(task *tektonV1Beta1API.Task) { if task.Labels[components.LabelKey] == components.Consumer.String() || task.Labels[components.LabelKey] == components.Base.String() { return } - task.Spec.Results = append(task.Spec.Results, tektonV1Beta1.TaskResult{ + task.Spec.Results = append(task.Spec.Results, tektonV1Beta1API.TaskResult{ Name: "anchor", Description: "An anchor to allow other tasks to depend on this task.", }) - task.Spec.Steps = append(task.Spec.Steps, tektonV1Beta1.Step{ + task.Spec.Steps = append(task.Spec.Steps, tektonV1Beta1API.Step{ Name: "anchor", Image: "docker.io/busybox", Script: "echo \"$(context.task.name)\" > \"$(results.anchor.path)\"", @@ -73,10 +93,10 @@ func addAnchorResult(task *tektonV1Beta1.Task) { // addAnchorParameter adds an `anchors` entry to the parameters of a Task. This entry will then be // filled in the pipeline with the anchors of the tasks that this task depends on. -func addAnchorParameter(task *tektonV1Beta1.Task) { +func addAnchorParameter(task *tektonV1Beta1API.Task) { componentType, err := components.ToComponentType(task.Labels[components.LabelKey]) if err != nil { - panic(fmt.Errorf("%s: %w", task.Name, err)) + panic(errors.Errorf("%s: %w", task.Name, err)) } if componentType < components.Producer { return @@ -88,21 +108,68 @@ func addAnchorParameter(task *tektonV1Beta1.Task) { } } - task.Spec.Params = append(task.Spec.Params, tektonV1Beta1.ParamSpec{ + task.Spec.Params = append(task.Spec.Params, tektonV1Beta1API.ParamSpec{ Name: "anchors", Description: "A list of tasks that this task depends on", Type: "array", - Default: &tektonV1Beta1.ParamValue{ - Type: tektonV1Beta1.ParamTypeArray, + Default: &tektonV1Beta1API.ParamValue{ + Type: tektonV1Beta1API.ParamTypeArray, }, }) } +// ResolveKustomizationResourceBases checks the resources section to find the base pipeline and +// task and fetches them wherever they are located. +func (pk *Kustomization) ResolveKustomizationResources(ctx context.Context) (*tektonV1Beta1API.Pipeline, []*tektonV1Beta1API.Task, error) { + var err error + var baseTaskPath string + var basePipeline *tektonV1Beta1API.Pipeline + + if len(pk.Resources) != 2 { + return nil, nil, errors.Errorf("%s: %w", pk.KustomizationDir, ErrKustomizationMissingBaseResources) + } + + if basePipeline, err = manifests.LoadTektonV1Beta1Pipeline(ctx, pk.KustomizationDir, pk.Resources[0]); err != nil { + if basePipeline, err = manifests.LoadTektonV1Beta1Pipeline(ctx, pk.KustomizationDir, pk.Resources[1]); err != nil { + return nil, nil, err + } + baseTaskPath = pk.Resources[0] + } else { + baseTaskPath = pk.Resources[1] + } + + baseTask, err := manifests.LoadTektonV1Beta1Task(ctx, pk.KustomizationDir, baseTaskPath) + if err != nil { + return nil, nil, errors.Errorf("%s: could not load task: %w", baseTaskPath, err) + } + + if len(pk.Components) == 0 { + return nil, nil, errors.Errorf("%s: %w", pk.KustomizationDir, ErrNoComponentsInKustomization) + } + + taskList := []*tektonV1Beta1API.Task{baseTask} + for _, pathOrURI := range pk.Components { + newTask, err := manifests.LoadTektonV1Beta1Task(ctx, pk.KustomizationDir, pathOrURI) + if err != nil { + return nil, nil, err + } + + if err = components.ValidateTask(newTask); err != nil { + return nil, nil, errors.Errorf("%s: invalid task found: %w", newTask.Name, err) + } + + newTask.Namespace = pk.Namespace + taskList = append(taskList, newTask) + } + + return basePipeline, taskList, nil +} + // NewTektonBackend returns an implementation of the Backend interface that will produce a Tekton // Pipeline object with all the configured tasks. -func NewTektonV1Beta1Backend(basePipeline *tektonV1Beta1.Pipeline, tasks []*tektonV1Beta1.Task, prefix, suffix string) (Backend[*tektonV1Beta1.Pipeline], error) { +func NewTektonV1Beta1Backend(basePipeline *tektonV1Beta1API.Pipeline, tasks []*tektonV1Beta1API.Task, prefix, suffix string) (Backend[*tektonV1Beta1API.Pipeline], error) { if len(tasks) == 0 { - return nil, errors.New("no tasks provided") + return nil, errors.Errorf("%w", ErrNoTasks) } tektonBackend := &tektonV1Beta1Backend{pipeline: basePipeline, tasks: tasks[:], prefix: prefix, suffix: suffix} @@ -114,7 +181,7 @@ func NewTektonV1Beta1Backend(basePipeline *tektonV1Beta1.Pipeline, tasks []*tekt } // Sort tasks based on their component type - slices.SortFunc(tektonBackend.tasks, func(a *tektonV1Beta1.Task, b *tektonV1Beta1.Task) int { + slices.SortFunc(tektonBackend.tasks, func(a *tektonV1Beta1API.Task, b *tektonV1Beta1API.Task) int { componentTypeA := components.MustGetComponentType(a.Labels[components.LabelKey]) componentTypeB := components.MustGetComponentType(b.Labels[components.LabelKey]) return int(componentTypeA) - int(componentTypeB) @@ -123,7 +190,7 @@ func NewTektonV1Beta1Backend(basePipeline *tektonV1Beta1.Pipeline, tasks []*tekt return tektonBackend, nil } -func (tb *tektonV1Beta1Backend) Generate() (*tektonV1Beta1.Pipeline, error) { +func (tb *tektonV1Beta1Backend) Generate() (*tektonV1Beta1API.Pipeline, error) { tb.pipeline.Name = tb.prefix + tb.pipeline.Name + tb.suffix pipelineWorkspaces := map[string]struct{}{} anchors := map[string][]string{} @@ -133,9 +200,9 @@ func (tb *tektonV1Beta1Backend) Generate() (*tektonV1Beta1.Pipeline, error) { anchors[componentType] = append(anchors[componentType], task.Name) // add task to pipeline tasks - pipelineTask := tektonV1Beta1.PipelineTask{ + pipelineTask := tektonV1Beta1API.PipelineTask{ Name: task.Name, - TaskRef: &tektonV1Beta1.TaskRef{ + TaskRef: &tektonV1Beta1API.TaskRef{ Name: task.Name, }, } @@ -144,13 +211,13 @@ func (tb *tektonV1Beta1Backend) Generate() (*tektonV1Beta1.Pipeline, error) { // make sure to propagate the `optional` field for _, ws := range task.Spec.Workspaces { if _, inserted := pipelineWorkspaces[ws.Name]; !inserted { - tb.pipeline.Spec.Workspaces = append(tb.pipeline.Spec.Workspaces, tektonV1Beta1.PipelineWorkspaceDeclaration{ + tb.pipeline.Spec.Workspaces = append(tb.pipeline.Spec.Workspaces, tektonV1Beta1API.PipelineWorkspaceDeclaration{ Name: ws.Name, Optional: ws.Optional, }) pipelineWorkspaces[ws.Name] = struct{}{} } - pipelineTask.Workspaces = append(pipelineTask.Workspaces, tektonV1Beta1.WorkspacePipelineTaskBinding{ + pipelineTask.Workspaces = append(pipelineTask.Workspaces, tektonV1Beta1API.WorkspacePipelineTaskBinding{ Name: ws.Name, Workspace: ws.Name, }) @@ -158,12 +225,12 @@ func (tb *tektonV1Beta1Backend) Generate() (*tektonV1Beta1.Pipeline, error) { // add the task's parameters to the pipeline's parameters and // reference them in the pipeline task parameters - pipelineTask.Params = make(tektonV1Beta1.Params, len(task.Spec.Params)) + pipelineTask.Params = make(tektonV1Beta1API.Params, len(task.Spec.Params)) for i, param := range task.Spec.Params { - pipelineTask.Params[i] = tektonV1Beta1.Param{ + pipelineTask.Params[i] = tektonV1Beta1API.Param{ Name: param.Name, - Value: tektonV1Beta1.ParamValue{}, + Value: tektonV1Beta1API.ParamValue{}, } if param.Name == "anchors" { @@ -176,17 +243,17 @@ func (tb *tektonV1Beta1Backend) Generate() (*tektonV1Beta1.Pipeline, error) { } pipelineTask.Params[i].Value.ArrayVal = values - pipelineTask.Params[i].Value.Type = tektonV1Beta1.ParamTypeArray + pipelineTask.Params[i].Value.Type = tektonV1Beta1API.ParamTypeArray } else { switch param.Type { - case tektonV1Beta1.ParamTypeArray: + case tektonV1Beta1API.ParamTypeArray: pipelineTask.Params[i].Value.Type = param.Type pipelineTask.Params[i].Value.ArrayVal = []string{fmt.Sprintf("$(params.%s)", param.Name)} - case tektonV1Beta1.ParamTypeString: + case tektonV1Beta1API.ParamTypeString: pipelineTask.Params[i].Value.Type = param.Type pipelineTask.Params[i].Value.StringVal = fmt.Sprintf("$(params.%s)", param.Name) case "": - return nil, fmt.Errorf("parameter %s of task %s has no type set", param.Name, task.Name) + return nil, errors.Errorf("parameter %s of task %s has no type set", param.Name, task.Name) } // ensure that the parameter type is always set @@ -195,7 +262,7 @@ func (tb *tektonV1Beta1Backend) Generate() (*tektonV1Beta1.Pipeline, error) { } // add parameter to pipeline parameters - tb.pipeline.Spec.Params = append(tb.pipeline.Spec.Params, tektonV1Beta1.ParamSpec{ + tb.pipeline.Spec.Params = append(tb.pipeline.Spec.Params, tektonV1Beta1API.ParamSpec{ Name: param.Name, Type: param.Type, Description: param.Description, @@ -219,43 +286,43 @@ func (tb *tektonV1Beta1Backend) Generate() (*tektonV1Beta1.Pipeline, error) { // addParamsAndEnvVars will add parameters and environment variables to the producer task that will // allow it to pick the start time, pipeline UUID and any tags that have been given as parameter to // the pipeline so that the issues discovered can be annotated with these values. -func addParamsAndEnvVars(pipelineTask *tektonV1Beta1.PipelineTask, anchors map[string][]string, task *tektonV1Beta1.Task) { - pipelineTask.Params = append(pipelineTask.Params, []tektonV1Beta1.Param{ +func addParamsAndEnvVars(pipelineTask *tektonV1Beta1API.PipelineTask, anchors map[string][]string, task *tektonV1Beta1API.Task) { + pipelineTask.Params = append(pipelineTask.Params, []tektonV1Beta1API.Param{ { Name: "dracon_scan_id", - Value: tektonV1Beta1.ParamValue{ - Type: tektonV1Beta1.ParamTypeString, + Value: tektonV1Beta1API.ParamValue{ + Type: tektonV1Beta1API.ParamTypeString, StringVal: fmt.Sprintf("$(tasks.%s.results.dracon-scan-id)", anchors[components.Base.String()][0]), }, }, { Name: "dracon_scan_start_time", - Value: tektonV1Beta1.ParamValue{ - Type: tektonV1Beta1.ParamTypeString, + Value: tektonV1Beta1API.ParamValue{ + Type: tektonV1Beta1API.ParamTypeString, StringVal: fmt.Sprintf("$(tasks.%s.results.dracon-scan-start-time)", anchors[components.Base.String()][0]), }, }, { Name: "dracon_scan_tags", - Value: tektonV1Beta1.ParamValue{ - Type: tektonV1Beta1.ParamTypeString, + Value: tektonV1Beta1API.ParamValue{ + Type: tektonV1Beta1API.ParamTypeString, StringVal: fmt.Sprintf("$(tasks.%s.results.dracon-scan-tags)", anchors[components.Base.String()][0]), }, }, }...) - task.Spec.Params = append(task.Spec.Params, tektonV1Beta1.ParamSpecs{ + task.Spec.Params = append(task.Spec.Params, tektonV1Beta1API.ParamSpecs{ { Name: "dracon_scan_id", - Type: tektonV1Beta1.ParamTypeString, + Type: tektonV1Beta1API.ParamTypeString, }, { Name: "dracon_scan_start_time", - Type: tektonV1Beta1.ParamTypeString, + Type: tektonV1Beta1API.ParamTypeString, }, { Name: "dracon_scan_tags", - Type: tektonV1Beta1.ParamTypeString, + Type: tektonV1Beta1API.ParamTypeString, }, }...) diff --git a/pkg/pipelines/tektonV1Beta1_test.go b/pkg/pipelines/tektonV1Beta1_test.go new file mode 100644 index 000000000..1ddf236cf --- /dev/null +++ b/pkg/pipelines/tektonV1Beta1_test.go @@ -0,0 +1,95 @@ +package pipelines + +import ( + "context" + "testing" + "time" + + "github.com/ocurity/dracon/pkg/manifests" + "github.com/stretchr/testify/require" + tektonV1Beta1API "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + kustomizeTypes "sigs.k8s.io/kustomize/api/types" +) + +func TestResolveKustomizationResourceBases(t *testing.T) { + testCtx, cancel := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel() + + baseTask, err := manifests.LoadTektonV1Beta1Task(testCtx, ".", "../../components/base") + require.NoError(t, err) + + gitTask, err := manifests.LoadTektonV1Beta1Task(testCtx, ".", "../../components/sources/git") + require.NoError(t, err) + + testCases := []struct { + name string + kustomization *Kustomization + expectedPipeline *tektonV1Beta1API.Pipeline + expectedTasks []*tektonV1Beta1API.Task + expectedErr error + }{ + { + name: "no base pipeline in the kustomization", + kustomization: &Kustomization{ + Kustomization: &kustomizeTypes.Kustomization{ + NameSuffix: "-cyberdyne-card-processing", + Resources: []string{}, + Components: []string{}, + }, + }, + expectedErr: ErrKustomizationMissingBaseResources, + }, + { + name: "no components in the kustomization", + kustomization: &Kustomization{ + Kustomization: &kustomizeTypes.Kustomization{ + NameSuffix: "-cyberdyne-card-processing", + Resources: []string{ + "https://raw.githubusercontent.com/ocurity/dracon/main/components/base/pipeline.yaml", + "https://raw.githubusercontent.com/ocurity/dracon/main/components/base/task.yaml", + }, + Components: []string{}, + }, + }, + expectedErr: ErrNoComponentsInKustomization, + }, + { + name: "success", + kustomization: &Kustomization{ + Kustomization: &kustomizeTypes.Kustomization{ + NameSuffix: "-cyberdyne-card-processing", + Resources: []string{ + "https://raw.githubusercontent.com/ocurity/dracon/main/components/base/pipeline.yaml", + "https://raw.githubusercontent.com/ocurity/dracon/main/components/base/task.yaml", + }, + Components: []string{"../../components/sources/git"}, + }, + }, + expectedPipeline: &tektonV1Beta1API.Pipeline{ + TypeMeta: metav1.TypeMeta{ + Kind: "Pipeline", + APIVersion: "tekton.dev/v1beta1", + }, + ObjectMeta: metav1.ObjectMeta{Name: "dracon"}, + Spec: tektonV1Beta1API.PipelineSpec{ + Tasks: []tektonV1Beta1API.PipelineTask{}, + Workspaces: []tektonV1Beta1API.PipelineWorkspaceDeclaration{}, + }, + }, + expectedTasks: []*tektonV1Beta1API.Task{baseTask, gitTask}, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + runCtx, cancel := context.WithCancel(testCtx) + defer cancel() + + basePipeline, taskList, err := testCase.kustomization.ResolveKustomizationResources(runCtx) + require.ErrorIs(t, err, testCase.expectedErr) + require.Equal(t, testCase.expectedPipeline, basePipeline) + require.EqualValues(t, testCase.expectedTasks, taskList) + }) + } +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 5bfe459c3..a688d3dd8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -885,8 +885,6 @@ google.golang.org/grpc/serviceconfig google.golang.org/grpc/stats google.golang.org/grpc/status google.golang.org/grpc/tap -# google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.3.0 -## explicit; go 1.17 # google.golang.org/protobuf v1.32.0 ## explicit; go 1.17 google.golang.org/protobuf/encoding/protojson