Skip to content

Commit e71a9b4

Browse files
fix: move image archive validation to assemble (#4490)
Signed-off-by: Austin Abro <austinabro321@gmail.com>
1 parent a42e3d0 commit e71a9b4

File tree

4 files changed

+205
-205
lines changed

4 files changed

+205
-205
lines changed

src/pkg/lint/validate.go

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"strings"
1212

1313
"github.com/zarf-dev/zarf/src/api/v1alpha1"
14-
"github.com/zarf-dev/zarf/src/pkg/transform"
1514
"k8s.io/apimachinery/pkg/util/validation"
1615
)
1716

@@ -159,10 +158,6 @@ func ValidatePackage(pkg v1alpha1.ZarfPackage) error {
159158
}
160159
}
161160

162-
if archiveErr := validateImageArchivesNoDuplicates(pkg.Components); archiveErr != nil {
163-
err = errors.Join(err, archiveErr)
164-
}
165-
166161
return err
167162
}
168163

@@ -340,43 +335,3 @@ func validateManifest(manifest v1alpha1.ZarfManifest) error {
340335

341336
return err
342337
}
343-
344-
// validateImageArchivesNoDuplicates ensures no image appears in multiple image archives
345-
// and that images in image archives don't conflict with images in component.Images.
346-
func validateImageArchivesNoDuplicates(components []v1alpha1.ZarfComponent) error {
347-
imageToArchive := make(map[string]string)
348-
349-
for _, comp := range components {
350-
for _, archive := range comp.ImageArchives {
351-
for _, image := range archive.Images {
352-
refInfo, err := transform.ParseImageRef(image)
353-
if err != nil {
354-
return fmt.Errorf("failed to parse image ref %s in archive %s: %w", image, archive.Path, err)
355-
}
356-
357-
if existingArchivePath, exists := imageToArchive[refInfo.Reference]; exists {
358-
// A user may want to represent the same tar twice across components if both components need the same image
359-
if existingArchivePath != archive.Path {
360-
return fmt.Errorf("image %s appears in multiple image archives: %s and %s", refInfo.Reference, existingArchivePath, archive.Path)
361-
}
362-
} else {
363-
imageToArchive[refInfo.Reference] = archive.Path
364-
}
365-
}
366-
}
367-
}
368-
369-
for _, comp := range components {
370-
for _, image := range comp.Images {
371-
refInfo, err := transform.ParseImageRef(image)
372-
if err != nil {
373-
return fmt.Errorf("failed to parse image ref %s in component %s: %w", image, comp.Name, err)
374-
}
375-
if archivePath, exists := imageToArchive[refInfo.Reference]; exists {
376-
return fmt.Errorf("image %s from %s is also pulled by component %s", refInfo.Reference, archivePath, comp.Name)
377-
}
378-
}
379-
}
380-
381-
return nil
382-
}

src/pkg/lint/validate_test.go

