diff --git a/src/internal/packager2/layout/create.go b/src/internal/packager2/layout/create.go index 960e4a70e2..237d48cce3 100644 --- a/src/internal/packager2/layout/create.go +++ b/src/internal/packager2/layout/create.go @@ -271,7 +271,7 @@ func LoadPackage(ctx context.Context, packagePath, flavor string, setVariables m return v1alpha1.ZarfPackage{}, err } pkg.Metadata.Architecture = config.GetArch(pkg.Metadata.Architecture) - pkg, err = resolveImports(ctx, pkg, packagePath, pkg.Metadata.Architecture, flavor, map[string]interface{}{}) + pkg, err = resolveImports(ctx, pkg, packagePath, pkg.Metadata.Architecture, flavor, []string{}) if err != nil { return v1alpha1.ZarfPackage{}, err } diff --git a/src/internal/packager2/layout/import.go b/src/internal/packager2/layout/import.go index af7d2ad7a4..c3f613984a 100644 --- a/src/internal/packager2/layout/import.go +++ b/src/internal/packager2/layout/import.go @@ -7,7 +7,6 @@ import ( "context" "errors" "fmt" - "maps" "os" "path/filepath" "slices" @@ -24,7 +23,9 @@ import ( "github.com/zarf-dev/zarf/src/pkg/zoci" ) -func resolveImports(ctx context.Context, pkg v1alpha1.ZarfPackage, packagePath, arch, flavor string, seenImports map[string]interface{}) (v1alpha1.ZarfPackage, error) { +func resolveImports(ctx context.Context, pkg v1alpha1.ZarfPackage, packagePath, arch, flavor string, importStack []string) (v1alpha1.ZarfPackage, error) { + importStack = append(importStack, packagePath) + variables := pkg.Variables constants := pkg.Constants components := []v1alpha1.ZarfComponent{} @@ -47,12 +48,11 @@ func resolveImports(ctx context.Context, pkg v1alpha1.ZarfPackage, packagePath, var importedPkg v1alpha1.ZarfPackage if component.Import.Path != "" { importPath := filepath.Join(packagePath, component.Import.Path) - importKey := fmt.Sprintf("%s-%s", component.Name, importPath) - if _, ok := seenImports[importKey]; ok { - return v1alpha1.ZarfPackage{}, fmt.Errorf("package %s imported in cycle by %s in component %s", filepath.ToSlash(importPath), filepath.ToSlash(packagePath), component.Name) + for _, sp := range importStack { + if sp == importPath { + return v1alpha1.ZarfPackage{}, fmt.Errorf("package %s imported in cycle by %s in component %s", filepath.ToSlash(importPath), filepath.ToSlash(packagePath), component.Name) + } } - seenImports = maps.Clone(seenImports) - seenImports[importKey] = nil b, err := os.ReadFile(filepath.Join(importPath, layout.ZarfYAML)) if err != nil { return v1alpha1.ZarfPackage{}, err @@ -61,7 +61,7 @@ func resolveImports(ctx context.Context, pkg v1alpha1.ZarfPackage, packagePath, if err != nil { return v1alpha1.ZarfPackage{}, err } - importedPkg, err = resolveImports(ctx, importedPkg, importPath, arch, flavor, seenImports) + importedPkg, err = resolveImports(ctx, importedPkg, importPath, arch, flavor, importStack) if err != nil { return v1alpha1.ZarfPackage{}, err } @@ -91,7 +91,7 @@ func resolveImports(ctx context.Context, pkg v1alpha1.ZarfPackage, packagePath, } } if len(found) == 0 { - return v1alpha1.ZarfPackage{}, fmt.Errorf("component %s not found", name) + return v1alpha1.ZarfPackage{}, fmt.Errorf("no compatible component named %s found", name) } else if len(found) > 1 { return v1alpha1.ZarfPackage{}, fmt.Errorf("multiple components named %s found", name) } @@ -122,7 +122,7 @@ func resolveImports(ctx context.Context, pkg v1alpha1.ZarfPackage, packagePath, pkg.Constants = slices.CompactFunc(constants, func(l, r v1alpha1.Constant) bool { return l.Name == r.Name }) - + importStack = importStack[0:len(importStack)-1] return pkg, nil } diff --git a/src/internal/packager2/layout/import_test.go b/src/internal/packager2/layout/import_test.go index 04d84a6d6b..4e45b7d4eb 100644 --- a/src/internal/packager2/layout/import_test.go +++ b/src/internal/packager2/layout/import_test.go @@ -23,13 +23,35 @@ func TestResolveImportsCircular(t *testing.T) { lint.ZarfSchema = testutil.LoadSchema(t, "../../../../zarf.schema.json") - b, err := os.ReadFile(filepath.Join("./testdata/import/first", ZarfYAML)) + b, err := os.ReadFile(filepath.Join("./testdata/import/circular/first", ZarfYAML)) require.NoError(t, err) pkg, err := ParseZarfPackage(b) require.NoError(t, err) - _, err = resolveImports(ctx, pkg, "./testdata/import/first", "", "", map[string]interface{}{}) - require.EqualError(t, err, "package testdata/import/second imported in cycle by testdata/import/third in component component") + _, err = resolveImports(ctx, pkg, "./testdata/import/circular/first", "", "", []string{}) + require.EqualError(t, err, "package testdata/import/circular/second imported in cycle by testdata/import/circular/third in component component") +} + +func TestResolveImportsBranches(t *testing.T) { + t.Parallel() + ctx := testutil.TestContext(t) + lint.ZarfSchema = testutil.LoadSchema(t, "../../../../zarf.schema.json") + + // Get the parent package + b, err := os.ReadFile(filepath.Join("./testdata/import/branch", ZarfYAML)) + require.NoError(t, err) + pkg, err := ParseZarfPackage(b) + require.NoError(t, err) + + resolvedPkg, err := resolveImports(ctx, pkg, "./testdata/import/branch", "", "", []string{}) + require.NoError(t, err) + + // ensure imports were resolved correctly + b, err = os.ReadFile(filepath.Join("./testdata/import/branch", "expected.yaml")) + require.NoError(t, err) + expectedPkg, err := ParseZarfPackage(b) + require.NoError(t, err) + require.Equal(t, expectedPkg, resolvedPkg) } func TestValidateComponentCompose(t *testing.T) { diff --git a/src/internal/packager2/layout/testdata/import/branch/child/zarf.yaml b/src/internal/packager2/layout/testdata/import/branch/child/zarf.yaml new file mode 100644 index 0000000000..99d53d5a9f --- /dev/null +++ b/src/internal/packager2/layout/testdata/import/branch/child/zarf.yaml @@ -0,0 +1,12 @@ +kind: ZarfPackageConfig +metadata: + name: example-child + +components: + - name: common + import: + path: ../common + + - name: parent-child-common + import: + path: ../common diff --git a/src/internal/packager2/layout/testdata/import/branch/common/zarf.yaml b/src/internal/packager2/layout/testdata/import/branch/common/zarf.yaml new file mode 100644 index 0000000000..1dfdc71f09 --- /dev/null +++ b/src/internal/packager2/layout/testdata/import/branch/common/zarf.yaml @@ -0,0 +1,10 @@ +kind: ZarfPackageConfig +metadata: + name: example-shared + +components: + - name: common + description: This gets imported by the parent and child directly + + - name: parent-child-common + description: This gets imported in a chain parent->child->common diff --git a/src/internal/packager2/layout/testdata/import/branch/expected.yaml b/src/internal/packager2/layout/testdata/import/branch/expected.yaml new file mode 100644 index 0000000000..d69881517f --- /dev/null +++ b/src/internal/packager2/layout/testdata/import/branch/expected.yaml @@ -0,0 +1,10 @@ +kind: ZarfPackageConfig +metadata: + name: example-package +components: +- name: common + description: This gets imported by the parent and child directly + required: true +- name: parent-child-common + description: This gets imported in a chain parent->child->common + required: true diff --git a/src/internal/packager2/layout/testdata/import/branch/zarf.yaml b/src/internal/packager2/layout/testdata/import/branch/zarf.yaml new file mode 100644 index 0000000000..ae930a1f78 --- /dev/null +++ b/src/internal/packager2/layout/testdata/import/branch/zarf.yaml @@ -0,0 +1,14 @@ +kind: ZarfPackageConfig +metadata: + name: example-package + +components: + - name: common + required: true + import: + path: common + + - name: parent-child-common + required: true + import: + path: child diff --git a/src/internal/packager2/layout/testdata/import/first/zarf.yaml b/src/internal/packager2/layout/testdata/import/circular/first/zarf.yaml similarity index 100% rename from src/internal/packager2/layout/testdata/import/first/zarf.yaml rename to src/internal/packager2/layout/testdata/import/circular/first/zarf.yaml diff --git a/src/internal/packager2/layout/testdata/import/second/zarf.yaml b/src/internal/packager2/layout/testdata/import/circular/second/zarf.yaml similarity index 100% rename from src/internal/packager2/layout/testdata/import/second/zarf.yaml rename to src/internal/packager2/layout/testdata/import/circular/second/zarf.yaml diff --git a/src/internal/packager2/layout/testdata/import/third/zarf.yaml b/src/internal/packager2/layout/testdata/import/circular/third/zarf.yaml similarity index 100% rename from src/internal/packager2/layout/testdata/import/third/zarf.yaml rename to src/internal/packager2/layout/testdata/import/circular/third/zarf.yaml