From 35952d8807961af2a595e72a56657c1119a6096f Mon Sep 17 00:00:00 2001 From: Austin Abro <37223396+AustinAbro321@users.noreply.github.com> Date: Wed, 7 Aug 2024 12:34:38 -0400 Subject: [PATCH] test: unit test index sha (#2844) Signed-off-by: Austin Abro --- src/internal/packager/images/pull.go | 39 ++++++----- src/internal/packager/images/pull_test.go | 69 +++++++++++++++++++ .../packager/images/testdata/agent-index.json | 50 ++++++++++++++ .../images/testdata/agent-manifest.json | 21 ++++++ .../packager/images/testdata/game-index.json | 34 +++++++++ src/test/e2e/14_create_sha_index_test.go | 40 ----------- .../14-index-sha/image-index/zarf.yaml | 9 --- .../14-index-sha/manifest-list/zarf.yaml | 9 --- 8 files changed, 196 insertions(+), 75 deletions(-) create mode 100644 src/internal/packager/images/testdata/agent-index.json create mode 100644 src/internal/packager/images/testdata/agent-manifest.json create mode 100644 src/internal/packager/images/testdata/game-index.json delete mode 100644 src/test/e2e/14_create_sha_index_test.go delete mode 100644 src/test/packages/14-index-sha/image-index/zarf.yaml delete mode 100644 src/test/packages/14-index-sha/manifest-list/zarf.yaml diff --git a/src/internal/packager/images/pull.go b/src/internal/packager/images/pull.go index 7a313c5db0..67907feb9d 100644 --- a/src/internal/packager/images/pull.go +++ b/src/internal/packager/images/pull.go @@ -40,6 +40,26 @@ import ( "golang.org/x/sync/errgroup" ) +func checkForIndex(refInfo transform.Image, desc *remote.Descriptor) error { + if refInfo.Digest != "" && desc != nil && types.MediaType(desc.MediaType).IsIndex() { + var idx v1.IndexManifest + if err := json.Unmarshal(desc.Manifest, &idx); err != nil { + return fmt.Errorf("unable to unmarshal index.json: %w", err) + } + lines := []string{"The following images are available in the index:"} + name := refInfo.Name + if refInfo.Tag != "" { + name += ":" + refInfo.Tag + } + for _, desc := range idx.Manifests { + lines = append(lines, fmt.Sprintf("image - %s@%s with platform %s", name, desc.Digest.String(), desc.Platform.String())) + } + imageOptions := strings.Join(lines, "\n") + return fmt.Errorf("%s resolved to an OCI image index which is not supported by Zarf, select a specific platform to use: %s", refInfo.Reference, imageOptions) + } + return nil +} + // Pull pulls all of the images from the given config. func Pull(ctx context.Context, cfg PullConfig) (map[transform.Image]v1.Image, error) { var longer string @@ -146,23 +166,8 @@ func Pull(ctx context.Context, cfg PullConfig) (map[transform.Image]v1.Image, er } } - if refInfo.Digest != "" && desc != nil && types.MediaType(desc.MediaType).IsIndex() { - message.Warn("Zarf does not currently support direct consumption of OCI image indexes or Docker manifest lists") - - var idx v1.IndexManifest - if err := json.Unmarshal(desc.Manifest, &idx); err != nil { - return fmt.Errorf("unable to unmarshal index manifest: %w", err) - } - lines := []string{"The following images are available in the index:"} - name := refInfo.Name - if refInfo.Tag != "" { - name += ":" + refInfo.Tag - } - for _, desc := range idx.Manifests { - lines = append(lines, fmt.Sprintf("\n(%s) %s@%s", desc.Platform, name, desc.Digest)) - } - message.Warn(strings.Join(lines, "\n")) - return fmt.Errorf("%s resolved to an index, please select a specific platform to use", refInfo.Reference) + if err := checkForIndex(refInfo, desc); err != nil { + return err } cacheImg, err := utils.OnlyHasImageLayers(img) diff --git a/src/internal/packager/images/pull_test.go b/src/internal/packager/images/pull_test.go index eef8bbc9e8..ad38bb3aac 100644 --- a/src/internal/packager/images/pull_test.go +++ b/src/internal/packager/images/pull_test.go @@ -6,14 +6,83 @@ package images import ( "context" + "encoding/json" + "fmt" "os" "path/filepath" "testing" + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/remote" "github.com/stretchr/testify/require" "github.com/zarf-dev/zarf/src/pkg/transform" ) +func TestCheckForIndex(t *testing.T) { + t.Parallel() + testCases := []struct { + name string + ref string + file string + expectedErr string + }{ + { + name: "index sha", + ref: "ghcr.io/zarf-dev/zarf/agent:v0.32.6@sha256:05a82656df5466ce17c3e364c16792ae21ce68438bfe06eeab309d0520c16b48", + file: "agent-index.json", + expectedErr: "%s resolved to an OCI image index which is not supported by Zarf, select a specific platform to use", + }, + { + name: "docker manifest list", + ref: "defenseunicorns/zarf-game@sha256:0b694ca1c33afae97b7471488e07968599f1d2470c629f76af67145ca64428af", + file: "game-index.json", + expectedErr: "%s resolved to an OCI image index which is not supported by Zarf, select a specific platform to use", + }, + { + name: "image manifest", + ref: "ghcr.io/zarf-dev/zarf/agent:v0.32.6", + file: "agent-manifest.json", + expectedErr: "", + }, + { + name: "image manifest sha'd", + ref: "ghcr.io/zarf-dev/zarf/agent:v0.32.6@sha256:b3fabdc7d4ecd0f396016ef78da19002c39e3ace352ea0ae4baa2ce9d5958376", + file: "agent-manifest.json", + expectedErr: "", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + refInfo, err := transform.ParseImageRef(tc.ref) + require.NoError(t, err) + file := filepath.Join("testdata", tc.file) + manifest, err := os.ReadFile(file) + require.NoError(t, err) + var idx v1.IndexManifest + err = json.Unmarshal(manifest, &idx) + require.NoError(t, err) + desc := &remote.Descriptor{ + Descriptor: v1.Descriptor{ + MediaType: idx.MediaType, + }, + Manifest: manifest, + } + err = checkForIndex(refInfo, desc) + if tc.expectedErr != "" { + require.ErrorContains(t, err, fmt.Sprintf(tc.expectedErr, refInfo.Reference)) + // Ensure the error message contains the digest of the manifests the user can use + for _, manifest := range idx.Manifests { + require.ErrorContains(t, err, manifest.Digest.String()) + } + return + } + require.NoError(t, err) + }) + } +} + func TestPull(t *testing.T) { t.Run("pulling a cosign image is successful and doesn't add anything to the cache", func(t *testing.T) { ref, err := transform.ParseImageRef("ghcr.io/stefanprodan/podinfo:sha256-57a654ace69ec02ba8973093b6a786faa15640575fbf0dbb603db55aca2ccec8.sig") diff --git a/src/internal/packager/images/testdata/agent-index.json b/src/internal/packager/images/testdata/agent-index.json new file mode 100644 index 0000000000..2288b7f242 --- /dev/null +++ b/src/internal/packager/images/testdata/agent-index.json @@ -0,0 +1,50 @@ +{ + "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.index.v1+json", + "manifests": [ + { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "digest": "sha256:b3fabdc7d4ecd0f396016ef78da19002c39e3ace352ea0ae4baa2ce9d5958376", + "size": 673, + "platform": { + "architecture": "arm64", + "os": "linux" + } + }, + { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "digest": "sha256:454bf871b5d826b6a31ab14c983583ae9d9e30c2036606b500368c5b552d8fdf", + "size": 673, + "platform": { + "architecture": "amd64", + "os": "linux" + } + }, + { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "digest": "sha256:449cd2cd763614f07219f38f8514b56c7831d583f7c6f767491d3ad31572189c", + "size": 566, + "annotations": { + "vnd.docker.reference.digest": "sha256:b3fabdc7d4ecd0f396016ef78da19002c39e3ace352ea0ae4baa2ce9d5958376", + "vnd.docker.reference.type": "attestation-manifest" + }, + "platform": { + "architecture": "unknown", + "os": "unknown" + } + }, + { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "digest": "sha256:2b51d89bf901b4e65dd85d8c6f135278fc7f86bb48608f0681a83291deadd645", + "size": 566, + "annotations": { + "vnd.docker.reference.digest": "sha256:454bf871b5d826b6a31ab14c983583ae9d9e30c2036606b500368c5b552d8fdf", + "vnd.docker.reference.type": "attestation-manifest" + }, + "platform": { + "architecture": "unknown", + "os": "unknown" + } + } + ] +} diff --git a/src/internal/packager/images/testdata/agent-manifest.json b/src/internal/packager/images/testdata/agent-manifest.json new file mode 100644 index 0000000000..5ef1467f7e --- /dev/null +++ b/src/internal/packager/images/testdata/agent-manifest.json @@ -0,0 +1,21 @@ +{ + "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "config": { + "mediaType": "application/vnd.oci.image.config.v1+json", + "digest": "sha256:cd4790c66d0f72eb0e0c12d75c1025478294af876ff0042d1c3501db57b0244b", + "size": 1284 + }, + "layers": [ + { + "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", + "digest": "sha256:32f8946351207153325c9d5eb56c61bbd96f9cd8deaa6dcecc4a3d4867609bec", + "size": 327582 + }, + { + "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip", + "digest": "sha256:72dbbe2df2af4ee1b7a0c7ee66782999e8f6f846ffed250aec3738791d941ba7", + "size": 38937433 + } + ] +} diff --git a/src/internal/packager/images/testdata/game-index.json b/src/internal/packager/images/testdata/game-index.json new file mode 100644 index 0000000000..b8eadb836d --- /dev/null +++ b/src/internal/packager/images/testdata/game-index.json @@ -0,0 +1,34 @@ +{ + "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json", + "schemaVersion": 2, + "manifests": [ + { + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "digest": "sha256:f78e442f0f3eb3e9459b5ae6b1a8fda62f8dfe818112e7d130a4e8ae72b3cbff", + "size": 739, + "platform": { + "architecture": "arm", + "os": "linux", + "variant": "v7" + } + }, + { + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "digest": "sha256:e4d27fe4b7bf6d5cb7ef02ed0d33ec0846796c09d6ed4bd94c8b946119a01b09", + "size": 739, + "platform": { + "architecture": "arm64", + "os": "linux" + } + }, + { + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "digest": "sha256:e81b1467b812019f8e8e81450728b084471806dc7e959b7beb9f39933c337e7d", + "size": 739, + "platform": { + "architecture": "amd64", + "os": "linux" + } + } + ] +} diff --git a/src/test/e2e/14_create_sha_index_test.go b/src/test/e2e/14_create_sha_index_test.go deleted file mode 100644 index 40b198738b..0000000000 --- a/src/test/e2e/14_create_sha_index_test.go +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2021-Present The Zarf Authors - -// Package test provides e2e tests for Zarf. -package test - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestCreateIndexShaErrors(t *testing.T) { - t.Log("E2E: CreateIndexShaErrors") - - testCases := []struct { - name string - packagePath string - expectedImageInStderr string - }{ - { - name: "Image Index", - packagePath: "src/test/packages/14-index-sha/image-index", - expectedImageInStderr: "ghcr.io/zarf-dev/zarf/agent:v0.32.6@sha256:b3fabdc7d4ecd0f396016ef78da19002c39e3ace352ea0ae4baa2ce9d5958376", - }, - { - name: "Manifest List", - packagePath: "src/test/packages/14-index-sha/manifest-list", - expectedImageInStderr: "docker.io/defenseunicorns/zarf-game@sha256:f78e442f0f3eb3e9459b5ae6b1a8fda62f8dfe818112e7d130a4e8ae72b3cbff", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - _, stderr, err := e2e.Zarf(t, "package", "create", tc.packagePath, "--confirm") - require.Error(t, err) - require.Contains(t, stderr, tc.expectedImageInStderr) - }) - } -} diff --git a/src/test/packages/14-index-sha/image-index/zarf.yaml b/src/test/packages/14-index-sha/image-index/zarf.yaml deleted file mode 100644 index 76d3e6ab4f..0000000000 --- a/src/test/packages/14-index-sha/image-index/zarf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -kind: ZarfPackageConfig -metadata: - name: image-index - -components: - - name: baseline - required: true - images: - - ghcr.io/zarf-dev/zarf/agent:v0.32.6@sha256:05a82656df5466ce17c3e364c16792ae21ce68438bfe06eeab309d0520c16b48 diff --git a/src/test/packages/14-index-sha/manifest-list/zarf.yaml b/src/test/packages/14-index-sha/manifest-list/zarf.yaml deleted file mode 100644 index 9d7ad76b20..0000000000 --- a/src/test/packages/14-index-sha/manifest-list/zarf.yaml +++ /dev/null @@ -1,9 +0,0 @@ -kind: ZarfPackageConfig -metadata: - name: manifest-list - -components: - - name: baseline - required: true - images: - - defenseunicorns/zarf-game@sha256:0b694ca1c33afae97b7471488e07968599f1d2470c629f76af67145ca64428af