From 116bc266f5784f8050cb04b7c31db47b112cb928 Mon Sep 17 00:00:00 2001 From: Michael Winberry Date: Mon, 28 Feb 2022 15:07:09 -0800 Subject: [PATCH] #208. ADD packager/compose.go: Created compose logic for imported components and nested import components. UPDATE types.go: Added ZarfImport type with a path variable as the associated type with ZarfComponent.Import field (this sets up sha verification additions in the future, still need team imput). UPDATE Packager/Common.go: pulled confirming optional component logic out of getValidComponents to play with composed package validation (need to validate expected behavior). UPDATE Packager/Create.go: Replaced the initial compose logic with call to GetComposedAssets(). ADD examples/compose-example: a simple composition example for explicit example. --- cli/config/config.go | 5 ++ cli/internal/packager/common.go | 31 +++++----- cli/internal/packager/compose.go | 94 ++++++++++++++++++++++++++++++ cli/internal/packager/create.go | 18 +----- cli/types/types.go | 9 ++- examples/compose-example/zarf.yaml | 14 +++++ examples/game/zarf.yaml | 3 - 7 files changed, 139 insertions(+), 35 deletions(-) create mode 100644 cli/internal/packager/compose.go create mode 100644 examples/compose-example/zarf.yaml diff --git a/cli/config/config.go b/cli/config/config.go index 5dba90b57f..86dc6124f5 100644 --- a/cli/config/config.go +++ b/cli/config/config.go @@ -95,6 +95,11 @@ func GetSeedImages() []string { } } +// SetSeedImages sets the list of image string specified in the package. +func SetSeedImages(seedImages []string) { + config.Seed = seedImages +} + func GetPackageName() string { metadata := GetMetaData() if metadata.Uncompressed { diff --git a/cli/internal/packager/common.go b/cli/internal/packager/common.go index e8facc96ce..de2cb5ea55 100644 --- a/cli/internal/packager/common.go +++ b/cli/internal/packager/common.go @@ -110,19 +110,7 @@ func getValidComponents(allComponents []types.ZarfComponent, requestedComponentN } } } else { - // Present the users with the component details one more time - displayComponent := component - displayComponent.Description = "" - content, _ := yaml.Marshal(displayComponent) - utils.ColorPrintYAML(string(content)) - message.Question(fmt.Sprintf("%s: %s", component.Name, component.Description)) - - // Since no requested components were provided, prompt the user - prompt := &survey.Confirm{ - Message: "Deploy this component?", - Default: component.Default, - } - _ = survey.AskOne(prompt, &confirmComponent) + confirmComponent = ConfirmOptionalComponent(component) } } @@ -150,6 +138,23 @@ func getValidComponents(allComponents []types.ZarfComponent, requestedComponentN return validComponentsList } +// Confirm optional component +func ConfirmOptionalComponent(component types.ZarfComponent) (confirmComponent bool) { + displayComponent := component + displayComponent.Description = "" + content, _ := yaml.Marshal(displayComponent) + utils.ColorPrintYAML(string(content)) + message.Question(fmt.Sprintf("%s: %s", component.Name, component.Description)) + + // Since no requested components were provided, prompt the user + prompt := &survey.Confirm{ + Message: "Deploy this component?", + Default: component.Default, + } + _ = survey.AskOne(prompt, &confirmComponent) + return confirmComponent +} + // HandleIfURL If provided package is a URL download it to a temp directory func HandleIfURL(packagePath string, shasum string, insecureDeploy bool) (string, func()) { // Check if the user gave us a remote package diff --git a/cli/internal/packager/compose.go b/cli/internal/packager/compose.go new file mode 100644 index 0000000000..cd2477af5a --- /dev/null +++ b/cli/internal/packager/compose.go @@ -0,0 +1,94 @@ +package packager + +import ( + "github.com/defenseunicorns/zarf/cli/config" + "github.com/defenseunicorns/zarf/cli/internal/utils" + "github.com/defenseunicorns/zarf/cli/types" +) + +func GetComposedAssets() (components []types.ZarfComponent, seedImages []string) { + for _, component := range config.GetComponents() { + // Build components list by expanding imported components. + if hasSubPackage(&component) { + importedComponents, importedImages := getSubPackageAssets(component) + components = append(components, importedComponents...) + seedImages = append(seedImages, importedImages...) + + } else { + components = append(components, component) + } + } + // Update the parent package config with the expanded sub components and seed images. + // This is important when the deploy package is created. + config.SetComponents(components) + config.SetSeedImages(seedImages) + return components, seedImages +} + +// Get the sub package components/seed images to add to parent assets, recurses on sub imports. +func getSubPackageAssets(importComponent types.ZarfComponent) (components []types.ZarfComponent, seedImages []string) { + importedPackage := getSubPackage(&importComponent) + seedImages = importedPackage.Seed + for _, componentToCompose := range importedPackage.Components { + if hasSubPackage(&componentToCompose) { + subComp, subImage := getSubPackageAssets(componentToCompose) + components = append(components, subComp...) + seedImages = append(seedImages, subImage...) + } else { + prepComponentToCompose(&componentToCompose, importedPackage.Metadata.Name, importComponent.Import.Path) + components = append(components, componentToCompose) + } + } + return components, seedImages +} + +// Confirms inclusion of SubPackage. Need team input. +func shouldAddImportedPackage(component *types.ZarfComponent) bool { + return hasSubPackage(component) && (component.Required || ConfirmOptionalComponent(*component)) +} + +// returns true if import has url +func hasSubPackage(component *types.ZarfComponent) bool { + return len(component.Import.Path) > 0 +} + +// Reads the locally imported zarf.yaml +func getSubPackage(component *types.ZarfComponent) (importedPackage types.ZarfPackage) { + utils.ReadYaml(component.Import.Path+"zarf.yaml", &importedPackage) + return importedPackage +} + +// Updates the name and sets all local asset paths relative to the importing package. +func prepComponentToCompose(component *types.ZarfComponent, parentPackageName string, importPath string) { + component.Name = parentPackageName + "-" + component.Name + + // Add import path to local component files. + for idx, file := range component.Files { + if !utils.IsUrl(file.Source) { + component.Files[idx].Source = importPath + file.Source + } + } + + // Add import path to local chart values files. + for chartIndex, chart := range component.Charts { + for valuesIndex, valuesFile := range chart.ValuesFiles { + if !utils.IsUrl(valuesFile) { + component.Charts[chartIndex].ValuesFiles[valuesIndex] = importPath + valuesFile + } + } + } + + // Add import path to local manifest files and kustomizations + for manifestIndex, manifest := range component.Manifests { + for fileIndex, file := range manifest.Files { + if !utils.IsUrl(file) { + component.Manifests[manifestIndex].Files[fileIndex] = importPath + file + } + } + for kustomizationIndex, kustomization := range manifest.Kustomizations { + if !utils.IsUrl(kustomization) { + component.Manifests[manifestIndex].Kustomizations[kustomizationIndex] = importPath + kustomization + } + } + } +} diff --git a/cli/internal/packager/create.go b/cli/internal/packager/create.go index 1a9c7728ea..30dfa0bb7f 100644 --- a/cli/internal/packager/create.go +++ b/cli/internal/packager/create.go @@ -30,29 +30,13 @@ func Create() { tempPath := createPaths() defer tempPath.clean() + components, seedImages := GetComposedAssets() packageName := config.GetPackageName() dataInjections := config.GetDataInjections() - seedImages := config.GetSeedImages() - components := []types.ZarfComponent{} configFile := tempPath.base + "/zarf.yaml" config.SetAcrch() - for _, component := range config.GetComponents() { - if len(component.Import) > 0 { - importedPackage := types.ZarfPackage{} - utils.ReadYaml(component.Import+"zarf.yaml", &importedPackage) - seedImages = append(seedImages, importedPackage.Seed...) - for _, composedComponent := range importedPackage.Components { - composedComponent.Name = importedPackage.Metadata.Name + "-" + composedComponent.Name - components = append(components, composedComponent) - } - } else { - components = append(components, component) - } - } - config.SetComponents(components) - // Save the transformed config if err := config.BuildConfig(configFile); err != nil { message.Fatalf(err, "Unable to write the %s file", configFile) diff --git a/cli/types/types.go b/cli/types/types.go index ac63223db4..c5c40c816e 100644 --- a/cli/types/types.go +++ b/cli/types/types.go @@ -54,8 +54,8 @@ type ZarfComponent struct { // Scripts are custom commands that run before or after package deployment Scripts ZarfComponentScripts `yaml:"scripts,omitempty"` - // Import refers to another zarf.yml package. - Import string `yaml:"import,omitempty"` + // Import refers to another zarf.yaml package. + Import ZarfImport `yaml:"import,omitempty"` } // ZarfManifest defines raw manifests Zarf will deploy as a helm chart @@ -144,3 +144,8 @@ type ZarfDeployOptions struct { Components string ApplianceMode bool } + +// ZarfImport structure for including imported zarf packages +type ZarfImport struct { + Path string `yaml:"path"` +} diff --git a/examples/compose-example/zarf.yaml b/examples/compose-example/zarf.yaml new file mode 100644 index 0000000000..0375aa81eb --- /dev/null +++ b/examples/compose-example/zarf.yaml @@ -0,0 +1,14 @@ +kind: ZarfPackageConfig +metadata: + name: compose-example + description: "Demo Zarf package composability" + +components: + - name: flux + required: false + import: + path: '../flux/' + - name: games + required: true + import: + path: '../game/' diff --git a/examples/game/zarf.yaml b/examples/game/zarf.yaml index 9b6ecfe149..1709d39e55 100644 --- a/examples/game/zarf.yaml +++ b/examples/game/zarf.yaml @@ -4,9 +4,6 @@ metadata: description: "Demo Zarf appliance mode with some dos games" components: - - name: flux - required: false - import: '../flux/' - name: baseline required: true manifests: