diff --git a/go.mod b/go.mod index abced6437a..9c05bfbcc0 100644 --- a/go.mod +++ b/go.mod @@ -64,6 +64,7 @@ require ( k8s.io/test-infra v0.0.0-20231004164548-dee1fe445410 k8s.io/utils v0.0.0-20240102154912-e7106e64919e knative.dev/pkg v0.0.0-20240219120257-9227ebb57a4e + oras.land/oras-go/v2 v2.3.0 sigs.k8s.io/controller-runtime v0.17.3 sigs.k8s.io/yaml v1.4.0 ) @@ -310,7 +311,6 @@ require ( k8s.io/kube-aggregator v0.29.2 // indirect k8s.io/kube-openapi v0.0.0-20240221221325-2ac9dc51f3f1 // indirect k8s.io/kubernetes v1.29.2 // indirect - oras.land/oras-go/v2 v2.3.0 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3 // indirect sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3 // indirect diff --git a/pkg/clients/oras/client.go b/pkg/clients/oras/client.go new file mode 100644 index 0000000000..f4b9f2679a --- /dev/null +++ b/pkg/clients/oras/client.go @@ -0,0 +1,52 @@ +package oras + +import ( + "context" + "os" + + "github.com/openshift/library-go/pkg/image/reference" + oras "oras.land/oras-go/v2" + "oras.land/oras-go/v2/content/file" + "oras.land/oras-go/v2/registry/remote" + "oras.land/oras-go/v2/registry/remote/auth" + "oras.land/oras-go/v2/registry/remote/retry" +) + +// PullArtifacts pulls artifacts from the given imagePullSpec. +// Pulled artifacts will be stored in a local directory, whose path is returned. +func PullArtifacts(imagePullSpec string) (string, error) { + storePath, err := os.MkdirTemp("", "pulled-artifacts") + if err != nil { + return "", err + } + fs, err := file.New(storePath) + if err != nil { + return "", err + } + defer fs.Close() + + imageRef, err := reference.Parse(imagePullSpec) + if err != nil { + return "", err + } + + repo, err := remote.NewRepository(imagePullSpec) + if err != nil { + return "", err + } + repo.Client = &auth.Client{ + Client: retry.DefaultClient, + Cache: auth.NewCache(), + Credential: auth.StaticCredential(imageRef.Registry, auth.Credential{ + AccessToken: os.Getenv("QUAY_TOKEN"), + }), + } + + ctx := context.Background() + tag := imageRef.Tag + if _, err := oras.Copy(ctx, repo, tag, fs, tag, oras.DefaultCopyOptions); err != nil { + return "", err + } + + return storePath, nil +} diff --git a/tests/build/build_templates.go b/tests/build/build_templates.go index fb2e0e68a7..beae5307c8 100644 --- a/tests/build/build_templates.go +++ b/tests/build/build_templates.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "os" + "path/filepath" "strings" "time" @@ -14,6 +15,7 @@ import ( appservice "github.com/konflux-ci/application-api/api/v1alpha1" "github.com/konflux-ci/e2e-tests/pkg/clients/has" kubeapi "github.com/konflux-ci/e2e-tests/pkg/clients/kubernetes" + "github.com/konflux-ci/e2e-tests/pkg/clients/oras" "github.com/konflux-ci/e2e-tests/pkg/constants" "github.com/konflux-ci/e2e-tests/pkg/framework" "github.com/konflux-ci/e2e-tests/pkg/utils" @@ -608,6 +610,10 @@ var _ = framework.BuildSuiteDescribe("Build templates E2E test", Label("build", }) } }) + + It("should push Dockerfile to registry", Label(buildTemplatesTestLabel), func() { + ensureOriginalDockerfileIsPushed(kubeadminClient, pr) + }) } It(fmt.Sprintf("pipelineRun should fail for symlink component with Git source URL %s", pythonComponentGitSourceURL), Label(buildTemplatesTestLabel, sourceBuildTestLabel), func() { @@ -682,3 +688,46 @@ func enableHermeticBuildInPipelineBundle(customDockerBuildBundle, prefetchInput } return newDockerBuildPipeline.String(), nil } + +func ensureOriginalDockerfileIsPushed(hub *framework.ControllerHub, pr *tektonpipeline.PipelineRun) { + binaryImage := build.GetBinaryImage(pr) + Expect(binaryImage).ShouldNot(BeEmpty()) + + binaryImageRef, err := reference.Parse(binaryImage) + Expect(err).Should(Succeed()) + + tagInfo, err := build.GetImageTag(binaryImageRef.Namespace, binaryImageRef.Name, binaryImageRef.Tag) + Expect(err).Should(Succeed()) + + dockerfileImageTag := fmt.Sprintf("%s.dockerfile", strings.Replace(tagInfo.ManifestDigest, ":", "-", 1)) + + dockerfileImage := reference.DockerImageReference{ + Registry: binaryImageRef.Registry, + Namespace: binaryImageRef.Namespace, + Name: binaryImageRef.Name, + Tag: dockerfileImageTag, + }.String() + exists, err := build.DoesTagExistsInQuay(dockerfileImage) + Expect(err).Should(Succeed()) + Expect(exists).Should(BeTrue()) + + // Ensure the original Dockerfile used for build was pushed + c := hub.CommonController.KubeRest() + originDockerfileContent, err := build.ReadDockerfileUsedForBuild(c, hub.TektonController, pr) + Expect(err).Should(Succeed()) + + storePath, err := oras.PullArtifacts(dockerfileImage) + Expect(err).Should(Succeed()) + entries, err := os.ReadDir(storePath) + Expect(err).Should(Succeed()) + for _, entry := range entries { + if entry.Type().IsRegular() && entry.Name() == "Dockerfile" { + content, err := os.ReadFile(filepath.Join(storePath, entry.Name())) + Expect(err).Should(Succeed()) + Expect(content).Should(Equal(originDockerfileContent)) + return + } + } + + Fail(fmt.Sprintf("Dockerfile is not found from the pulled artifacts for %s", dockerfileImage)) +} diff --git a/tests/build/multi-platform.go b/tests/build/multi-platform.go index 08b3f75fb4..740035583d 100644 --- a/tests/build/multi-platform.go +++ b/tests/build/multi-platform.go @@ -413,6 +413,13 @@ var _ = framework.MultiPlatformBuildSuiteDescribe("Multi Platform Controller E2E }, timeout, interval).Should(Succeed(), "timed out when verifying that the remote host was cleaned up correctly") }) + It("should push Dockerfile to registry", func() { + pr, err := f.AsKubeAdmin.HasController.GetComponentPipelineRun( + component.GetName(), component.Spec.Application, component.GetNamespace(), "") + Expect(err).Should(Succeed()) + ensureOriginalDockerfileIsPushed(f.AsKubeAdmin, pr) + }) + }) }) })