Lines changed: 0 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -493,163 +493,3 @@ func TestValidateComponentAction(t *testing.T) {
493493
})
494494
}
495495
}
496-
497-
func TestValidateImageArchivesNoDuplicates(t *testing.T) {
498-
t.Parallel()
499-
500-
tests := []struct {
501-
name string
502-
components []v1alpha1.ZarfComponent
503-
errorContains string
504-
}{
505-
{
506-
name: "no duplicates",
507-
components: []v1alpha1.ZarfComponent{
508-
{
509-
Name: "component1",
510-
ImageArchives: []v1alpha1.ImageArchive{
511-
{
512-
Path: "/path/to/archive1.tar",
513-
Images: []string{"nginx:1.21"},
514-
},
515-
},
516-
Images: []string{"redis:6.2"},
517-
},
518-
},
519-
},
520-
{
521-
name: "duplicate in different image archive",
522-
components: []v1alpha1.ZarfComponent{
523-
{
524-
Name: "component1",
525-
ImageArchives: []v1alpha1.ImageArchive{
526-
{
527-
Path: "/path/to/archive1.tar",
528-
Images: []string{"postgres:13"},
529-
},
530-
{
531-
Path: "/path/to/archive2.tar",
532-
Images: []string{"postgres:13"},
533-
},
534-
},
535-
},
536-
},
537-
errorContains: "appears in multiple image archives",
538-
},
539-
{
540-
name: "duplicate in component images",
541-
components: []v1alpha1.ZarfComponent{
542-
{
543-
Name: "component1",
544-
ImageArchives: []v1alpha1.ImageArchive{
545-
{
546-
Path: "/path/to/archive1.tar",
547-
Images: []string{"ghcr.io/zarf-dev/zarf/agent:0.65.0"},
548-
},
549-
},
550-
Images: []string{"nginx:1.21", "ghcr.io/zarf-dev/zarf/agent:0.65.0"},
551-
},
552-
},
553-
errorContains: "is also pulled by component",
554-
},
555-
{
556-
name: "duplicate in component with docker ref",
557-
components: []v1alpha1.ZarfComponent{
558-
{
559-
Name: "component1",
560-
ImageArchives: []v1alpha1.ImageArchive{
561-
{
562-
Path: "/path/to/archive1.tar",
563-
Images: []string{"nginx:1.21"},
564-
},
565-
},
566-
Images: []string{"nginx:1.21"},
567-
},
568-
},
569-
errorContains: "is also pulled by component",
570-
},
571-
{
572-
name: "duplicate across multiple components",
573-
components: []v1alpha1.ZarfComponent{
574-
{
575-
Name: "component1",
576-
ImageArchives: []v1alpha1.ImageArchive{
577-
{
578-
Path: "/path/to/archive1.tar",
579-
Images: []string{"nginx:1.21"},
580-
},
581-
},
582-
},
583-
{
584-
Name: "component2",
585-
ImageArchives: []v1alpha1.ImageArchive{
586-
{
587-
Path: "/path/to/archive2.tar",
588-
Images: []string{"nginx:1.21"},
589-
},
590-
},
591-
},
592-
},
593-
errorContains: "appears in multiple image archives",
594-
},
595-
{
596-
name: "empty components",
597-
components: []v1alpha1.ZarfComponent{
598-
{
599-
Name: "component1",
600-
},
601-
},
602-
},
603-
{
604-
name: "duplicate images in component.Images is allowed",
605-
components: []v1alpha1.ZarfComponent{
606-
{
607-
Name: "component1",
608-
Images: []string{"nginx:1.21"},
609-
},
610-
{
611-
Name: "component2",
612-
Images: []string{"nginx:1.21"},
613-
},
614-
},
615-
},
616-
{
617-
name: "same archive path used by multiple components is allowed",
618-
components: []v1alpha1.ZarfComponent{
619-
{
620-
Name: "component1",
621-
ImageArchives: []v1alpha1.ImageArchive{
622-
{
623-
Path: "/path/to/shared-archive.tar",
624-
Images: []string{"nginx:1.21", "redis:6.2"},
625-
},
626-
},
627-
},
628-
{
629-
Name: "component2",
630-
ImageArchives: []v1alpha1.ImageArchive{
631-
{
632-
Path: "/path/to/shared-archive.tar",
633-
Images: []string{"nginx:1.21", "postgres:13"},
634-
},
635-
},
636-
},
637-
},
638-
},
639-
}
640-
641-
for _, tt := range tests {
642-
t.Run(tt.name, func(t *testing.T) {
643-
t.Parallel()
644-
645-
err := validateImageArchivesNoDuplicates(tt.components)
646-
647-
if tt.errorContains != "" {
648-
require.Error(t, err)
649-
require.Contains(t, err.Error(), tt.errorContains)
650-
} else {
651-
require.NoError(t, err)
652-
}
653-
})
654-
}
655-
}

src/pkg/packager/layout/assemble.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ func AssemblePackage(ctx context.Context, pkg v1alpha1.ZarfPackage, packagePath
6161
l := logger.From(ctx)
6262
l.Info("assembling package", "path", packagePath)
6363

64+
if err := validateImageArchivesNoDuplicates(pkg.Components); err != nil {
65+
return nil, err
66+
}
67+
6468
if opts.DifferentialPackage.Metadata.Name != "" {
6569
l.Debug("creating differential package", "differential", opts.DifferentialPackage)
6670
allIncludedImagesMap := map[string]bool{}
@@ -302,6 +306,46 @@ func AssembleSkeleton(ctx context.Context, pkg v1alpha1.ZarfPackage, packagePath
302306
return pkgLayout, nil
303307
}
304308

309+
// validateImageArchivesNoDuplicates ensures no image appears in multiple image archives
310+
// and that images in image archives don't conflict with images in component.Images.
311+
func validateImageArchivesNoDuplicates(components []v1alpha1.ZarfComponent) error {
312+
imageToArchive := make(map[string]string)
313+
314+
for _, comp := range components {
315+
for _, archive := range comp.ImageArchives {
316+
for _, image := range archive.Images {
317+
refInfo, err := transform.ParseImageRef(image)
318+
if err != nil {
319+
return fmt.Errorf("failed to parse image ref %s in archive %s: %w", image, archive.Path, err)
320+
}
321+
322+
if existingArchivePath, exists := imageToArchive[refInfo.Reference]; exists {
323+
// A user may want to represent the same tar twice across components if both components need the same image
324+
if existingArchivePath != archive.Path {
325+
return fmt.Errorf("image %s appears in multiple image archives: %s and %s", refInfo.Reference, existingArchivePath, archive.Path)
326+
}
327+
} else {
328+
imageToArchive[refInfo.Reference] = archive.Path
329+
}
330+
}
331+
}
332+
}
333+
334+
for _, comp := range components {
335+
for _, image := range comp.Images {
336+
refInfo, err := transform.ParseImageRef(image)
337+
if err != nil {
338+
return fmt.Errorf("failed to parse image ref %s in component %s: %w", image, comp.Name, err)
339+
}
340+
if archivePath, exists := imageToArchive[refInfo.Reference]; exists {
341+
return fmt.Errorf("image %s from %s is also pulled by component %s", refInfo.Reference, archivePath, comp.Name)
342+
}
343+
}
344+
}
345+
346+
return nil
347+
}
348+
305349
func assemblePackageComponent(ctx context.Context, component v1alpha1.ZarfComponent, packagePath, buildPath string) (err error) {
306350
tmpBuildPath, err := utils.MakeTempDir(config.CommonOptions.TempDirectory)
307351
if err != nil {

0 commit comments

Comments
 (0)