From b711779c190090f691de477ba398d4a007145690 Mon Sep 17 00:00:00 2001 From: Jerome Ju Date: Thu, 8 Jun 2023 15:16:04 +0000 Subject: [PATCH] Fix bundle conversion for pipelineRef This commit fixes the bundle conversion for PipelineRef which should be converting to the bundle Resolver with a Pipeline kind. It also adds the conversion integration tests for the bundle logics to prevent such error. It extracts the setup for bundle and makes it into a helper function. --- .../v1beta1/pipelineref_conversion.go | 2 +- .../v1beta1/pipelinerun_conversion_test.go | 2 +- test/conversion_test.go | 303 ++++++++++++++++++ test/tektonbundles_test.go | 194 ++++------- 4 files changed, 372 insertions(+), 129 deletions(-) diff --git a/pkg/apis/pipeline/v1beta1/pipelineref_conversion.go b/pkg/apis/pipeline/v1beta1/pipelineref_conversion.go index 88fed430ccb..ae794c8b738 100644 --- a/pkg/apis/pipeline/v1beta1/pipelineref_conversion.go +++ b/pkg/apis/pipeline/v1beta1/pipelineref_conversion.go @@ -56,7 +56,7 @@ func (pr PipelineRef) convertBundleToResolver(sink *v1.PipelineRef) { Value: v1.ParamValue{StringVal: pr.Name, Type: v1.ParamTypeString}, }, { Name: "kind", - Value: v1.ParamValue{StringVal: "Task", Type: v1.ParamTypeString}, + Value: v1.ParamValue{StringVal: "Pipeline", Type: v1.ParamTypeString}, }}, } } diff --git a/pkg/apis/pipeline/v1beta1/pipelinerun_conversion_test.go b/pkg/apis/pipeline/v1beta1/pipelinerun_conversion_test.go index d4744209dbc..0dc99e2b518 100644 --- a/pkg/apis/pipeline/v1beta1/pipelinerun_conversion_test.go +++ b/pkg/apis/pipeline/v1beta1/pipelinerun_conversion_test.go @@ -448,7 +448,7 @@ func TestPipelineRunConversionFromDeprecated(t *testing.T) { Params: v1beta1.Params{ {Name: "bundle", Value: v1beta1.ParamValue{StringVal: "test-bundle", Type: "string"}}, {Name: "name", Value: v1beta1.ParamValue{StringVal: "test-bundle-name", Type: "string"}}, - {Name: "kind", Value: v1beta1.ParamValue{StringVal: "Task", Type: "string"}}, + {Name: "kind", Value: v1beta1.ParamValue{StringVal: "Pipeline", Type: "string"}}, }, }, }, diff --git a/test/conversion_test.go b/test/conversion_test.go index b82ed9fd014..d429e68fcf2 100644 --- a/test/conversion_test.go +++ b/test/conversion_test.go @@ -42,6 +42,10 @@ var ( filterContainerStateTerminated = cmpopts.IgnoreFields(corev1.ContainerStateTerminated{}, "StartedAt", "FinishedAt", "ContainerID", "Message") filterV1StepState = cmpopts.IgnoreFields(v1.StepState{}, "Name", "ImageID", "Container") filterV1beta1StepState = cmpopts.IgnoreFields(v1beta1.StepState{}, "Name", "ImageID", "ContainerName") + filterV1TaskRunSA = cmpopts.IgnoreFields(v1.TaskRunSpec{}, "ServiceAccountName") + filterV1beta1TaskRunSA = cmpopts.IgnoreFields(v1beta1.TaskRunSpec{}, "ServiceAccountName") + filterV1PipelineRunSA = cmpopts.IgnoreFields(v1.PipelineTaskRunTemplate{}, "ServiceAccountName") + filterV1beta1PipelineRunSA = cmpopts.IgnoreFields(v1beta1.PipelineRunSpec{}, "ServiceAccountName") filterMetadata = []cmp.Option{filterTypeMeta, filterObjectMeta, filterAnnotations} filterV1TaskRunFields = []cmp.Option{filterTypeMeta, filterObjectMeta, filterLabels, filterAnnotations, filterCondition, filterV1TaskRunStatus, filterContainerStateTerminated, filterV1StepState} @@ -649,6 +653,210 @@ status: name: %s-hello-task pipelineTaskName: hello-task ` + + v1beta1TaskWithBundleYaml = ` +metadata: + name: %s + namespace: %s +spec: + steps: + - name: hello + image: alpine + script: 'echo Hello' +` + + v1beta1PipelineWithBundleYaml = ` +metadata: + name: %s + namespace: %s +spec: + tasks: + - name: hello-world + taskRef: + resolver: bundles + params: + - name: bundle + value: %s + - name: name + value: %s +` + + v1beta1TaskRunWithBundleYaml = ` +metadata: + name: %s + namespace: %s +spec: + taskRef: + name: %s + bundle: %s +` + + v1beta1PipelineRunWithBundleYaml = ` +metadata: + name: %s + namespace: %s +spec: + pipelineRef: + name: %s + bundle: %s +` + + v1TaskRunWithBundleExpectedYaml = ` +metadata: + name: %s + namespace: %s +spec: + serviceAccountName: default + timeout: 1h + taskRef: + kind: Task + resolver: bundles + params: + - name: bundle + value: %s + - name: name + value: %s + - name: kind + value: Task +status: + conditions: + - type: Succeeded + status: "True" + reason: "Succeeded" + podName: %s-pod + taskSpec: + steps: + - computeResources: {} + image: alpine + name: hello + script: 'echo Hello' + steps: + - image: alpine + name: hello + script: 'echo Hello' + terminated: + reason: Completed +` + + v1PipelineRunWithBundleExpectedYaml = ` +metadata: + name: %s + namespace: %s +spec: + taskRunTemplate: + serviceAccountName: default + timeouts: + pipeline: 1h + pipelineRef: + kind: Pipeline + resolver: bundles + params: + - name: bundle + value: %s + - name: name + value: %s + - name: kind + value: Pipeline +status: + conditions: + - type: Succeeded + status: "True" + reason: "Succeeded" + pipelineSpec: + tasks: + - name: hello-world + taskRef: + kind: Task + resolver: bundles + params: + - name: bundle + value: %s + - name: name + value: %s + childReferences: + - apiVersion: tekton.dev/v1beta1 + kind: TaskRun + name: %s-hello-world + pipelineTaskName: hello-world +` + + v1beta1TaskRunWithBundleRoundTripYaml = ` +metadata: + name: %s + namespace: %s +spec: + serviceAccountName: default + timeout: 1h + taskRef: + kind: Task + resolver: bundles + params: + - name: bundle + value: %s + - name: name + value: %s + - name: kind + value: Task +status: + conditions: + - type: Succeeded + status: "True" + reason: "Succeeded" + podName: %s-pod + taskSpec: + steps: + - computeResources: {} + image: alpine + name: hello + script: 'echo Hello' + steps: + - image: alpine + name: hello + script: 'echo Hello' + terminated: + reason: Completed +` + + v1beta1PipelineRunWithBundleRoundTripYaml = ` +metadata: + name: %s + namespace: %s +spec: + serviceAccountName: default + timeouts: + pipeline: 1h + pipelineRef: + kind: Pipeline + resolver: bundles + params: + - name: bundle + value: %s + - name: name + value: %s + - name: kind + value: Pipeline +status: + conditions: + - type: Succeeded + status: "True" + reason: "Succeeded" + pipelineSpec: + tasks: + - name: hello-world + taskRef: + kind: Task + resolver: bundles + params: + - name: bundle + value: %s + - name: name + value: %s + childReferences: + - apiVersion: tekton.dev/v1beta1 + kind: TaskRun + name: %s-hello-world + pipelineTaskName: hello-world +` ) // TestTaskCRDConversion first creates a v1beta1 Task CRD using v1beta1Clients and @@ -947,3 +1155,98 @@ func TestPipelineRunCRDConversion(t *testing.T) { t.Errorf("-want, +got: %v", d) } } + +// TestBundleConversion tests v1beta1 bundle syntax converted into v1 since it has +// been deprecated in v1 and it would be converted into bundle resolver in pipelineRef +// and taskRef. It setups a registry for a bundle of a v1beta1 Task and Pipeline +// and uses the v1beta1 TaskRef/ PipelineRef to test the conversion from v1beta1 bundle +// syntax to a v1 bundle resolver and then it tests roundtrip back to v1beta1 bundle +// resolver syntax. +func TestBundleConversion(t *testing.T) { + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + t.Parallel() + + c, namespace := setup(ctx, t, withRegistry, bundleFeatureFlags) + knativetest.CleanupOnInterrupt(func() { tearDown(ctx, t, c, namespace) }, t.Logf) + defer tearDown(ctx, t, c, namespace) + + repo := fmt.Sprintf("%s:5000/tektonbundlessimple", getRegistryServiceIP(ctx, t, c, namespace)) + taskName := helpers.ObjectNameForTest(t) + pipelineName := helpers.ObjectNameForTest(t) + task := parse.MustParseV1beta1Task(t, fmt.Sprintf(v1beta1TaskWithBundleYaml, taskName, namespace)) + pipeline := parse.MustParseV1beta1Pipeline(t, fmt.Sprintf(v1beta1PipelineWithBundleYaml, pipelineName, namespace, repo, taskName)) + setupBundle(ctx, t, c, namespace, repo, task, pipeline) + + v1beta1TaskRunName := helpers.ObjectNameForTest(t) + v1beta1TaskRun := parse.MustParseV1beta1TaskRun(t, fmt.Sprintf(v1beta1TaskRunWithBundleYaml, v1beta1TaskRunName, namespace, taskName, repo)) + v1TaskRunExpected := parse.MustParseV1TaskRun(t, fmt.Sprintf(v1TaskRunWithBundleExpectedYaml, v1beta1TaskRunName, namespace, repo, taskName, v1beta1TaskRunName)) + v1beta1TaskRunRoundTripExpected := parse.MustParseV1beta1TaskRun(t, fmt.Sprintf(v1beta1TaskRunWithBundleRoundTripYaml, v1beta1TaskRunName, namespace, repo, taskName, v1beta1TaskRunName)) + + v1TaskRunExpected.Status.Provenance = &v1.Provenance{ + FeatureFlags: getFeatureFlagsBaseOnAPIFlag(t), + } + v1beta1TaskRunRoundTripExpected.Status.Provenance = &v1beta1.Provenance{ + FeatureFlags: getFeatureFlagsBaseOnAPIFlag(t), + } + + if _, err := c.V1beta1TaskRunClient.Create(ctx, v1beta1TaskRun, metav1.CreateOptions{}); err != nil { + t.Fatalf("Failed to create v1beta1 TaskRun: %s", err) + } + if err := WaitForTaskRunState(ctx, c, v1beta1TaskRunName, Succeed(v1beta1TaskRunName), v1beta1TaskRunName, "v1beta1"); err != nil { + t.Fatalf("Failed waiting for v1beta1 TaskRun done: %v", err) + } + + v1TaskRunGot, err := c.V1TaskRunClient.Get(ctx, v1beta1TaskRunName, metav1.GetOptions{}) + if err != nil { + t.Fatalf("Couldn't get expected v1 TaskRun for %s: %s", v1beta1TaskRunName, err) + } + if d := cmp.Diff(v1TaskRunExpected, v1TaskRunGot, append(filterV1TaskRunFields, filterV1TaskRunSA)...); d != "" { + t.Errorf("-want, +got: %v", d) + } + + v1beta1TaskRunRoundTrip := &v1beta1.TaskRun{} + if err := v1beta1TaskRunRoundTrip.ConvertFrom(context.Background(), v1TaskRunGot); err != nil { + t.Fatalf("Failed to convert roundtrip v1beta1TaskRunGot ConvertFrom v1 = %v", err) + } + if d := cmp.Diff(v1beta1TaskRunRoundTripExpected, v1beta1TaskRunRoundTrip, append(filterV1beta1TaskRunFields, filterV1beta1TaskRunSA)...); d != "" { + t.Errorf("-want, +got: %v", d) + } + + v1beta1ToV1PipelineRunName := helpers.ObjectNameForTest(t) + v1beta1PipelineRun := parse.MustParseV1beta1PipelineRun(t, fmt.Sprintf(v1beta1PipelineRunWithBundleYaml, v1beta1ToV1PipelineRunName, namespace, pipelineName, repo)) + v1PipelineRunExpected := parse.MustParseV1PipelineRun(t, fmt.Sprintf(v1PipelineRunWithBundleExpectedYaml, v1beta1ToV1PipelineRunName, namespace, repo, pipelineName, repo, taskName, v1beta1ToV1PipelineRunName)) + v1beta1PRRoundTripExpected := parse.MustParseV1beta1PipelineRun(t, fmt.Sprintf(v1beta1PipelineRunWithBundleRoundTripYaml, v1beta1ToV1PipelineRunName, namespace, repo, pipelineName, repo, taskName, v1beta1ToV1PipelineRunName)) + + v1PipelineRunExpected.Status.Provenance = &v1.Provenance{ + FeatureFlags: getFeatureFlagsBaseOnAPIFlag(t), + } + v1beta1PRRoundTripExpected.Status.Provenance = &v1beta1.Provenance{ + FeatureFlags: getFeatureFlagsBaseOnAPIFlag(t), + } + + if _, err := c.V1beta1PipelineRunClient.Create(ctx, v1beta1PipelineRun, metav1.CreateOptions{}); err != nil { + t.Fatalf("Failed to create v1beta1 PipelineRun: %s", err) + } + if err := WaitForPipelineRunState(ctx, c, v1beta1ToV1PipelineRunName, timeout, Succeed(v1beta1ToV1PipelineRunName), v1beta1ToV1PipelineRunName, "v1beta1"); err != nil { + t.Fatalf("Failed waiting for v1beta1 PipelineRun done: %v", err) + } + + v1PipelineRunGot, err := c.V1PipelineRunClient.Get(ctx, v1beta1ToV1PipelineRunName, metav1.GetOptions{}) + if err != nil { + t.Fatalf("Couldn't get expected v1 PipelineRun for %s: %s", v1beta1ToV1PipelineRunName, err) + } + if d := cmp.Diff(v1PipelineRunExpected, v1PipelineRunGot, append(filterV1PipelineRunFields, filterV1PipelineRunSA)...); d != "" { + t.Errorf("-want, +got: %v", d) + } + + v1beta1PRRoundTrip := &v1beta1.PipelineRun{} + if err := v1beta1PRRoundTrip.ConvertFrom(context.Background(), v1PipelineRunGot); err != nil { + t.Fatalf("Error roundtrip v1beta1PipelineRun ConvertFrom v1PipelineRunGot = %v", err) + } + if d := cmp.Diff(v1beta1PRRoundTripExpected, v1beta1PRRoundTrip, append(filterV1beta1PipelineRunFields, filterV1beta1PipelineRunSA)...); d != "" { + t.Errorf("-want, +got: %v", d) + } +} diff --git a/test/tektonbundles_test.go b/test/tektonbundles_test.go index 35279094d30..7444f5aee02 100644 --- a/test/tektonbundles_test.go +++ b/test/tektonbundles_test.go @@ -36,6 +36,7 @@ import ( "github.com/google/go-containerregistry/pkg/v1/layout" "github.com/google/go-containerregistry/pkg/v1/mutate" "github.com/google/go-containerregistry/pkg/v1/tarball" + "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" "github.com/tektoncd/pipeline/pkg/pod" "github.com/tektoncd/pipeline/pkg/reconciler/pipelinerun" "github.com/tektoncd/pipeline/test/parse" @@ -47,7 +48,7 @@ import ( "sigs.k8s.io/yaml" ) -var requireFeatureFlags = requireAnyGate(map[string]string{ +var bundleFeatureFlags = requireAnyGate(map[string]string{ "enable-tekton-oci-bundles": "true", "enable-api-fields": "alpha", }) @@ -60,7 +61,7 @@ var resolverFeatureFlags = requireAnyGate(map[string]string{ // images. func TestTektonBundlesSimpleWorkingExample(t *testing.T) { ctx := context.Background() - c, namespace := setup(ctx, t, withRegistry, requireFeatureFlags) + c, namespace := setup(ctx, t, withRegistry, bundleFeatureFlags) t.Parallel() @@ -71,12 +72,6 @@ func TestTektonBundlesSimpleWorkingExample(t *testing.T) { pipelineName := helpers.ObjectNameForTest(t) pipelineRunName := helpers.ObjectNameForTest(t) repo := fmt.Sprintf("%s:5000/tektonbundlessimple", getRegistryServiceIP(ctx, t, c, namespace)) - - ref, err := name.ParseReference(repo) - if err != nil { - t.Fatalf("Failed to parse %s as an OCI reference: %s", repo, err) - } - task := parse.MustParseV1beta1Task(t, fmt.Sprintf(` metadata: name: %s @@ -100,47 +95,7 @@ spec: bundle: %s `, pipelineName, namespace, taskName, repo)) - // Write the task and pipeline into an image to the registry in the proper format. - rawTask, err := yaml.Marshal(task) - if err != nil { - t.Fatalf("Failed to marshal task to yaml: %s", err) - } - - rawPipeline, err := yaml.Marshal(pipeline) - if err != nil { - t.Fatalf("Failed to marshal task to yaml: %s", err) - } - - img := empty.Image - taskLayer, err := tarball.LayerFromReader(bytes.NewBuffer(rawTask)) - if err != nil { - t.Fatalf("Failed to create oci layer from task: %s", err) - } - pipelineLayer, err := tarball.LayerFromReader(bytes.NewBuffer(rawPipeline)) - if err != nil { - t.Fatalf("Failed to create oci layer from pipeline: %s", err) - } - img, err = mutate.Append(img, mutate.Addendum{ - Layer: taskLayer, - Annotations: map[string]string{ - "dev.tekton.image.name": taskName, - "dev.tekton.image.kind": strings.ToLower(task.Kind), - "dev.tekton.image.apiVersion": task.APIVersion, - }, - }, mutate.Addendum{ - Layer: pipelineLayer, - Annotations: map[string]string{ - "dev.tekton.image.name": pipelineName, - "dev.tekton.image.kind": strings.ToLower(pipeline.Kind), - "dev.tekton.image.apiVersion": pipeline.APIVersion, - }, - }) - if err != nil { - t.Fatalf("Failed to create an oci image from the task and pipeline layers: %s", err) - } - - // Publish this image to the in-cluster registry. - publishImg(ctx, t, c, namespace, img, ref) + setupBundle(ctx, t, c, namespace, repo, task, pipeline) // Now generate a PipelineRun to invoke this pipeline and task. pr := parse.MustParseV1beta1PipelineRun(t, fmt.Sprintf(` @@ -210,10 +165,6 @@ func TestTektonBundlesResolver(t *testing.T) { pipelineName := helpers.ObjectNameForTest(t) pipelineRunName := helpers.ObjectNameForTest(t) repo := fmt.Sprintf("%s:5000/tektonbundlesresolver", getRegistryServiceIP(ctx, t, c, namespace)) - ref, err := name.ParseReference(repo) - if err != nil { - t.Fatalf("Failed to parse %s as an OCI reference: %s", repo, err) - } task := parse.MustParseV1beta1Task(t, fmt.Sprintf(` metadata: @@ -242,47 +193,7 @@ spec: value: %s `, pipelineName, namespace, repo, taskName)) - // Write the task and pipeline into an image to the registry in the proper format. - rawTask, err := yaml.Marshal(task) - if err != nil { - t.Fatalf("Failed to marshal task to yaml: %s", err) - } - - rawPipeline, err := yaml.Marshal(pipeline) - if err != nil { - t.Fatalf("Failed to marshal task to yaml: %s", err) - } - - img := empty.Image - taskLayer, err := tarball.LayerFromReader(bytes.NewBuffer(rawTask)) - if err != nil { - t.Fatalf("Failed to create oci layer from task: %s", err) - } - pipelineLayer, err := tarball.LayerFromReader(bytes.NewBuffer(rawPipeline)) - if err != nil { - t.Fatalf("Failed to create oci layer from pipeline: %s", err) - } - img, err = mutate.Append(img, mutate.Addendum{ - Layer: taskLayer, - Annotations: map[string]string{ - "dev.tekton.image.name": taskName, - "dev.tekton.image.kind": strings.ToLower(task.Kind), - "dev.tekton.image.apiVersion": task.APIVersion, - }, - }, mutate.Addendum{ - Layer: pipelineLayer, - Annotations: map[string]string{ - "dev.tekton.image.name": pipelineName, - "dev.tekton.image.kind": strings.ToLower(pipeline.Kind), - "dev.tekton.image.apiVersion": pipeline.APIVersion, - }, - }) - if err != nil { - t.Fatalf("Failed to create an oci image from the task and pipeline layers: %s", err) - } - - // Publish this image to the in-cluster registry. - publishImg(ctx, t, c, namespace, img, ref) + setupBundle(ctx, t, c, namespace, repo, task, pipeline) // Now generate a PipelineRun to invoke this pipeline and task. pr := parse.MustParseV1beta1PipelineRun(t, fmt.Sprintf(` @@ -346,7 +257,7 @@ spec: // TestTektonBundlesUsingRegularImage is an integration test which passes a non-Tekton bundle as a task reference. func TestTektonBundlesUsingRegularImage(t *testing.T) { ctx := context.Background() - c, namespace := setup(ctx, t, withRegistry, requireFeatureFlags) + c, namespace := setup(ctx, t, withRegistry, bundleFeatureFlags) t.Parallel() @@ -358,11 +269,6 @@ func TestTektonBundlesUsingRegularImage(t *testing.T) { pipelineRunName := helpers.ObjectNameForTest(t) repo := fmt.Sprintf("%s:5000/tektonbundlesregularimage", getRegistryServiceIP(ctx, t, c, namespace)) - ref, err := name.ParseReference(repo) - if err != nil { - t.Fatalf("Failed to parse %s as an OCI reference: %s", repo, err) - } - pipeline := parse.MustParseV1beta1Pipeline(t, fmt.Sprintf(` metadata: name: %s @@ -375,32 +281,7 @@ spec: bundle: registry `, pipelineName, namespace, taskName)) - // Write the pipeline into an image to the registry in the proper format. We don't write the task because we are - // using an non Tekton Bundle. - rawPipeline, err := yaml.Marshal(pipeline) - if err != nil { - t.Fatalf("Failed to marshal task to yaml: %s", err) - } - - img := empty.Image - pipelineLayer, err := tarball.LayerFromReader(bytes.NewBuffer(rawPipeline)) - if err != nil { - t.Fatalf("Failed to create oci layer from pipeline: %s", err) - } - img, err = mutate.Append(img, mutate.Addendum{ - Layer: pipelineLayer, - Annotations: map[string]string{ - "dev.tekton.image.name": pipelineName, - "dev.tekton.image.kind": strings.ToLower(pipeline.Kind), - "dev.tekton.image.apiVersion": pipeline.APIVersion, - }, - }) - if err != nil { - t.Fatalf("Failed to create an oci image from the task and pipeline layers: %s", err) - } - - // Publish this image to the in-cluster registry. - publishImg(ctx, t, c, namespace, img, ref) + setupBundle(ctx, t, c, namespace, repo, nil, pipeline) // Now generate a PipelineRun to invoke this pipeline and task. pr := parse.MustParseV1beta1PipelineRun(t, fmt.Sprintf(` @@ -429,7 +310,7 @@ spec: // task reference. func TestTektonBundlesUsingImproperFormat(t *testing.T) { ctx := context.Background() - c, namespace := setup(ctx, t, withRegistry, requireFeatureFlags) + c, namespace := setup(ctx, t, withRegistry, bundleFeatureFlags) t.Parallel() @@ -669,3 +550,62 @@ func publishImg(ctx context.Context, t *testing.T, c *clients, namespace string, } } } + +// setupBundle creates an empty image, provides a reference to the fake registry and pushes the +// bundled task and pipeline within an image into the registry +func setupBundle(ctx context.Context, t *testing.T, c *clients, namespace, repo string, task *v1beta1.Task, pipeline *v1beta1.Pipeline) { + t.Helper() + img := empty.Image + ref, err := name.ParseReference(repo) + if err != nil { + t.Fatalf("Failed to parse %s as an OCI reference: %s", repo, err) + } + + var rawTask, rawPipeline []byte + var taskLayer, pipelineLayer v1.Layer + // Write the task and pipeline into an image to the registry in the proper format. + if task != nil { + rawTask, err = yaml.Marshal(task) + if err != nil { + t.Fatalf("Failed to marshal task to yaml: %s", err) + } + taskLayer, err = tarball.LayerFromReader(bytes.NewBuffer(rawTask)) + if err != nil { + t.Fatalf("Failed to create oci layer from task: %s", err) + } + img, err = mutate.Append(img, mutate.Addendum{ + Layer: taskLayer, + Annotations: map[string]string{ + "dev.tekton.image.name": task.Name, + "dev.tekton.image.kind": strings.ToLower(task.Kind), + "dev.tekton.image.apiVersion": task.APIVersion, + }, + }) + } + + if pipeline != nil { + rawPipeline, err = yaml.Marshal(pipeline) + if err != nil { + t.Fatalf("Failed to marshal task to yaml: %s", err) + } + pipelineLayer, err = tarball.LayerFromReader(bytes.NewBuffer(rawPipeline)) + if err != nil { + t.Fatalf("Failed to create oci layer from pipeline: %s", err) + } + img, err = mutate.Append(img, mutate.Addendum{ + Layer: pipelineLayer, + Annotations: map[string]string{ + "dev.tekton.image.name": pipeline.Name, + "dev.tekton.image.kind": strings.ToLower(pipeline.Kind), + "dev.tekton.image.apiVersion": pipeline.APIVersion, + }, + }) + } + + if err != nil { + t.Fatalf("Failed to create an oci image from the task and pipeline layers: %s", err) + } + + // Publish this image to the in-cluster registry. + publishImg(ctx, t, c, namespace, img, ref) +}