diff --git a/tests/integration/dry_run_test.go b/tests/integration/dry_run_test.go index 177e1244f..9e207e18f 100644 --- a/tests/integration/dry_run_test.go +++ b/tests/integration/dry_run_test.go @@ -1,11 +1,17 @@ package integration_test import ( + "os" "path/filepath" + "strings" . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" ) +// dockerTransportPrefix is the "docker://" transport prefix used in mapping.txt entries. +const dockerTransportPrefix = "docker://" + var _ = Describe("dry-run", func() { var workDir string BeforeEach(func() { @@ -65,4 +71,57 @@ var _ = Describe("dry-run", func() { expectNoRepositoriesInRegistry(*testRegistry) }) }) + + Describe("mirrorToDisk dry-run with a manifest list image", func() { + iscManifestList := filepath.Join("dry_run", "isc-manifest-list.yaml") + topLevelSource := "docker://quay.io/openshifttest/hello-openshift@sha256:61b8f5e1a3b5dbd9e2c35fd448dc5106337d7a299873dd3a6f0cd8d4891ecc27" + + // The digest above resolves to a manifest list (image index) with these two + // platform-specific sub-manifests, which never change since it's pinned by digest. + manifestListSubDigests := []string{ + "sha256:685a0ca5f33d9f921966c9d9f5922e266affbf93dde0c156709ecdea362f88f4", + "sha256:a51d6da571b2e1f57249f4d966af65cbfb361dad66cde7121c1bb656ab196269", + } + + It("should include manifest list sub-digests in mapping.txt (OCPBUGS-66263)", func() { + By("running mirrorToDisk with --dry-run-manifest-lists") + result, err := runner.MirrorToDisk(ctx, filepath.Join(iscDir, iscManifestList), workDir, + "--remove-signatures=true", "--dry-run-manifest-lists") + expectOcMirrorCommandSuccess(result, err) + + By("verifying mapping.txt contains an entry for each manifest list sub-digest") + expectMappingContainsManifestListSubDigests(workDir, topLevelSource, manifestListSubDigests) + }) + }) }) + +// expectMappingContainsManifestListSubDigests verifies that mapping.txt contains one extra +// line per manifest list sub-digest, each re-pinned to the top-level destination's repository. +// Guards against OCPBUGS-66263, where sub-manifest digests were dropped from the mapping file. +func expectMappingContainsManifestListSubDigests(workDir, topLevelSource string, subDigests []string) { + mappingPath := filepath.Join(workDir, dirWorkingDir, "dry-run", "mapping.txt") + data, err := os.ReadFile(mappingPath) + Expect(err).NotTo(HaveOccurred(), "mapping.txt not found at: %s", mappingPath) + + mappings := make(map[string]string) + for _, line := range strings.Split(strings.TrimSpace(string(data)), "\n") { + parts := strings.SplitN(line, "=", 2) + Expect(parts).To(HaveLen(2), "mapping line does not follow source=destination format: %s", line) + mappings[parts[0]] = parts[1] + } + + destination, found := mappings[topLevelSource] + Expect(found).To(BeTrue(), "mapping.txt does not contain top-level entry for %q", topLevelSource) + + sourceBase, _, _ := strings.Cut(topLevelSource, "@") + // referenceWithoutTagOrDigest expects a bare image reference, so the "docker://" transport + // prefix (present on mapping.txt destinations) must be stripped before parsing and restored + // afterwards to rebuild a destination in the same format written by oc-mirror. + destBase := dockerTransportPrefix + referenceWithoutTagOrDigest(strings.TrimPrefix(destination, dockerTransportPrefix)) + for _, digest := range subDigests { + subSource := sourceBase + "@" + digest + subDest := destBase + "@" + digest + Expect(mappings).To(HaveKeyWithValue(subSource, subDest), + "mapping.txt is missing sub-digest entry %q -> %q", subSource, subDest) + } +} diff --git a/tests/integration/testdata/imagesetconfigs/dry_run/isc-manifest-list.yaml b/tests/integration/testdata/imagesetconfigs/dry_run/isc-manifest-list.yaml new file mode 100644 index 000000000..3e92ed036 --- /dev/null +++ b/tests/integration/testdata/imagesetconfigs/dry_run/isc-manifest-list.yaml @@ -0,0 +1,9 @@ +# ImageSetConfig to test dry-run mapping of a manifest list (multi-arch) additional image. +# quay.io/openshifttest/hello-openshift@sha256:61b8f5e1a3b5dbd9e2c35fd448dc5106337d7a299873dd3a6f0cd8d4891ecc27 +# is a manifest list with two platform-specific sub-manifests (arm64, ppc64le), used to verify that +# dry-run mapping.txt includes an entry for each sub-digest (OCPBUGS-66263). +kind: ImageSetConfiguration +apiVersion: mirror.openshift.io/v2alpha1 +mirror: + additionalImages: + - name: quay.io/openshifttest/hello-openshift@sha256:61b8f5e1a3b5dbd9e2c35fd448dc5106337d7a299873dd3a6f0cd8d4891ecc27