diff --git a/docs/4-user-guide/1-the-zarf-cli/100-cli-commands/zarf_tools_archiver_decompress.md b/docs/4-user-guide/1-the-zarf-cli/100-cli-commands/zarf_tools_archiver_decompress.md index f6b4385aed..62d1f900c6 100644 --- a/docs/4-user-guide/1-the-zarf-cli/100-cli-commands/zarf_tools_archiver_decompress.md +++ b/docs/4-user-guide/1-the-zarf-cli/100-cli-commands/zarf_tools_archiver_decompress.md @@ -10,7 +10,8 @@ zarf tools archiver decompress {ARCHIVE} {DESTINATION} [flags] ## Options ``` - -h, --help help for decompress + --decompress-all Decompress all layers in the archive + -h, --help help for decompress ``` ## Options inherited from parent commands diff --git a/src/cmd/tools.go b/src/cmd/tools.go index 1e97df1943..76eaf45bbf 100644 --- a/src/cmd/tools.go +++ b/src/cmd/tools.go @@ -6,6 +6,8 @@ package cmd import ( "os" + "path/filepath" + "strings" "github.com/anchore/syft/cmd/syft/cli" "github.com/defenseunicorns/zarf/src/config" @@ -22,6 +24,7 @@ import ( ) var subAltNames []string +var decompressLayers bool var toolsCmd = &cobra.Command{ Use: "tools", @@ -64,6 +67,23 @@ var archiverDecompressCmd = &cobra.Command{ if err != nil { message.Fatal(err, lang.CmdToolsArchiverDecompressErr) } + + // Decompress component layers in the destination path + if decompressLayers { + layersDir := filepath.Join(destinationPath, "components") + + files, err := os.ReadDir(layersDir) + if err != nil { + message.Fatalf(err, "failed to read the layers of components") + } + for _, file := range files { + if strings.HasSuffix(file.Name(), "tar.zst") { + if err := archiver.Unarchive(filepath.Join(layersDir, file.Name()), layersDir); err != nil { + message.Fatalf(err, "failed to decompress the component layer") + } + } + } + } }, } @@ -173,6 +193,7 @@ func init() { archiverCmd.AddCommand(archiverCompressCmd) archiverCmd.AddCommand(archiverDecompressCmd) + archiverDecompressCmd.Flags().BoolVar(&decompressLayers, "decompress-all", false, "Decompress all layers in the archive") cranePlatformOptions := config.GetCraneOptions(false) diff --git a/src/pkg/packager/common.go b/src/pkg/packager/common.go index 6a46eb2ff1..a756463e22 100644 --- a/src/pkg/packager/common.go +++ b/src/pkg/packager/common.go @@ -205,6 +205,24 @@ func (p *Packager) loadZarfPkg() error { return fmt.Errorf("unable to read the zarf.yaml in %s: %w", p.tmp.Base, err) } + // Get a list of paths for the components of the package + components, err := os.ReadDir(p.tmp.Components) + if err != nil { + return fmt.Errorf("unable to get a list of components... %w", err) + } + for _, component := range components { + // If the components are compressed tarballs, un-compress them + componentPath := filepath.Join(p.tmp.Components, component.Name()) + if !component.IsDir() && strings.HasSuffix(component.Name(), ".tar.zst") { + if err := archiver.Unarchive(componentPath, p.tmp.Components); err != nil { + return fmt.Errorf("unable to extract the component: %w", err) + } + + // After extracting the component, remove the compressed tarball to release disk space + _ = os.Remove(filepath.Join(p.tmp.Components, component.Name())) + } + } + // If SBOM files exist, temporarily place them in the deploy directory if err := sbom.OutputSBOMFiles(p.tmp, config.ZarfSBOMDir, ""); err != nil { // Don't stop the deployment, let the user decide if they want to continue the deployment diff --git a/src/pkg/packager/create.go b/src/pkg/packager/create.go index d4641844f8..ab2fcd5633 100644 --- a/src/pkg/packager/create.go +++ b/src/pkg/packager/create.go @@ -164,6 +164,23 @@ func (p *Packager) Create(baseDir string) error { sbom.Catalog(componentSBOMs, imgList, p.tmp.Images, p.tmp.Sboms) } + // Process the component directories into compressed tarballs + // NOTE: This is purposefully being done after the SBOM cataloging + for _, component := range p.cfg.Pkg.Components { + // Make the component a tar.zst archive + componentPaths, _ := p.createComponentPaths(component) + componentName := fmt.Sprintf("%s.%s", component.Name, "tar.zst") + componentTarPath := filepath.Join(p.tmp.Components, componentName) + if err := archiver.Archive([]string{componentPaths.Base}, componentTarPath); err != nil { + return fmt.Errorf("unable to create package: %w", err) + } + + // Remove the deflated component directory + if err := os.RemoveAll(componentPaths.Base); err != nil { + message.Debugf("unable to remove the component directory (%s): %s", componentPaths.Base, err.Error()) + } + } + // In case the directory was changed, reset to prevent breaking relative target paths if originalDir != "" { _ = os.Chdir(originalDir) diff --git a/src/test/e2e/04_create_templating_test.go b/src/test/e2e/04_create_templating_test.go index 41e5b7ea19..e49907e2f3 100644 --- a/src/test/e2e/04_create_templating_test.go +++ b/src/test/e2e/04_create_templating_test.go @@ -36,7 +36,7 @@ func TestCreateTemplating(t *testing.T) { stdOut, stdErr, err := e2e.execZarfCommand("package", "create", "examples/package-variables", "--set", "CONFIG_MAP=simple-configmap.yaml", "--set", "ACTION=template", "--confirm", "--zarf-cache", cachePath) require.NoError(t, err, stdOut, stdErr) - stdOut, stdErr, err = e2e.execZarfCommand("t", "archiver", "decompress", pkgName, decompressPath) + stdOut, stdErr, err = e2e.execZarfCommand("t", "archiver", "decompress", pkgName, decompressPath, "--decompress-all", "-l=trace") require.NoError(t, err, stdOut, stdErr) // Check that the configmap exists and is readable diff --git a/src/test/e2e/22_git_and_flux_test.go b/src/test/e2e/22_git_and_flux_test.go index 16552f2691..07195f99a6 100644 --- a/src/test/e2e/22_git_and_flux_test.go +++ b/src/test/e2e/22_git_and_flux_test.go @@ -162,7 +162,7 @@ func testRemovingTagsOnCreate(t *testing.T) { // Extract the built package so we can inspect the repositories that are included extractedDirPath := "tmp-extraction" - stdOut, stdErr, err := e2e.execZarfCommand("tools", "archiver", "decompress", testPackagePath, extractedDirPath, "-l=trace") + stdOut, stdErr, err := e2e.execZarfCommand("tools", "archiver", "decompress", testPackagePath, extractedDirPath, "-l=trace", "--decompress-all") defer e2e.cleanFiles(extractedDirPath) require.NoError(t, err, stdOut, stdErr)