From 859717aa721419ad851af9347dd3cb23e51f88f6 Mon Sep 17 00:00:00 2001 From: unclegedd Date: Sun, 26 May 2024 16:03:35 -0500 Subject: [PATCH] feat: speeds up layer verification and removes non-required layers from bundle --- src/pkg/bundler/fetcher/local.go | 2 +- src/test/common.go | 8 +- src/test/e2e/bundle_test.go | 38 ------ src/test/e2e/optional_bundle_test.go | 163 +++++++++++++++++++++++++ src/test/packages/prometheus/zarf.yaml | 11 ++ 5 files changed, 182 insertions(+), 40 deletions(-) create mode 100644 src/test/e2e/optional_bundle_test.go diff --git a/src/pkg/bundler/fetcher/local.go b/src/pkg/bundler/fetcher/local.go index 3dc320bd3..da538b078 100644 --- a/src/pkg/bundler/fetcher/local.go +++ b/src/pkg/bundler/fetcher/local.go @@ -160,7 +160,7 @@ func (f *localFetcher) toBundle(pkgTmp string) ([]ocispec.Descriptor, error) { return nil, err } - // go through the filtered paths and add them to the bundle store + // go through the paths that should be bundled and add them to the bundle store var descs []ocispec.Descriptor for _, path := range pathsToBundle { name, err := filepath.Rel(pkgTmp, path) diff --git a/src/test/common.go b/src/test/common.go index e122b4a18..b6e5475ea 100644 --- a/src/test/common.go +++ b/src/test/common.go @@ -91,8 +91,14 @@ func (e2e *UDSE2ETest) GetLogFileContents(t *testing.T, stdErr string) string { // SetupDockerRegistry uses the host machine's docker daemon to spin up a local registry for testing purposes. func (e2e *UDSE2ETest) SetupDockerRegistry(t *testing.T, port int) { + // check if registry is already running on port + _, _, err := exec.Cmd("docker", "inspect", fmt.Sprintf("registry-%d", port)) + if err == nil { + fmt.Println("Registry already running, skipping setup") + return + } registryImage := "registry:2.8.3" - err := exec.CmdWithPrint("docker", "run", "-d", "--restart=always", "-p", fmt.Sprintf("%d:5000", port), "--name", fmt.Sprintf("registry-%d", port), registryImage) + err = exec.CmdWithPrint("docker", "run", "-d", "--restart=always", "-p", fmt.Sprintf("%d:5000", port), "--name", fmt.Sprintf("registry-%d", port), registryImage) require.NoError(t, err) // Check for registry health diff --git a/src/test/e2e/bundle_test.go b/src/test/e2e/bundle_test.go index ec705bf9b..6274d81be 100644 --- a/src/test/e2e/bundle_test.go +++ b/src/test/e2e/bundle_test.go @@ -521,44 +521,6 @@ func TestBundleWithComposedPkgComponent(t *testing.T) { remove(t, bundlePath) } -func TestBundleOptionalComponents(t *testing.T) { - deployZarfInit(t) - e2e.SetupDockerRegistry(t, 888) - defer e2e.TeardownRegistry(t, 888) - - // create 2 Zarf pkgs to be bundled - zarfPkgPath := "src/test/packages/podinfo-and-nginx" - e2e.CreateZarfPkg(t, zarfPkgPath, false) - - zarfPkgPath = "src/test/packages/prometheus" - pkg := filepath.Join(zarfPkgPath, fmt.Sprintf("zarf-package-prometheus-%s-0.0.1.tar.zst", e2e.Arch)) - e2e.CreateZarfPkg(t, zarfPkgPath, false) - zarfPublish(t, pkg, "localhost:888") - - // create bundle and publish - bundleDir := "src/test/bundles/14-optional-components" - bundleName := "optional-components" - bundleTarballName := fmt.Sprintf("uds-bundle-%s-%s-0.0.1.tar.zst", bundleName, e2e.Arch) - bundlePath := filepath.Join(bundleDir, bundleTarballName) - createLocal(t, bundleDir, e2e.Arch) - publishInsecure(t, bundlePath, "localhost:888") - - // todo: look through bundle contents to make sure only the selected components are present - // local pkgs will a correct pkg manifest (missing non-selected optional component tarballs) - // remote pkgs will not, because they already have a pkg manifest and we don't want to rewrite it - - t.Run("test local deploy", func(t *testing.T) { - deploy(t, bundlePath) - remove(t, bundlePath) - }) - - t.Run("test remote deploy + pulled deploy", func(t *testing.T) { - pulledBundlePath := filepath.Join("build", bundleTarballName) - pull(t, fmt.Sprintf("localhost:888/%s:0.0.1", bundleName), bundleTarballName) - deployAndRemoveLocalAndRemoteInsecure(t, fmt.Sprintf("oci://localhost:888/%s:0.0.1", bundleName), pulledBundlePath) - }) -} - func TestBundleTmpDir(t *testing.T) { deployZarfInit(t) e2e.CreateZarfPkg(t, "src/test/packages/podinfo", false) diff --git a/src/test/e2e/optional_bundle_test.go b/src/test/e2e/optional_bundle_test.go new file mode 100644 index 000000000..564b14bd8 --- /dev/null +++ b/src/test/e2e/optional_bundle_test.go @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023-Present The UDS Authors + +// Package test provides e2e tests for UDS. +package test + +import ( + "encoding/json" + "fmt" + "os" + "path/filepath" + "strings" + "testing" + + ocispec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/stretchr/testify/require" +) + +func TestBundleOptionalComponents(t *testing.T) { + deployZarfInit(t) + e2e.SetupDockerRegistry(t, 888) + defer e2e.TeardownRegistry(t, 888) + + // create 2 Zarf pkgs to be bundled + zarfPkgPath := "src/test/packages/podinfo-and-nginx" + e2e.CreateZarfPkg(t, zarfPkgPath, false) + + zarfPkgPath = "src/test/packages/prometheus" + pkg := filepath.Join(zarfPkgPath, fmt.Sprintf("zarf-package-prometheus-%s-0.0.1.tar.zst", e2e.Arch)) + e2e.CreateZarfPkg(t, zarfPkgPath, false) + zarfPublish(t, pkg, "localhost:888") + + // create bundle and publish + bundleDir := "src/test/bundles/14-optional-components" + bundleName := "optional-components" + bundleTarballName := fmt.Sprintf("uds-bundle-%s-%s-0.0.1.tar.zst", bundleName, e2e.Arch) + bundlePath := filepath.Join(bundleDir, bundleTarballName) + createLocal(t, bundleDir, e2e.Arch) + publishInsecure(t, bundlePath, "localhost:888") + + t.Run("look through contents of local bundle to ensure only selected components are present", func(t *testing.T) { + // local pkgs will have a correct pkg manifest (ie. missing non-selected optional component tarballs) + // remote pkgs will not, they will contain non-selected optional component tarballs + // because they already have a pkg manifest and we don't want to rewrite it + introspectOptionalComponentsBundle(t) + }) + + t.Run("test local deploy", func(t *testing.T) { + deploy(t, bundlePath) + remove(t, bundlePath) + }) + + t.Run("test remote deploy + pulled deploy", func(t *testing.T) { + pulledBundlePath := filepath.Join("build", bundleTarballName) + pull(t, fmt.Sprintf("localhost:888/%s:0.0.1", bundleName), bundleTarballName) + deployAndRemoveLocalAndRemoteInsecure(t, fmt.Sprintf("oci://localhost:888/%s:0.0.1", bundleName), pulledBundlePath) + }) +} + +// introspectOptionalComponentsBundle is a helper function that decompresses a bundle tarball and introspects the contents +// (has hardcoded checks meant for only the bundle in 14-optinal-components) +func introspectOptionalComponentsBundle(t *testing.T) { + // ensure a decompressed bundle doesn't already exist + decompressionLoc := "build/decompressed-bundle" + err := os.RemoveAll(decompressionLoc) + if err != nil { + return + } + defer e2e.CleanFiles(decompressionLoc) + + // decompress the bundle + bundlePath := fmt.Sprintf("src/test/bundles/14-optional-components/uds-bundle-optional-components-%s-0.0.1.tar.zst", e2e.Arch) + cmd := []string{"zarf", "tools", "archiver", "decompress", bundlePath, decompressionLoc} + _, _, err = e2e.UDS(cmd...) + require.NoError(t, err) + + // read in the bundle's index.json + index := ocispec.Index{} + bundleIndexBytes, err := os.ReadFile(filepath.Join(decompressionLoc, "index.json")) + require.NoError(t, err) + err = json.Unmarshal(bundleIndexBytes, &index) + require.NoError(t, err) + require.Equal(t, 1, len(index.Manifests)) + blobsDir := filepath.Join(decompressionLoc, "blobs", "sha256") + + // grab the bundle root manifest + rootManifesBytes, err := os.ReadFile(filepath.Join(blobsDir, index.Manifests[0].Digest.Encoded())) + require.NoError(t, err) + bundleRootManifest := ocispec.Manifest{} + err = json.Unmarshal(rootManifesBytes, &bundleRootManifest) + require.NoError(t, err) + + // grab the first pkg (note that it came from a remote source) + pkgManifestBytes, err := os.ReadFile(filepath.Join(blobsDir, bundleRootManifest.Layers[0].Digest.Encoded())) + require.NoError(t, err) + remotePkgManifest := ocispec.Manifest{} + err = json.Unmarshal(pkgManifestBytes, &remotePkgManifest) + require.NoError(t, err) + + // ensure kiwix not present in bundle bc we didn't specify its component in the optional components + ensureImgNotPresent(t, "ghcr.io/kiwix/kiwix-serve", remotePkgManifest, blobsDir) + + // for this remote pkg, ensure component tars exist in img manifest, but not in the bundle + componentName := "optional-kiwix" + verifyComponentNotIncluded := false + for _, desc := range remotePkgManifest.Layers { + if strings.Contains(desc.Annotations[ocispec.AnnotationTitle], fmt.Sprintf("components/%s.tar", componentName)) { + _, err = os.ReadFile(filepath.Join(blobsDir, desc.Digest.Encoded())) + require.ErrorContains(t, err, "no such file or directory") + verifyComponentNotIncluded = true + } + } + require.True(t, verifyComponentNotIncluded) + + // grab the second pkg (note that it came from a local source) + pkgManifestBytes, err = os.ReadFile(filepath.Join(blobsDir, bundleRootManifest.Layers[1].Digest.Encoded())) + require.NoError(t, err) + localPkgManifest := ocispec.Manifest{} + err = json.Unmarshal(pkgManifestBytes, &localPkgManifest) + require.NoError(t, err) + + // ensure nginx not present in bundle bc we didn't specify its component in the optional components + ensureImgNotPresent(t, "docker.io/library/nginx", localPkgManifest, blobsDir) + + // for this local pkg, ensure component tars DO NOT exist in img manifest + componentName = "nginx-remote" + verifyComponentNotIncluded = true + for _, desc := range localPkgManifest.Layers { + if strings.Contains(desc.Annotations[ocispec.AnnotationTitle], fmt.Sprintf("components/%s.tar", componentName)) { + // component shouldn't exist in pkg manifest for locally sourced pkgs + verifyComponentNotIncluded = false + } + } + require.True(t, verifyComponentNotIncluded) +} + +func ensureImgNotPresent(t *testing.T, imgName string, remotePkgManifest ocispec.Manifest, blobsDir string) { + // used to verify that the kiwix img is not included in the bundle (note that kiwix is intentionally excluded!) + verifyImgNotIncluded := false + + // grab image index from pkg root manifest + var imgIndex ocispec.Index + for _, layer := range remotePkgManifest.Layers { + if layer.Annotations[ocispec.AnnotationTitle] == "images/index.json" { + imgIndexBytes, err := os.ReadFile(filepath.Join(blobsDir, layer.Digest.Encoded())) + require.NoError(t, err) + err = json.Unmarshal(imgIndexBytes, &imgIndex) + require.NoError(t, err) + + // ensure specified img exists in the img index but isn't actually included in the bundle + for _, desc := range imgIndex.Manifests { + if strings.Contains(desc.Annotations[ocispec.AnnotationBaseImageName], imgName) { + _, err = os.ReadFile(filepath.Join(blobsDir, desc.Digest.Encoded())) + require.ErrorContains(t, err, "no such file or directory") + verifyImgNotIncluded = true + break + } + } + break + } + } + require.True(t, verifyImgNotIncluded) +} diff --git a/src/test/packages/prometheus/zarf.yaml b/src/test/packages/prometheus/zarf.yaml index 2acbeeb99..fe4abddb0 100644 --- a/src/test/packages/prometheus/zarf.yaml +++ b/src/test/packages/prometheus/zarf.yaml @@ -11,6 +11,17 @@ components: import: path: images name: upload + - name: optional-kiwix + description: used to test optional component images (not actually bundled) + charts: + # random helm chart that isn't important; just used to test that it doesn't get bundled + - name: prometheus-node-exporter + url: https://prometheus-community.github.io/helm-charts + version: 4.32.0 + namespace: prometheus + images: + # again, not bundled, we just need a unique name to test that it doesn't get bundled + - ghcr.io/kiwix/kiwix-serve:3.7.0 - name: deploy required: true charts: