From 8ed431edf8f47afdc6a60ac3ecde977acf115581 Mon Sep 17 00:00:00 2001 From: Philip Laine Date: Wed, 7 Aug 2024 16:20:59 +0200 Subject: [PATCH] refactor: FindImages to return errors immediatly Signed-off-by: Philip Laine --- src/pkg/packager/prepare.go | 210 ++++++++---------- src/pkg/packager/prepare_test.go | 160 +++++++++++-- .../find-images/agent/deployment.yaml | 16 ++ .../testdata/find-images/agent/zarf.yaml | 12 + .../find-images/helm-chart/chart/.helmignore | 23 ++ .../find-images/helm-chart/chart/Chart.yaml | 24 ++ .../helm-chart/chart/templates/NOTES.txt | 22 ++ .../helm-chart/chart/templates/_helpers.tpl | 62 ++++++ .../chart/templates/deployment.yaml | 68 ++++++ .../helm-chart/chart/templates/hpa.yaml | 32 +++ .../helm-chart/chart/templates/ingress.yaml | 61 +++++ .../helm-chart/chart/templates/service.yaml | 15 ++ .../chart/templates/serviceaccount.yaml | 13 ++ .../templates/tests/test-connection.yaml | 15 ++ .../find-images/helm-chart/chart/values.yaml | 107 +++++++++ .../find-images/helm-chart/values.yaml | 1 + .../testdata/find-images/helm-chart/zarf.yaml | 18 ++ .../find-images/invalid-helm-repo/zarf.yaml | 9 + .../invalid-manifest-yaml/deployment.yaml | 17 ++ .../invalid-manifest-yaml/zarf.yaml | 12 + src/test/e2e/13_find_images_test.go | 3 +- 21 files changed, 769 insertions(+), 131 deletions(-) create mode 100644 src/pkg/packager/testdata/find-images/agent/deployment.yaml create mode 100644 src/pkg/packager/testdata/find-images/agent/zarf.yaml create mode 100644 src/pkg/packager/testdata/find-images/helm-chart/chart/.helmignore create mode 100644 src/pkg/packager/testdata/find-images/helm-chart/chart/Chart.yaml create mode 100644 src/pkg/packager/testdata/find-images/helm-chart/chart/templates/NOTES.txt create mode 100644 src/pkg/packager/testdata/find-images/helm-chart/chart/templates/_helpers.tpl create mode 100644 src/pkg/packager/testdata/find-images/helm-chart/chart/templates/deployment.yaml create mode 100644 src/pkg/packager/testdata/find-images/helm-chart/chart/templates/hpa.yaml create mode 100644 src/pkg/packager/testdata/find-images/helm-chart/chart/templates/ingress.yaml create mode 100644 src/pkg/packager/testdata/find-images/helm-chart/chart/templates/service.yaml create mode 100644 src/pkg/packager/testdata/find-images/helm-chart/chart/templates/serviceaccount.yaml create mode 100644 src/pkg/packager/testdata/find-images/helm-chart/chart/templates/tests/test-connection.yaml create mode 100644 src/pkg/packager/testdata/find-images/helm-chart/chart/values.yaml create mode 100644 src/pkg/packager/testdata/find-images/helm-chart/values.yaml create mode 100644 src/pkg/packager/testdata/find-images/helm-chart/zarf.yaml create mode 100644 src/pkg/packager/testdata/find-images/invalid-helm-repo/zarf.yaml create mode 100644 src/pkg/packager/testdata/find-images/invalid-manifest-yaml/deployment.yaml create mode 100644 src/pkg/packager/testdata/find-images/invalid-manifest-yaml/zarf.yaml diff --git a/src/pkg/packager/prepare.go b/src/pkg/packager/prepare.go index 0c1ef19f30..37b80f0ec4 100644 --- a/src/pkg/packager/prepare.go +++ b/src/pkg/packager/prepare.go @@ -35,8 +35,8 @@ import ( "github.com/zarf-dev/zarf/src/types" ) -// imageMap is a map of image/boolean pairs. -type imageMap map[string]bool +var imageCheck = regexp.MustCompile(`(?mi)"image":"([^"]+)"`) +var imageFuzzyCheck = regexp.MustCompile(`(?mi)["|=]([a-z0-9\-.\/:]+:[\w.\-]*[a-z\.\-][\w.\-]*)"`) // FindImages iterates over a Zarf.yaml and attempts to parse any images. func (p *Packager) FindImages(ctx context.Context) (map[string][]string, error) { @@ -65,43 +65,33 @@ func (p *Packager) FindImages(ctx context.Context) (map[string][]string, error) if err != nil { return nil, err } - p.cfg.Pkg = pkg - for _, warning := range warnings { message.Warn(warning) } + p.cfg.Pkg = pkg return p.findImages(ctx) } -func (p *Packager) findImages(ctx context.Context) (imgMap map[string][]string, err error) { - repoHelmChartPath := p.cfg.FindImagesOpts.RepoHelmChartPath - kubeVersionOverride := p.cfg.FindImagesOpts.KubeVersionOverride - whyImage := p.cfg.FindImagesOpts.Why - - imagesMap := make(map[string][]string) - erroredCharts := []string{} - erroredCosignLookups := []string{} - whyResources := []string{} - +// TODO: Refactor to return output string instead of printing inside of function. +func (p *Packager) findImages(ctx context.Context) (map[string][]string, error) { for _, component := range p.cfg.Pkg.Components { - if len(component.Repos) > 0 && repoHelmChartPath == "" { + if len(component.Repos) > 0 && p.cfg.FindImagesOpts.RepoHelmChartPath == "" { message.Note("This Zarf package contains git repositories, " + "if any repos contain helm charts you want to template and " + "search for images, make sure to specify the helm chart path " + "via the --repo-chart-path flag") + break } } - componentDefinition := "\ncomponents:\n" - if err := p.populatePackageVariableConfig(); err != nil { return nil, fmt.Errorf("unable to set the active variables: %w", err) } // Set default builtin values so they exist in case any helm charts rely on them registryInfo := types.RegistryInfo{Address: p.cfg.FindImagesOpts.RegistryURL} - err = registryInfo.FillInEmptyValues() + err := registryInfo.FillInEmptyValues() if err != nil { return nil, err } @@ -118,41 +108,34 @@ func (p *Packager) findImages(ctx context.Context) (imgMap map[string][]string, ArtifactServer: artifactServer, } + componentDefinition := "\ncomponents:\n" + imagesMap := map[string][]string{} + whyResources := []string{} for _, component := range p.cfg.Pkg.Components { if len(component.Charts)+len(component.Manifests)+len(component.Repos) < 1 { // Skip if it doesn't have what we need continue } - if repoHelmChartPath != "" { + if p.cfg.FindImagesOpts.RepoHelmChartPath != "" { // Also process git repos that have helm charts for _, repo := range component.Repos { matches := strings.Split(repo, "@") if len(matches) < 2 { - message.Warnf("Cannot convert git repo %s to helm chart without a version tag", repo) - continue + return nil, fmt.Errorf("cannot convert the Git repository %s to a Helm chart without a version tag", repo) } - // Trim the first char to match how the packager expects it, this is messy,need to clean up better - repoHelmChartPath = strings.TrimPrefix(repoHelmChartPath, "/") - // If a repo helm chart path is specified, component.Charts = append(component.Charts, v1alpha1.ZarfChart{ Name: repo, URL: matches[0], Version: matches[1], - GitPath: repoHelmChartPath, + // Trim the first char to match how the packager expects it, this is messy,need to clean up better + GitPath: strings.TrimPrefix(p.cfg.FindImagesOpts.RepoHelmChartPath, "/"), }) } } - // matchedImages holds the collection of images, reset per-component - matchedImages := make(imageMap) - maybeImages := make(imageMap) - - // resources are a slice of generic structs that represent parsed K8s resources - var resources []*unstructured.Unstructured - componentPaths, err := p.layout.Components.Create(component) if err != nil { return nil, err @@ -162,56 +145,61 @@ func (p *Packager) findImages(ctx context.Context) (imgMap map[string][]string, return nil, err } + resources := []*unstructured.Unstructured{} + matchedImages := map[string]bool{} + maybeImages := map[string]bool{} for _, chart := range component.Charts { + // Generate helm templates for this chart helmCfg := helm.New( chart, componentPaths.Charts, componentPaths.Values, - helm.WithKubeVersion(kubeVersionOverride), + helm.WithKubeVersion(p.cfg.FindImagesOpts.KubeVersionOverride), helm.WithVariableConfig(p.variableConfig), ) - err = helmCfg.PackageChart(ctx, component.DeprecatedCosignKeyPath) if err != nil { return nil, fmt.Errorf("unable to package the chart %s: %w", chart.Name, err) } - valuesFilePaths, _ := helpers.RecursiveFileList(componentPaths.Values, nil, false) + valuesFilePaths, err := helpers.RecursiveFileList(componentPaths.Values, nil, false) + // TODO: The values path should exist if the path is set, otherwise it should be empty. + if err != nil && !errors.Is(err, os.ErrNotExist) { + return nil, err + } for _, valueFilePath := range valuesFilePaths { - if err := p.variableConfig.ReplaceTextTemplate(valueFilePath); err != nil { + err := p.variableConfig.ReplaceTextTemplate(valueFilePath) + if err != nil { return nil, err } } - // Generate helm templates for this chart chartTemplate, chartValues, err := helmCfg.TemplateChart(ctx) if err != nil { - message.WarnErrf(err, "Problem rendering the helm template for %s: %s", chart.Name, err.Error()) - erroredCharts = append(erroredCharts, chart.Name) - continue + return nil, fmt.Errorf("could not render the Helm template for chart %s: %w", chart.Name, err) } // Break the template into separate resources - yamls, _ := utils.SplitYAML([]byte(chartTemplate)) + yamls, err := utils.SplitYAML([]byte(chartTemplate)) + if err != nil { + return nil, err + } resources = append(resources, yamls...) chartTarball := helm.StandardName(componentPaths.Charts, chart) + ".tgz" - annotatedImages, err := helm.FindAnnotatedImagesForChart(chartTarball, chartValues) if err != nil { - message.WarnErrf(err, "Problem looking for image annotations for %s: %s", chart.URL, err.Error()) - erroredCharts = append(erroredCharts, chart.URL) - continue + return nil, fmt.Errorf("could not look up image annotations for chart URL %s: %w", chart.URL, err) } for _, image := range annotatedImages { matchedImages[image] = true } // Check if the --why flag is set - if whyImage != "" { - whyResourcesChart, err := findWhyResources(yamls, whyImage, component.Name, chart.Name, true) + if p.cfg.FindImagesOpts.Why != "" { + whyResourcesChart, err := findWhyResources(yamls, p.cfg.FindImagesOpts.Why, component.Name, chart.Name, true) if err != nil { - message.WarnErrf(err, "Error finding why resources for chart %s: %s", chart.Name, err.Error()) + return nil, fmt.Errorf("could not determine why resource for the chart %s: %w", chart.Name, err) } whyResources = append(whyResources, whyResourcesChart...) } @@ -251,20 +239,22 @@ func (p *Packager) findImages(ctx context.Context) (imgMap map[string][]string, // Read the contents of each file contents, err := os.ReadFile(f) if err != nil { - message.WarnErrf(err, "Unable to read the file %s", f) - continue + return nil, fmt.Errorf("could not read the file %s: %w", f, err) } // Break the manifest into separate resources - // TODO: Do not dogsled error - yamls, _ := utils.SplitYAML(contents) + yamls, err := utils.SplitYAML(contents) + if err != nil { + fmt.Println("got this err") + return nil, err + } resources = append(resources, yamls...) // Check if the --why flag is set and if it is process the manifests - if whyImage != "" { - whyResourcesManifest, err := findWhyResources(yamls, whyImage, component.Name, manifest.Name, false) + if p.cfg.FindImagesOpts.Why != "" { + whyResourcesManifest, err := findWhyResources(yamls, p.cfg.FindImagesOpts.Why, component.Name, manifest.Name, false) if err != nil { - message.WarnErrf(err, "Error finding why resources for manifest %s: %s", manifest.Name, err.Error()) + return nil, fmt.Errorf("could not find why resources for manifest %s: %w", manifest.Name, err) } whyResources = append(whyResources, whyResourcesManifest...) } @@ -275,15 +265,17 @@ func (p *Packager) findImages(ctx context.Context) (imgMap map[string][]string, defer spinner.Stop() for _, resource := range resources { - if matchedImages, maybeImages, err = p.processUnstructuredImages(resource, matchedImages, maybeImages); err != nil { - message.WarnErrf(err, "Problem processing K8s resource %s", resource.GetName()) + if matchedImages, maybeImages, err = processUnstructuredImages(resource, matchedImages, maybeImages); err != nil { + return nil, fmt.Errorf("could not process the Kubernetes resource %s: %w", resource.GetName(), err) } } - if sortedImages := sortImages(matchedImages, nil); len(sortedImages) > 0 { + sortedMatchedImages, sortedExpectedImages := getSortedImages(matchedImages, maybeImages) + + if len(sortedMatchedImages) > 0 { // Log the header comment componentDefinition += fmt.Sprintf("\n - name: %s\n images:\n", component.Name) - for _, image := range sortedImages { + for _, image := range sortedMatchedImages { // Use print because we want this dumped to stdout imagesMap[component.Name] = append(imagesMap[component.Name], image) componentDefinition += fmt.Sprintf(" - %s\n", image) @@ -291,9 +283,9 @@ func (p *Packager) findImages(ctx context.Context) (imgMap map[string][]string, } // Handle the "maybes" - if sortedImages := sortImages(maybeImages, matchedImages); len(sortedImages) > 0 { + if len(sortedExpectedImages) > 0 { var validImages []string - for _, image := range sortedImages { + for _, image := range sortedExpectedImages { if descriptor, err := crane.Head(image, images.WithGlobalInsecureFlag()...); err != nil { // Test if this is a real image, if not just quiet log to debug, this is normal message.Debugf("Suspected image does not appear to be valid: %#v", err) @@ -326,8 +318,7 @@ func (p *Packager) findImages(ctx context.Context) (imgMap map[string][]string, spinner.Updatef("Looking up cosign artifacts for discovered images (%d/%d)", idx+1, len(imagesMap[component.Name])) cosignArtifacts, err := utils.GetCosignArtifacts(image) if err != nil { - message.WarnErrf(err, "Problem looking up cosign artifacts for %s: %s", image, err.Error()) - erroredCosignLookups = append(erroredCosignLookups, image) + return nil, fmt.Errorf("could not lookup the cosing artifacts for image %s: %w", image, err) } cosignArtifactList = append(cosignArtifactList, cosignArtifacts...) } @@ -345,80 +336,64 @@ func (p *Packager) findImages(ctx context.Context) (imgMap map[string][]string, } } - if whyImage != "" { + if p.cfg.FindImagesOpts.Why != "" { if len(whyResources) == 0 { - message.Warnf("image %q not found in any charts or manifests", whyImage) + return nil, fmt.Errorf("image %s not found in any charts or manifests", p.cfg.FindImagesOpts.Why) } return nil, nil } fmt.Println(componentDefinition) - if len(erroredCharts) > 0 || len(erroredCosignLookups) > 0 { - errMsg := "" - if len(erroredCharts) > 0 { - errMsg = fmt.Sprintf("the following charts had errors: %s", erroredCharts) - } - if len(erroredCosignLookups) > 0 { - if errMsg != "" { - errMsg += "\n" - } - errMsg += fmt.Sprintf("the following images errored on cosign lookups: %s", erroredCosignLookups) - } - return imagesMap, errors.New(errMsg) - } - return imagesMap, nil } -func (p *Packager) processUnstructuredImages(resource *unstructured.Unstructured, matchedImages, maybeImages imageMap) (imageMap, imageMap, error) { - var imageSanityCheck = regexp.MustCompile(`(?mi)"image":"([^"]+)"`) - var imageFuzzyCheck = regexp.MustCompile(`(?mi)["|=]([a-z0-9\-.\/:]+:[\w.\-]*[a-z\.\-][\w.\-]*)"`) - var json string - +func processUnstructuredImages(resource *unstructured.Unstructured, matchedImages, maybeImages map[string]bool) (map[string]bool, map[string]bool, error) { contents := resource.UnstructuredContent() - bytes, _ := resource.MarshalJSON() - json = string(bytes) + b, err := resource.MarshalJSON() + if err != nil { + return nil, nil, err + } switch resource.GetKind() { case "Deployment": var deployment v1.Deployment if err := runtime.DefaultUnstructuredConverter.FromUnstructured(contents, &deployment); err != nil { - return matchedImages, maybeImages, fmt.Errorf("could not parse deployment: %w", err) + return nil, nil, fmt.Errorf("could not parse deployment: %w", err) } - matchedImages = buildImageMap(matchedImages, deployment.Spec.Template.Spec) + matchedImages = appendToImageMap(matchedImages, deployment.Spec.Template.Spec) case "DaemonSet": var daemonSet v1.DaemonSet if err := runtime.DefaultUnstructuredConverter.FromUnstructured(contents, &daemonSet); err != nil { - return matchedImages, maybeImages, fmt.Errorf("could not parse daemonset: %w", err) + return nil, nil, fmt.Errorf("could not parse daemonset: %w", err) } - matchedImages = buildImageMap(matchedImages, daemonSet.Spec.Template.Spec) + matchedImages = appendToImageMap(matchedImages, daemonSet.Spec.Template.Spec) case "StatefulSet": var statefulSet v1.StatefulSet if err := runtime.DefaultUnstructuredConverter.FromUnstructured(contents, &statefulSet); err != nil { - return matchedImages, maybeImages, fmt.Errorf("could not parse statefulset: %w", err) + return nil, nil, fmt.Errorf("could not parse statefulset: %w", err) } - matchedImages = buildImageMap(matchedImages, statefulSet.Spec.Template.Spec) + matchedImages = appendToImageMap(matchedImages, statefulSet.Spec.Template.Spec) case "ReplicaSet": var replicaSet v1.ReplicaSet if err := runtime.DefaultUnstructuredConverter.FromUnstructured(contents, &replicaSet); err != nil { - return matchedImages, maybeImages, fmt.Errorf("could not parse replicaset: %w", err) + return nil, nil, fmt.Errorf("could not parse replicaset: %w", err) } - matchedImages = buildImageMap(matchedImages, replicaSet.Spec.Template.Spec) + matchedImages = appendToImageMap(matchedImages, replicaSet.Spec.Template.Spec) case "Job": var job batchv1.Job if err := runtime.DefaultUnstructuredConverter.FromUnstructured(contents, &job); err != nil { - return matchedImages, maybeImages, fmt.Errorf("could not parse job: %w", err) + return nil, nil, fmt.Errorf("could not parse job: %w", err) } - matchedImages = buildImageMap(matchedImages, job.Spec.Template.Spec) + matchedImages = appendToImageMap(matchedImages, job.Spec.Template.Spec) default: // Capture any custom images - matches := imageSanityCheck.FindAllStringSubmatch(json, -1) + matches := imageCheck.FindAllStringSubmatch(string(b), -1) for _, group := range matches { message.Debugf("Found unknown match, Kind: %s, Value: %s", resource.GetKind(), group[1]) matchedImages[group[1]] = true @@ -426,7 +401,7 @@ func (p *Packager) processUnstructuredImages(resource *unstructured.Unstructured } // Capture "maybe images" too for all kinds because they might be in unexpected places.... 👀 - matches := imageFuzzyCheck.FindAllStringSubmatch(json, -1) + matches := imageFuzzyCheck.FindAllStringSubmatch(string(b), -1) for _, group := range matches { message.Debugf("Found possible fuzzy match, Kind: %s, Value: %s", resource.GetKind(), group[1]) maybeImages[group[1]] = true @@ -438,11 +413,11 @@ func (p *Packager) processUnstructuredImages(resource *unstructured.Unstructured func findWhyResources(resources []*unstructured.Unstructured, whyImage, componentName, resourceName string, isChart bool) ([]string, error) { foundWhyResources := []string{} for _, resource := range resources { - bytes, err := yaml.Marshal(resource.Object) + b, err := yaml.Marshal(resource.Object) if err != nil { return nil, err } - yaml := string(bytes) + yaml := string(b) resourceTypeKey := "manifest" if isChart { resourceTypeKey = "chart" @@ -456,29 +431,34 @@ func findWhyResources(resources []*unstructured.Unstructured, whyImage, componen return foundWhyResources, nil } -// BuildImageMap looks for init container, ephemeral and regular container images. -func buildImageMap(images imageMap, pod corev1.PodSpec) imageMap { +func appendToImageMap(imgMap map[string]bool, pod corev1.PodSpec) map[string]bool { for _, container := range pod.InitContainers { - images[container.Image] = true + imgMap[container.Image] = true } for _, container := range pod.Containers { - images[container.Image] = true + imgMap[container.Image] = true } for _, container := range pod.EphemeralContainers { - images[container.Image] = true + imgMap[container.Image] = true } - return images + return imgMap } -// SortImages returns a sorted list of images. -func sortImages(images, compareWith imageMap) []string { - sortedImages := sort.StringSlice{} - for image := range images { - if !compareWith[image] || compareWith == nil { - // Check compareWith, if it exists only add if not in that list. - sortedImages = append(sortedImages, image) +func getSortedImages(matchedImages map[string]bool, maybeImages map[string]bool) ([]string, []string) { + sortedMatchedImages := sort.StringSlice{} + for image := range matchedImages { + sortedMatchedImages = append(sortedMatchedImages, image) + } + sort.Sort(sortedMatchedImages) + + sortedMaybeImages := sort.StringSlice{} + for image := range maybeImages { + if matchedImages[image] { + continue } + sortedMaybeImages = append(sortedMaybeImages, image) } - sort.Sort(sortedImages) - return sortedImages + sort.Sort(sortedMaybeImages) + + return sortedMatchedImages, sortedMaybeImages } diff --git a/src/pkg/packager/prepare_test.go b/src/pkg/packager/prepare_test.go index 8c43e194ac..f2fb760534 100644 --- a/src/pkg/packager/prepare_test.go +++ b/src/pkg/packager/prepare_test.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" "github.com/zarf-dev/zarf/src/pkg/lint" "github.com/zarf-dev/zarf/src/test/testutil" @@ -20,23 +21,154 @@ func TestFindImages(t *testing.T) { lint.ZarfSchema = testutil.LoadSchema(t, "../../../zarf.schema.json") - cfg := &types.PackagerConfig{ - CreateOpts: types.ZarfCreateOptions{ - BaseDir: "../../../examples/dos-games/", + tests := []struct { + name string + cfg *types.PackagerConfig + expectedErr string + expectedImages map[string][]string + }{ + { + name: "agent deployment", + cfg: &types.PackagerConfig{ + CreateOpts: types.ZarfCreateOptions{ + BaseDir: "./testdata/find-images/agent", + }, + }, + expectedImages: map[string][]string{ + "baseline": { + "ghcr.io/zarf-dev/zarf/agent:v0.38.1", + "ghcr.io/zarf-dev/zarf/agent:sha256-f8b1c2f99349516ae1bd0711a19697abcc41555076b0ae90f1a70ca6b50dcbd8.sig", + }, + }, }, + { + name: "helm chart", + cfg: &types.PackagerConfig{ + CreateOpts: types.ZarfCreateOptions{ + BaseDir: "./testdata/find-images/helm-chart", + }, + }, + expectedImages: map[string][]string{ + "baseline": { + "nginx:1.16.0", + "busybox", + }, + }, + }, + { + name: "image not found", + cfg: &types.PackagerConfig{ + CreateOpts: types.ZarfCreateOptions{ + BaseDir: "./testdata/find-images/agent", + }, + FindImagesOpts: types.ZarfFindImagesOptions{ + Why: "foobar", + }, + }, + expectedErr: "image foobar not found in any charts or manifests", + }, + { + name: "invalid helm repository", + cfg: &types.PackagerConfig{ + CreateOpts: types.ZarfCreateOptions{ + BaseDir: "./testdata/find-images/invalid-helm-repo", + }, + FindImagesOpts: types.ZarfFindImagesOptions{ + RepoHelmChartPath: "test", + }, + }, + expectedErr: "cannot convert the Git repository https://github.com/zarf-dev/zarf-public-test.git to a Helm chart without a version tag", + }, + { + name: "invalid manifest yaml", + cfg: &types.PackagerConfig{ + CreateOpts: types.ZarfCreateOptions{ + BaseDir: "./testdata/find-images/invalid-manifest-yaml", + }, + }, + expectedErr: "failed to unmarshal manifest: error converting YAML to JSON: yaml: line 12: could not find expected ':'", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p, err := New(tt.cfg) + require.NoError(t, err) + images, err := p.FindImages(ctx) + if tt.expectedErr != "" { + require.EqualError(t, err, tt.expectedErr) + return + } + require.NoError(t, err) + require.Equal(t, len(tt.expectedImages), len(images)) + for k, v := range tt.expectedImages { + require.ElementsMatch(t, v, images[k]) + } + }) } - p, err := New(cfg) - require.NoError(t, err) - images, err := p.FindImages(ctx) - require.NoError(t, err) - expectedImages := map[string][]string{ - "baseline": { - "ghcr.io/zarf-dev/doom-game:0.0.1", - "ghcr.io/zarf-dev/doom-game:sha256-7464ecc8a7172fce5c2ad631fc2a1b8572c686f4bf15c4bd51d7d6c9f0c460a7.sig", +} + +func TestBuildImageMap(t *testing.T) { + t.Parallel() + + podSpec := corev1.PodSpec{ + InitContainers: []corev1.Container{ + { + Image: "init-image", + }, + { + Image: "duplicate-image", + }, }, + Containers: []corev1.Container{ + + { + Image: "container-image", + }, + { + Image: "alpine:latest", + }, + }, + EphemeralContainers: []corev1.EphemeralContainer{ + { + EphemeralContainerCommon: corev1.EphemeralContainerCommon{ + Image: "ephemeral-image", + }, + }, + { + EphemeralContainerCommon: corev1.EphemeralContainerCommon{ + Image: "duplicate-image", + }, + }, + }, + } + imgMap := appendToImageMap(map[string]bool{}, podSpec) + expectedImgMap := map[string]bool{ + "init-image": true, + "duplicate-image": true, + "container-image": true, + "alpine:latest": true, + "ephemeral-image": true, + } + require.Equal(t, expectedImgMap, imgMap) +} + +func TestGetSortedImages(t *testing.T) { + t.Parallel() + + matchedImages := map[string]bool{ + "C": true, + "A": true, + "E": true, + "D": true, } - require.Equal(t, len(expectedImages), len(images)) - for k, v := range expectedImages { - require.ElementsMatch(t, v, images[k]) + maybeImages := map[string]bool{ + "Z": true, + "A": true, + "B": true, } + sortedMatchedImages, sortedMaybeImages := getSortedImages(matchedImages, maybeImages) + expectedSortedMatchedImages := []string{"A", "C", "D", "E"} + require.Equal(t, expectedSortedMatchedImages, sortedMatchedImages) + expectedSortedMaybeImages := []string{"B", "Z"} + require.Equal(t, expectedSortedMaybeImages, sortedMaybeImages) } diff --git a/src/pkg/packager/testdata/find-images/agent/deployment.yaml b/src/pkg/packager/testdata/find-images/agent/deployment.yaml new file mode 100644 index 0000000000..d307843973 --- /dev/null +++ b/src/pkg/packager/testdata/find-images/agent/deployment.yaml @@ -0,0 +1,16 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: agent +spec: + selector: + matchLabels: + app: agent + template: + metadata: + labels: + app: agent + spec: + containers: + - name: agent + image: ghcr.io/zarf-dev/zarf/agent:v0.38.1 diff --git a/src/pkg/packager/testdata/find-images/agent/zarf.yaml b/src/pkg/packager/testdata/find-images/agent/zarf.yaml new file mode 100644 index 0000000000..1ac8c9ea82 --- /dev/null +++ b/src/pkg/packager/testdata/find-images/agent/zarf.yaml @@ -0,0 +1,12 @@ +kind: ZarfPackageConfig +metadata: + name: agent + version: 1.0.0 +components: + - name: baseline + required: true + manifests: + - name: agent + namespace: default + files: + - deployment.yaml diff --git a/src/pkg/packager/testdata/find-images/helm-chart/chart/.helmignore b/src/pkg/packager/testdata/find-images/helm-chart/chart/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/src/pkg/packager/testdata/find-images/helm-chart/chart/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/src/pkg/packager/testdata/find-images/helm-chart/chart/Chart.yaml b/src/pkg/packager/testdata/find-images/helm-chart/chart/Chart.yaml new file mode 100644 index 0000000000..df2d97f9b0 --- /dev/null +++ b/src/pkg/packager/testdata/find-images/helm-chart/chart/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: chart +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/NOTES.txt b/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/NOTES.txt new file mode 100644 index 0000000000..b97199377a --- /dev/null +++ b/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "chart.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch its status by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "chart.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "chart.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "chart.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/_helpers.tpl b/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/_helpers.tpl new file mode 100644 index 0000000000..7ba5edc272 --- /dev/null +++ b/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "chart.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "chart.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "chart.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "chart.labels" -}} +helm.sh/chart: {{ include "chart.chart" . }} +{{ include "chart.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "chart.selectorLabels" -}} +app.kubernetes.io/name: {{ include "chart.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "chart.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "chart.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/deployment.yaml b/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/deployment.yaml new file mode 100644 index 0000000000..9fa1767865 --- /dev/null +++ b/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/deployment.yaml @@ -0,0 +1,68 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "chart.fullname" . }} + labels: + {{- include "chart.labels" . | nindent 4 }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "chart.selectorLabels" . | nindent 6 }} + template: + metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "chart.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "chart.serviceAccountName" . }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + containers: + - name: {{ .Chart.Name }} + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + livenessProbe: + {{- toYaml .Values.livenessProbe | nindent 12 }} + readinessProbe: + {{- toYaml .Values.readinessProbe | nindent 12 }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.volumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/hpa.yaml b/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/hpa.yaml new file mode 100644 index 0000000000..a91f61bd5c --- /dev/null +++ b/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/hpa.yaml @@ -0,0 +1,32 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "chart.fullname" . }} + labels: + {{- include "chart.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "chart.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/ingress.yaml b/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/ingress.yaml new file mode 100644 index 0000000000..63c1311c95 --- /dev/null +++ b/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "chart.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "chart.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/service.yaml b/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/service.yaml new file mode 100644 index 0000000000..dfc5b3a33d --- /dev/null +++ b/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "chart.fullname" . }} + labels: + {{- include "chart.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "chart.selectorLabels" . | nindent 4 }} diff --git a/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/serviceaccount.yaml b/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/serviceaccount.yaml new file mode 100644 index 0000000000..1df935010a --- /dev/null +++ b/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "chart.serviceAccountName" . }} + labels: + {{- include "chart.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/tests/test-connection.yaml b/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/tests/test-connection.yaml new file mode 100644 index 0000000000..8dfed872de --- /dev/null +++ b/src/pkg/packager/testdata/find-images/helm-chart/chart/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "chart.fullname" . }}-test-connection" + labels: + {{- include "chart.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "chart.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/src/pkg/packager/testdata/find-images/helm-chart/chart/values.yaml b/src/pkg/packager/testdata/find-images/helm-chart/chart/values.yaml new file mode 100644 index 0000000000..4d7ead8785 --- /dev/null +++ b/src/pkg/packager/testdata/find-images/helm-chart/chart/values.yaml @@ -0,0 +1,107 @@ +# Default values for chart. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: nginx + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Automatically mount a ServiceAccount's API credentials? + automount: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} +podLabels: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +livenessProbe: + httpGet: + path: / + port: http +readinessProbe: + httpGet: + path: / + port: http + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +# Additional volumes on the output Deployment definition. +volumes: [] +# - name: foo +# secret: +# secretName: mysecret +# optional: false + +# Additional volumeMounts on the output Deployment definition. +volumeMounts: [] +# - name: foo +# mountPath: "/etc/foo" +# readOnly: true + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/src/pkg/packager/testdata/find-images/helm-chart/values.yaml b/src/pkg/packager/testdata/find-images/helm-chart/values.yaml new file mode 100644 index 0000000000..6bc803e90f --- /dev/null +++ b/src/pkg/packager/testdata/find-images/helm-chart/values.yaml @@ -0,0 +1 @@ +replicaCount: 3 diff --git a/src/pkg/packager/testdata/find-images/helm-chart/zarf.yaml b/src/pkg/packager/testdata/find-images/helm-chart/zarf.yaml new file mode 100644 index 0000000000..1e33cb9515 --- /dev/null +++ b/src/pkg/packager/testdata/find-images/helm-chart/zarf.yaml @@ -0,0 +1,18 @@ +kind: ZarfPackageConfig +metadata: + name: helm-chart + version: 1.0.0 +components: + - name: baseline + required: true + charts: + - name: with-values + version: 0.1.0 + namespace: with-values + localPath: chart + valuesFiles: + - values.yaml + - name: without-values + version: 0.1.0 + namespace: without-values + localPath: chart diff --git a/src/pkg/packager/testdata/find-images/invalid-helm-repo/zarf.yaml b/src/pkg/packager/testdata/find-images/invalid-helm-repo/zarf.yaml new file mode 100644 index 0000000000..71fd539d3f --- /dev/null +++ b/src/pkg/packager/testdata/find-images/invalid-helm-repo/zarf.yaml @@ -0,0 +1,9 @@ +kind: ZarfPackageConfig +metadata: + name: invalid-helm-repo + version: 1.0.0 +components: + - name: baseline + required: true + repos: + - https://github.com/zarf-dev/zarf-public-test.git diff --git a/src/pkg/packager/testdata/find-images/invalid-manifest-yaml/deployment.yaml b/src/pkg/packager/testdata/find-images/invalid-manifest-yaml/deployment.yaml new file mode 100644 index 0000000000..3e36e465cc --- /dev/null +++ b/src/pkg/packager/testdata/find-images/invalid-manifest-yaml/deployment.yaml @@ -0,0 +1,17 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: agent +spec: + selector: + matchLabels: + app: agent + template: + metadata: +asdasd + labels: + app: agent + spec: + containers: + - name: agent + image: ghcr.io/zarf-dev/zarf/agent:v0.38.1 diff --git a/src/pkg/packager/testdata/find-images/invalid-manifest-yaml/zarf.yaml b/src/pkg/packager/testdata/find-images/invalid-manifest-yaml/zarf.yaml new file mode 100644 index 0000000000..cd8390489e --- /dev/null +++ b/src/pkg/packager/testdata/find-images/invalid-manifest-yaml/zarf.yaml @@ -0,0 +1,12 @@ +kind: ZarfPackageConfig +metadata: + name: invalid-manifest-yaml + version: 1.0.0 +components: + - name: baseline + required: true + manifests: + - name: agent + namespace: default + files: + - deployment.yaml diff --git a/src/test/e2e/13_find_images_test.go b/src/test/e2e/13_find_images_test.go index 39f6691b16..6d01bdd569 100644 --- a/src/test/e2e/13_find_images_test.go +++ b/src/test/e2e/13_find_images_test.go @@ -42,8 +42,7 @@ func TestFindImages(t *testing.T) { // Test `zarf prepare find-images` with `--kube-version` specified and less than than the declared minimum (v1.21.0) stdOut, stdErr, err = e2e.Zarf(t, "prepare", "find-images", "--kube-version=v1.20.0", "src/test/packages/00-kube-version-override") require.Error(t, err, stdOut, stdErr) - require.Contains(t, stdErr, "Problem rendering the helm template for cert-manager", "The kubeVersion declaration should prevent this from templating") - require.Contains(t, stdErr, "following charts had errors: [cert-manager]", "Zarf should print an ending error message") + require.Contains(t, stdErr, "could not render the Helm template for chart cert-manager", "The kubeVersion declaration should prevent this from templating") }) t.Run("zarf dev find-images with helm or manifest vars", func(t *testing.T) {