diff --git a/docs/3-create-a-zarf-package/4-zarf-schema.md b/docs/3-create-a-zarf-package/4-zarf-schema.md index 050539669f..0ca39289a3 100644 --- a/docs/3-create-a-zarf-package/4-zarf-schema.md +++ b/docs/3-create-a-zarf-package/4-zarf-schema.md @@ -1848,7 +1848,7 @@ Must be one of: | ------------------------- | -------------------------------------------------------------------------------------------------------- | | **Type** | `object` | | **Additional properties** | [![Not allowed](https://img.shields.io/badge/Not%20allowed-red)](# "Additional Properties not allowed.") | -| **Defined in** | #/definitions/ZarfComponentActionShell | +| **Defined in** | #/definitions/Shell |
diff --git a/src/cmd/common/setup.go b/src/cmd/common/setup.go index 8bde89e640..a3736a35ae 100644 --- a/src/cmd/common/setup.go +++ b/src/cmd/common/setup.go @@ -10,7 +10,6 @@ import ( "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/pkg/message" - "github.com/defenseunicorns/zarf/src/pkg/utils/exec" ) // LogLevelCLI holds the log level as input from a command @@ -18,7 +17,7 @@ var LogLevelCLI string // SetupCLI sets up the CLI logging, interrupt functions, and more func SetupCLI() { - exec.ExitOnInterrupt() + ExitOnInterrupt() match := map[string]message.LogLevel{ "warn": message.WarnLevel, diff --git a/src/cmd/common/utils.go b/src/cmd/common/utils.go index 1da7e456ee..54113ee90f 100644 --- a/src/cmd/common/utils.go +++ b/src/cmd/common/utils.go @@ -4,6 +4,18 @@ // Package common handles command configuration across all commands package common +import ( + "os" + "os/signal" + "syscall" + + "github.com/defenseunicorns/zarf/src/config/lang" + "github.com/defenseunicorns/zarf/src/pkg/message" +) + +// SuppressGlobalInterrupt suppresses the global error on an interrupt +var SuppressGlobalInterrupt = false + // SetBaseDirectory sets the base directory. This is a directory with a zarf.yaml. func SetBaseDirectory(args []string) string { if len(args) > 0 { @@ -11,3 +23,15 @@ func SetBaseDirectory(args []string) string { } return "." } + +// ExitOnInterrupt catches an interrupt and exits with fatal error +func ExitOnInterrupt() { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + go func() { + <-c + if !SuppressGlobalInterrupt { + message.Fatal(lang.ErrInterrupt, lang.ErrInterrupt.Error()) + } + }() +} diff --git a/src/cmd/connect.go b/src/cmd/connect.go index fe3442e395..39b382cad3 100644 --- a/src/cmd/connect.go +++ b/src/cmd/connect.go @@ -10,6 +10,7 @@ import ( "os/signal" "syscall" + "github.com/defenseunicorns/zarf/src/cmd/common" "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/pkg/cluster" "github.com/defenseunicorns/zarf/src/pkg/k8s" @@ -72,7 +73,7 @@ var ( // Keep this open until an interrupt signal is received. interruptChan := make(chan os.Signal, 1) signal.Notify(interruptChan, os.Interrupt, syscall.SIGTERM) - exec.SuppressGlobalInterrupt = true + common.SuppressGlobalInterrupt = true // Wait for the interrupt signal or an error. select { diff --git a/src/cmd/destroy.go b/src/cmd/destroy.go index 0a60d05503..b0825af632 100644 --- a/src/cmd/destroy.go +++ b/src/cmd/destroy.go @@ -14,8 +14,8 @@ import ( "github.com/defenseunicorns/zarf/src/internal/packager/helm" "github.com/defenseunicorns/zarf/src/pkg/cluster" "github.com/defenseunicorns/zarf/src/pkg/message" - "github.com/defenseunicorns/zarf/src/pkg/utils" "github.com/defenseunicorns/zarf/src/pkg/utils/exec" + "github.com/defenseunicorns/zarf/src/pkg/utils/helpers" "github.com/spf13/cobra" ) @@ -52,7 +52,7 @@ var destroyCmd = &cobra.Command{ // Run all the scripts! pattern := regexp.MustCompile(`(?mi)zarf-clean-.+\.sh$`) - scripts, _ := utils.RecursiveFileList(config.ZarfCleanupScriptsPath, pattern, true) + scripts, _ := helpers.RecursiveFileList(config.ZarfCleanupScriptsPath, pattern, true) // Iterate over all matching zarf-clean scripts and exec them for _, script := range scripts { // Run the matched script diff --git a/src/cmd/initialize.go b/src/cmd/initialize.go index 40f633ed41..db3f790466 100644 --- a/src/cmd/initialize.go +++ b/src/cmd/initialize.go @@ -77,7 +77,7 @@ var initCmd = &cobra.Command{ func findInitPackage(initPackageName string) (string, error) { // First, look for the init package in the current working directory - if !utils.InvalidPath(initPackageName) { + if !helpers.InvalidPath(initPackageName) { return initPackageName, nil } @@ -87,19 +87,19 @@ func findInitPackage(initPackageName string) (string, error) { return "", err } executableDir := path.Dir(binaryPath) - if !utils.InvalidPath(filepath.Join(executableDir, initPackageName)) { + if !helpers.InvalidPath(filepath.Join(executableDir, initPackageName)) { return filepath.Join(executableDir, initPackageName), nil } // Create the cache directory if it doesn't exist - if utils.InvalidPath(config.GetAbsCachePath()) { - if err := utils.CreateDirectory(config.GetAbsCachePath(), helpers.ReadExecuteAllWriteUser); err != nil { + if helpers.InvalidPath(config.GetAbsCachePath()) { + if err := helpers.CreateDirectory(config.GetAbsCachePath(), helpers.ReadExecuteAllWriteUser); err != nil { message.Fatalf(err, lang.CmdInitErrUnableCreateCache, config.GetAbsCachePath()) } } // Next, look in the cache directory - if !utils.InvalidPath(filepath.Join(config.GetAbsCachePath(), initPackageName)) { + if !helpers.InvalidPath(filepath.Join(config.GetAbsCachePath(), initPackageName)) { return filepath.Join(config.GetAbsCachePath(), initPackageName), nil } diff --git a/src/cmd/package.go b/src/cmd/package.go index f296684e7a..b113c94e67 100644 --- a/src/cmd/package.go +++ b/src/cmd/package.go @@ -14,7 +14,6 @@ import ( "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/pkg/message" "github.com/defenseunicorns/zarf/src/pkg/packager/sources" - "github.com/defenseunicorns/zarf/src/pkg/utils" "github.com/defenseunicorns/zarf/src/types" "oras.land/oras-go/v2/registry" @@ -214,7 +213,7 @@ var packagePublishCmd = &cobra.Command{ message.Fatalf(nil, "%s", err.Error()) } - if utils.IsDir(pkgConfig.PkgOpts.PackageSource) { + if helpers.IsDir(pkgConfig.PkgOpts.PackageSource) { pkgConfig.CreateOpts.BaseDir = pkgConfig.PkgOpts.PackageSource pkgConfig.CreateOpts.IsSkeleton = true } diff --git a/src/cmd/tools/crane.go b/src/cmd/tools/crane.go index b3ddd32e17..b6fd47e5a0 100644 --- a/src/cmd/tools/crane.go +++ b/src/cmd/tools/crane.go @@ -10,12 +10,12 @@ import ( "strings" "github.com/AlecAivazis/survey/v2" + "github.com/defenseunicorns/zarf/src/cmd/common" "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/pkg/cluster" "github.com/defenseunicorns/zarf/src/pkg/message" "github.com/defenseunicorns/zarf/src/pkg/transform" - "github.com/defenseunicorns/zarf/src/pkg/utils/exec" "github.com/defenseunicorns/zarf/src/types" craneCmd "github.com/google/go-containerregistry/cmd/crane/cmd" "github.com/google/go-containerregistry/pkg/crane" @@ -39,7 +39,7 @@ func init() { Short: lang.CmdToolsRegistryShort, PersistentPreRun: func(cmd *cobra.Command, _ []string) { - exec.ExitOnInterrupt() + common.ExitOnInterrupt() // The crane options loading here comes from the rootCmd of crane craneOptions = append(craneOptions, crane.WithContext(cmd.Context())) diff --git a/src/extensions/bigbang/bigbang.go b/src/extensions/bigbang/bigbang.go index 3f2025f787..50da7a9fe4 100644 --- a/src/extensions/bigbang/bigbang.go +++ b/src/extensions/bigbang/bigbang.go @@ -262,7 +262,7 @@ func Skeletonize(tmpPaths *layout.ComponentPaths, c types.ZarfComponent) (types. rel := filepath.Join(layout.TempDir, skelName) dst := filepath.Join(tmpPaths.Base, rel) - if err := utils.CreatePathAndCopy(valuesFile, dst); err != nil { + if err := helpers.CreatePathAndCopy(valuesFile, dst); err != nil { return c, err } @@ -282,7 +282,7 @@ func Skeletonize(tmpPaths *layout.ComponentPaths, c types.ZarfComponent) (types. rel := filepath.Join(layout.TempDir, skelName) dst := filepath.Join(tmpPaths.Base, rel) - if err := utils.CreatePathAndCopy(fluxPatchFile, dst); err != nil { + if err := helpers.CreatePathAndCopy(fluxPatchFile, dst); err != nil { return c, err } diff --git a/src/internal/packager/helm/repo.go b/src/internal/packager/helm/repo.go index 4df052dbd8..459c6a3bd2 100644 --- a/src/internal/packager/helm/repo.go +++ b/src/internal/packager/helm/repo.go @@ -93,7 +93,7 @@ func (h *Helm) PackageChartFromLocalFiles(cosignKeyPath string) error { saved, err = client.Run(h.chart.LocalPath, nil) } else { saved = filepath.Join(temp, filepath.Base(h.chart.LocalPath)) - err = utils.CreatePathAndCopy(h.chart.LocalPath, saved) + err = helpers.CreatePathAndCopy(h.chart.LocalPath, saved) } defer os.RemoveAll(temp) @@ -204,7 +204,7 @@ func (h *Helm) DownloadPublishedChart(cosignKeyPath string) error { // Download the file into a temp directory since we don't control what name helm creates here temp := filepath.Join(h.chartPath, "temp") - if err = utils.CreateDirectory(temp, helpers.ReadWriteExecuteUser); err != nil { + if err = helpers.CreateDirectory(temp, helpers.ReadWriteExecuteUser); err != nil { return fmt.Errorf("unable to create helm chart temp directory: %w", err) } defer os.RemoveAll(temp) @@ -269,7 +269,7 @@ func (h *Helm) packageValues(cosignKeyPath string) error { return fmt.Errorf(lang.ErrDownloading, path, err.Error()) } } else { - if err := utils.CreatePathAndCopy(path, dst); err != nil { + if err := helpers.CreatePathAndCopy(path, dst); err != nil { return fmt.Errorf("unable to copy chart values file %s: %w", path, err) } } diff --git a/src/internal/packager/images/pull.go b/src/internal/packager/images/pull.go index f6cba2a980..8f38a27964 100644 --- a/src/internal/packager/images/pull.go +++ b/src/internal/packager/images/pull.go @@ -123,7 +123,7 @@ func (i *ImageConfig) PullAll() ([]ImgInfo, error) { } // Create the ImagePath directory - if err := utils.CreateDirectory(i.ImagesPath, helpers.ReadExecuteAllWriteUser); err != nil { + if err := helpers.CreateDirectory(i.ImagesPath, helpers.ReadExecuteAllWriteUser); err != nil { return nil, fmt.Errorf("failed to create image path %s: %w", i.ImagesPath, err) } @@ -233,7 +233,7 @@ func (i *ImageConfig) PullAll() ([]ImgInfo, error) { // Create the directory for the blob if it doesn't exist dir := filepath.Join(string(cranePath), "blobs", digest.Algorithm) - if err := utils.CreateDirectory(dir, os.ModePerm); err != nil { + if err := helpers.CreateDirectory(dir, os.ModePerm); err != nil { layerWritingConcurrency.ErrorChan <- err return } diff --git a/src/internal/packager/sbom/catalog.go b/src/internal/packager/sbom/catalog.go index 646f066a34..a2a6618622 100755 --- a/src/internal/packager/sbom/catalog.go +++ b/src/internal/packager/sbom/catalog.go @@ -60,7 +60,7 @@ func Catalog(componentSBOMs map[string]*layout.ComponentSBOM, imageList []transf defer builder.spinner.Stop() // Ensure the sbom directory exists - _ = utils.CreateDirectory(builder.outputDir, helpers.ReadWriteExecuteUser) + _ = helpers.CreateDirectory(builder.outputDir, helpers.ReadWriteExecuteUser) // Generate a list of images and files for the sbom viewer json, err := builder.generateJSONList(componentSBOMs, imageList) @@ -152,7 +152,7 @@ func (b *Builder) createImageSBOM(img v1.Image, src string) ([]byte, error) { imageCachePath := filepath.Join(b.cachePath, layout.ImagesDir) // Ensure the image cache directory exists. - if err := utils.CreateDirectory(imageCachePath, helpers.ReadWriteExecuteUser); err != nil { + if err := helpers.CreateDirectory(imageCachePath, helpers.ReadWriteExecuteUser); err != nil { return nil, err } diff --git a/src/internal/packager/template/template.go b/src/internal/packager/template/template.go index 75882e6bdf..f7e669ef58 100644 --- a/src/internal/packager/template/template.go +++ b/src/internal/packager/template/template.go @@ -5,8 +5,11 @@ package template import ( + "bufio" "encoding/base64" "fmt" + "os" + "regexp" "strings" "github.com/defenseunicorns/zarf/src/types" @@ -17,6 +20,14 @@ import ( "github.com/defenseunicorns/zarf/src/pkg/utils/helpers" ) +// TextTemplate represents a value to be templated into a text file. +type TextTemplate struct { + Sensitive bool + AutoIndent bool + Type types.VariableType + Value string +} + // Values contains the values to be used in the template. type Values struct { config *types.PackagerConfig @@ -69,8 +80,8 @@ func (values *Values) SetState(state *types.ZarfState) { } // GetVariables returns the variables to be used in the template. -func (values *Values) GetVariables(component types.ZarfComponent) (templateMap map[string]*utils.TextTemplate, deprecations map[string]string) { - templateMap = make(map[string]*utils.TextTemplate) +func (values *Values) GetVariables(component types.ZarfComponent) (templateMap map[string]*TextTemplate, deprecations map[string]string) { + templateMap = make(map[string]*TextTemplate) depMarkerOld := "DATA_INJECTON_MARKER" depMarkerNew := "DATA_INJECTION_MARKER" @@ -125,7 +136,7 @@ func (values *Values) GetVariables(component types.ZarfComponent) (templateMap m // Iterate over any custom variables and add them to the mappings for templating for key, value := range builtinMap { // Builtin keys are always uppercase in the format ###ZARF_KEY### - templateMap[strings.ToUpper(fmt.Sprintf("###ZARF_%s###", key))] = &utils.TextTemplate{ + templateMap[strings.ToUpper(fmt.Sprintf("###ZARF_%s###", key))] = &TextTemplate{ Value: value, } @@ -140,7 +151,7 @@ func (values *Values) GetVariables(component types.ZarfComponent) (templateMap m for key, variable := range values.config.SetVariableMap { // Variable keys are always uppercase in the format ###ZARF_VAR_KEY### - templateMap[strings.ToUpper(fmt.Sprintf("###ZARF_VAR_%s###", key))] = &utils.TextTemplate{ + templateMap[strings.ToUpper(fmt.Sprintf("###ZARF_VAR_%s###", key))] = &TextTemplate{ Value: variable.Value, Sensitive: variable.Sensitive, AutoIndent: variable.AutoIndent, @@ -150,7 +161,7 @@ func (values *Values) GetVariables(component types.ZarfComponent) (templateMap m for _, constant := range values.config.Pkg.Constants { // Constant keys are always uppercase in the format ###ZARF_CONST_KEY### - templateMap[strings.ToUpper(fmt.Sprintf("###ZARF_CONST_%s###", constant.Name))] = &utils.TextTemplate{ + templateMap[strings.ToUpper(fmt.Sprintf("###ZARF_CONST_%s###", constant.Name))] = &TextTemplate{ Value: constant.Value, AutoIndent: constant.AutoIndent, } @@ -170,12 +181,99 @@ func (values *Values) Apply(component types.ZarfComponent, path string, ignoreRe } templateMap, deprecations := values.GetVariables(component) - err := utils.ReplaceTextTemplate(path, templateMap, deprecations, "###ZARF_[A-Z0-9_]+###") + err := ReplaceTextTemplate(path, templateMap, deprecations, "###ZARF_[A-Z0-9_]+###") return err } -func debugPrintTemplateMap(templateMap map[string]*utils.TextTemplate) { +// ReplaceTextTemplate loads a file from a given path, replaces text in it and writes it back in place. +func ReplaceTextTemplate(path string, mappings map[string]*TextTemplate, deprecations map[string]string, templateRegex string) error { + textFile, err := os.Open(path) + if err != nil { + return err + } + + // This regex takes a line and parses the text before and after a discovered template: https://regex101.com/r/ilUxAz/1 + regexTemplateLine := regexp.MustCompile(fmt.Sprintf("(?P.*?)(?P