diff --git a/src/api/go.mod b/src/api/go.mod index 1351380609..4740f3ad9d 100644 --- a/src/api/go.mod +++ b/src/api/go.mod @@ -8,7 +8,6 @@ require ( github.com/defenseunicorns/pkg/helpers/v2 v2.0.1 github.com/invopop/jsonschema v0.12.0 github.com/stretchr/testify v1.9.0 - github.com/zarf-dev/zarf v0.37.0 k8s.io/apimachinery v0.30.3 ) diff --git a/src/api/v1alpha1/component.go b/src/api/v1alpha1/component.go index bd798c82f6..9739de8644 100644 --- a/src/api/v1alpha1/component.go +++ b/src/api/v1alpha1/component.go @@ -7,8 +7,6 @@ package v1alpha1 import ( "github.com/invopop/jsonschema" "github.com/zarf-dev/zarf/src/api/v1alpha1/extensions" - "github.com/zarf-dev/zarf/src/pkg/utils/exec" - "github.com/zarf-dev/zarf/src/pkg/variables" ) // ZarfComponent is the primary functional grouping of assets to deploy by Zarf. @@ -228,7 +226,7 @@ type ZarfComponentActionDefaults struct { // Additional environment variables for commands. Env []string `json:"env,omitempty"` // (cmd only) Indicates a preference for a shell for the provided cmd to be executed in on supported operating systems. - Shell exec.Shell `json:"shell,omitempty"` + Shell Shell `json:"shell,omitempty"` } // ZarfComponentAction represents a single action to run during a zarf package operation. @@ -246,11 +244,11 @@ type ZarfComponentAction struct { // The command to run. Must specify either cmd or wait for the action to do anything. Cmd string `json:"cmd,omitempty"` // (cmd only) Indicates a preference for a shell for the provided cmd to be executed in on supported operating systems. - Shell *exec.Shell `json:"shell,omitempty"` + Shell *Shell `json:"shell,omitempty"` // [Deprecated] (replaced by setVariables) (onDeploy/cmd only) The name of a variable to update with the output of the command. This variable will be available to all remaining actions and components in the package. This will be removed in Zarf v1.0.0. DeprecatedSetVariable string `json:"setVariable,omitempty" jsonschema:"pattern=^[A-Z0-9_]+$"` // (onDeploy/cmd only) An array of variables to update with the output of the command. These variables will be available to all remaining actions and components in the package. - SetVariables []variables.Variable `json:"setVariables,omitempty"` + SetVariables []Variable `json:"setVariables,omitempty"` // Description of the action to be displayed during package execution instead of the command. Description string `json:"description,omitempty"` // Wait for a condition to be met before continuing. Must specify either cmd or wait for the action. See the 'zarf tools wait-for' command for more info. @@ -331,3 +329,10 @@ func (ZarfComponentImport) JSONSchemaExtend(schema *jsonschema.Schema) { path.Not = notSchema url.Not = notSchema } + +// Shell represents the desired shell to use for a given command +type Shell struct { + Windows string `json:"windows,omitempty" jsonschema:"description=(default 'powershell') Indicates a preference for the shell to use on Windows systems (note that choosing 'cmd' will turn off migrations like touch -> New-Item),example=powershell,example=cmd,example=pwsh,example=sh,example=bash,example=gsh"` + Linux string `json:"linux,omitempty" jsonschema:"description=(default 'sh') Indicates a preference for the shell to use on Linux systems,example=sh,example=bash,example=fish,example=zsh,example=pwsh"` + Darwin string `json:"darwin,omitempty" jsonschema:"description=(default 'sh') Indicates a preference for the shell to use on macOS systems,example=sh,example=bash,example=fish,example=zsh,example=pwsh"` +} diff --git a/src/api/v1alpha1/package.go b/src/api/v1alpha1/package.go index 2262bf3e82..eb279d90a1 100644 --- a/src/api/v1alpha1/package.go +++ b/src/api/v1alpha1/package.go @@ -5,7 +5,24 @@ package v1alpha1 import ( - "github.com/zarf-dev/zarf/src/pkg/variables" + "fmt" + "regexp" +) + +// VariableType represents a type of a Zarf package variable +type VariableType string + +const ( + // RawVariableType is the default type for a Zarf package variable + RawVariableType VariableType = "raw" + // FileVariableType is a type for a Zarf package variable that loads its contents from a file + FileVariableType VariableType = "file" +) + +var ( + // IsUppercaseNumberUnderscore is a regex for uppercase, numbers and underscores. + // https://regex101.com/r/tfsEuZ/1 + IsUppercaseNumberUnderscore = regexp.MustCompile(`^[A-Z0-9_]+$`).MatchString ) // ZarfPackageKind is an enum of the different kinds of Zarf packages. @@ -32,9 +49,9 @@ type ZarfPackage struct { // List of components to deploy in this package. Components []ZarfComponent `json:"components" jsonschema:"minItems=1"` // Constant template values applied on deploy for K8s resources. - Constants []variables.Constant `json:"constants,omitempty"` + Constants []Constant `json:"constants,omitempty"` // Variable template values applied on deploy for K8s resources. - Variables []variables.InteractiveVariable `json:"variables,omitempty"` + Variables []InteractiveVariable `json:"variables,omitempty"` } // IsInitConfig returns whether a Zarf package is an init config. @@ -62,6 +79,60 @@ func (pkg ZarfPackage) IsSBOMAble() bool { return false } +// Variable represents a variable that has a value set programmatically +type Variable struct { + // The name to be used for the variable + Name string `json:"name" jsonschema:"pattern=^[A-Z0-9_]+$"` + // Whether to mark this variable as sensitive to not print it in the log + Sensitive bool `json:"sensitive,omitempty"` + // Whether to automatically indent the variable's value (if multiline) when templating. Based on the number of chars before the start of ###ZARF_VAR_. + AutoIndent bool `json:"autoIndent,omitempty"` + // An optional regex pattern that a variable value must match before a package deployment can continue. + Pattern string `json:"pattern,omitempty"` + // Changes the handling of a variable to load contents differently (i.e. from a file rather than as a raw variable - templated files should be kept below 1 MiB) + Type VariableType `json:"type,omitempty" jsonschema:"enum=raw,enum=file"` +} + +// InteractiveVariable is a variable that can be used to prompt a user for more information +type InteractiveVariable struct { + Variable `json:",inline"` + // A description of the variable to be used when prompting the user a value + Description string `json:"description,omitempty"` + // The default value to use for the variable + Default string `json:"default,omitempty"` + // Whether to prompt the user for input for this variable + Prompt bool `json:"prompt,omitempty"` +} + +// Constant are constants that can be used to dynamically template K8s resources or run in actions. +type Constant struct { + // The name to be used for the constant + Name string `json:"name" jsonschema:"pattern=^[A-Z0-9_]+$"` + // The value to set for the constant during deploy + Value string `json:"value"` + // A description of the constant to explain its purpose on package create or deploy confirmation prompts + Description string `json:"description,omitempty"` + // Whether to automatically indent the variable's value (if multiline) when templating. Based on the number of chars before the start of ###ZARF_CONST_. + AutoIndent bool `json:"autoIndent,omitempty"` + // An optional regex pattern that a constant value must match before a package can be created. + Pattern string `json:"pattern,omitempty"` +} + +// SetVariable tracks internal variables that have been set during this run of Zarf +type SetVariable struct { + Variable `json:",inline"` + // The value the variable is currently set with + Value string `json:"value"` +} + +// Validate runs all validation checks on a package constant. +func (c Constant) Validate() error { + if !regexp.MustCompile(c.Pattern).MatchString(c.Value) { + return fmt.Errorf("provided value for constant %s does not match pattern %s", c.Name, c.Pattern) + } + return nil +} + // ZarfMetadata lists information about the current ZarfPackage. type ZarfMetadata struct { // Name to identify this Zarf package. diff --git a/src/api/v1alpha1/validate.go b/src/api/v1alpha1/validate.go index 391e0f000d..7d6ad66c8d 100644 --- a/src/api/v1alpha1/validate.go +++ b/src/api/v1alpha1/validate.go @@ -12,7 +12,6 @@ import ( "strings" "github.com/defenseunicorns/pkg/helpers/v2" - "github.com/zarf-dev/zarf/src/config/lang" "k8s.io/apimachinery/pkg/util/validation" ) @@ -48,16 +47,46 @@ const ( errChartReleaseNameEmpty = "release name empty, unable to fallback to chart name" ) +const ( + PkgValidateErrInitNoYOLO = "sorry, you can't YOLO an init package" + PkgValidateErrConstant = "invalid package constant: %w" + PkgValidateErrYOLONoOCI = "OCI images not allowed in YOLO" + PkgValidateErrYOLONoGit = "git repos not allowed in YOLO" + PkgValidateErrYOLONoArch = "cluster architecture not allowed in YOLO" + PkgValidateErrYOLONoDistro = "cluster distros not allowed in YOLO" + PkgValidateErrComponentNameNotUnique = "component name %q is not unique" + PkgValidateErrComponentReqDefault = "component %q cannot be both required and default" + PkgValidateErrComponentReqGrouped = "component %q cannot be both required and grouped" + PkgValidateErrChartNameNotUnique = "chart name %q is not unique" + PkgValidateErrChart = "invalid chart definition: %w" + PkgValidateErrManifestNameNotUnique = "manifest name %q is not unique" + PkgValidateErrManifest = "invalid manifest definition: %w" + PkgValidateErrGroupMultipleDefaults = "group %q has multiple defaults (%q, %q)" + PkgValidateErrGroupOneComponent = "group %q only has one component (%q)" + PkgValidateTemplateDeprecation = "Package template %q is using the deprecated syntax ###ZARF_PKG_VAR_%s###. This will be removed in Zarf v1.0.0. Please update to ###ZARF_PKG_TMPL_%s###." + PkgValidateErrAction = "invalid action: %w" + PkgValidateErrActionCmdWait = "action %q cannot be both a command and wait action" + PkgValidateErrActionClusterNetwork = "a single wait action must contain only one of cluster or network" + PkgValidateErrChartName = "chart %q exceed the maximum length of %d characters" + PkgValidateErrChartNamespaceMissing = "chart %q must include a namespace" + PkgValidateErrChartURLOrPath = "chart %q must have either a url or localPath" + PkgValidateErrChartVersion = "chart %q must include a chart version" + PkgValidateErrImportDefinition = "invalid imported definition for %s: %s" + PkgValidateErrManifestFileOrKustomize = "manifest %q must have at least one file or kustomization" + PkgValidateErrManifestNameLength = "manifest %q exceed the maximum length of %d characters" + PkgValidateErrVariable = "invalid package variable: %w" +) + // Validate runs all validation checks on the package. func (pkg ZarfPackage) Validate() error { var err error if pkg.Kind == ZarfInitConfig && pkg.Metadata.YOLO { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrInitNoYOLO)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrInitNoYOLO)) } for _, constant := range pkg.Constants { if varErr := constant.Validate(); varErr != nil { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrConstant, varErr)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrConstant, varErr)) } } @@ -68,19 +97,19 @@ func (pkg ZarfPackage) Validate() error { if pkg.Metadata.YOLO { for _, component := range pkg.Components { if len(component.Images) > 0 { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrYOLONoOCI)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrYOLONoOCI)) } if len(component.Repos) > 0 { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrYOLONoGit)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrYOLONoGit)) } if component.Only.Cluster.Architecture != "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrYOLONoArch)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrYOLONoArch)) } if len(component.Only.Cluster.Distros) > 0 { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrYOLONoDistro)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrYOLONoDistro)) } } } @@ -88,16 +117,16 @@ func (pkg ZarfPackage) Validate() error { for _, component := range pkg.Components { // ensure component name is unique if _, ok := uniqueComponentNames[component.Name]; ok { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrComponentNameNotUnique, component.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrComponentNameNotUnique, component.Name)) } uniqueComponentNames[component.Name] = true if component.IsRequired() { if component.Default { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrComponentReqDefault, component.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrComponentReqDefault, component.Name)) } if component.DeprecatedGroup != "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrComponentReqGrouped, component.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrComponentReqGrouped, component.Name)) } } @@ -105,12 +134,12 @@ func (pkg ZarfPackage) Validate() error { for _, chart := range component.Charts { // ensure chart name is unique if _, ok := uniqueChartNames[chart.Name]; ok { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrChartNameNotUnique, chart.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrChartNameNotUnique, chart.Name)) } uniqueChartNames[chart.Name] = true if chartErr := chart.Validate(); chartErr != nil { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrChart, chartErr)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrChart, chartErr)) } } @@ -118,12 +147,12 @@ func (pkg ZarfPackage) Validate() error { for _, manifest := range component.Manifests { // ensure manifest name is unique if _, ok := uniqueManifestNames[manifest.Name]; ok { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrManifestNameNotUnique, manifest.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrManifestNameNotUnique, manifest.Name)) } uniqueManifestNames[manifest.Name] = true if manifestErr := manifest.Validate(); manifestErr != nil { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrManifest, manifestErr)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrManifest, manifestErr)) } } @@ -135,7 +164,7 @@ func (pkg ZarfPackage) Validate() error { if component.DeprecatedGroup != "" { if component.Default { if _, ok := groupDefault[component.DeprecatedGroup]; ok { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrGroupMultipleDefaults, component.DeprecatedGroup, groupDefault[component.DeprecatedGroup], component.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrGroupMultipleDefaults, component.DeprecatedGroup, groupDefault[component.DeprecatedGroup], component.Name)) } groupDefault[component.DeprecatedGroup] = component.Name } @@ -145,7 +174,7 @@ func (pkg ZarfPackage) Validate() error { for groupKey, componentNames := range groupedComponents { if len(componentNames) == 1 { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrGroupOneComponent, groupKey, componentNames[0])) + err = errors.Join(err, fmt.Errorf(PkgValidateErrGroupOneComponent, groupKey, componentNames[0])) } } @@ -180,19 +209,19 @@ func (c ZarfComponent) Validate() error { // ensure path or url is provided if path == "" && url == "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrImportDefinition, c.Name, "neither a path nor a URL was provided")) + err = errors.Join(err, fmt.Errorf(PkgValidateErrImportDefinition, c.Name, "neither a path nor a URL was provided")) } // ensure path and url are not both provided if path != "" && url != "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrImportDefinition, c.Name, "both a path and a URL were provided")) + err = errors.Join(err, fmt.Errorf(PkgValidateErrImportDefinition, c.Name, "both a path and a URL were provided")) } // validation for path if url == "" && path != "" { // ensure path is not an absolute path if filepath.IsAbs(path) { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrImportDefinition, c.Name, "path cannot be an absolute path")) + err = errors.Join(err, fmt.Errorf(PkgValidateErrImportDefinition, c.Name, "path cannot be an absolute path")) } } @@ -200,7 +229,7 @@ func (c ZarfComponent) Validate() error { if url != "" && path == "" { ok := helpers.IsOCIURL(url) if !ok { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrImportDefinition, c.Name, "URL is not a valid OCI URL")) + err = errors.Join(err, fmt.Errorf(PkgValidateErrImportDefinition, c.Name, "URL is not a valid OCI URL")) } } @@ -227,7 +256,7 @@ func (as ZarfComponentActionSet) Validate() error { validate := func(actions []ZarfComponentAction) { for _, action := range actions { if actionErr := action.Validate(); actionErr != nil { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrAction, actionErr)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrAction, actionErr)) } } } @@ -246,17 +275,17 @@ func (action ZarfComponentAction) Validate() error { if action.Wait != nil { // Validate only cmd or wait, not both if action.Cmd != "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrActionCmdWait, action.Cmd)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrActionCmdWait, action.Cmd)) } // Validate only cluster or network, not both if action.Wait.Cluster != nil && action.Wait.Network != nil { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrActionClusterNetwork)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrActionClusterNetwork)) } // Validate at least one of cluster or network if action.Wait.Cluster == nil && action.Wait.Network == nil { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrActionClusterNetwork)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrActionClusterNetwork)) } } @@ -291,24 +320,24 @@ func (chart ZarfChart) Validate() error { var err error if len(chart.Name) > ZarfMaxChartNameLength { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrChartName, chart.Name, ZarfMaxChartNameLength)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrChartName, chart.Name, ZarfMaxChartNameLength)) } if chart.Namespace == "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrChartNamespaceMissing, chart.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrChartNamespaceMissing, chart.Name)) } // Must have a url or localPath (and not both) if chart.URL != "" && chart.LocalPath != "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrChartURLOrPath, chart.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrChartURLOrPath, chart.Name)) } if chart.URL == "" && chart.LocalPath == "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrChartURLOrPath, chart.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrChartURLOrPath, chart.Name)) } if chart.Version == "" { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrChartVersion, chart.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrChartVersion, chart.Name)) } if nameErr := validateReleaseName(chart.Name, chart.ReleaseName); nameErr != nil { @@ -323,11 +352,11 @@ func (manifest ZarfManifest) Validate() error { var err error if len(manifest.Name) > ZarfMaxChartNameLength { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrManifestNameLength, manifest.Name, ZarfMaxChartNameLength)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrManifestNameLength, manifest.Name, ZarfMaxChartNameLength)) } if len(manifest.Files) < 1 && len(manifest.Kustomizations) < 1 { - err = errors.Join(err, fmt.Errorf(lang.PkgValidateErrManifestFileOrKustomize, manifest.Name)) + err = errors.Join(err, fmt.Errorf(PkgValidateErrManifestFileOrKustomize, manifest.Name)) } return err diff --git a/src/api/v1alpha1/validate_test.go b/src/api/v1alpha1/validate_test.go index de3f675c2e..1cdd11e7f0 100644 --- a/src/api/v1alpha1/validate_test.go +++ b/src/api/v1alpha1/validate_test.go @@ -12,8 +12,6 @@ import ( "github.com/defenseunicorns/pkg/helpers/v2" "github.com/stretchr/testify/require" - "github.com/zarf-dev/zarf/src/config/lang" - "github.com/zarf-dev/zarf/src/pkg/variables" ) func TestZarfPackageValidate(t *testing.T) { @@ -81,7 +79,7 @@ func TestZarfPackageValidate(t *testing.T) { Name: "duplicate", }, }, - Constants: []variables.Constant{ + Constants: []Constant{ { Name: "BAD", Pattern: "^good_val$", @@ -90,14 +88,14 @@ func TestZarfPackageValidate(t *testing.T) { }, }, expectedErrs: []string{ - fmt.Errorf(lang.PkgValidateErrConstant, fmt.Errorf(lang.PkgValidateErrPkgConstantPattern, "BAD", "^good_val$")).Error(), - fmt.Sprintf(lang.PkgValidateErrComponentReqDefault, "invalid"), - fmt.Sprintf(lang.PkgValidateErrChartNameNotUnique, "chart1"), - fmt.Sprintf(lang.PkgValidateErrManifestNameNotUnique, "manifest1"), - fmt.Sprintf(lang.PkgValidateErrComponentReqGrouped, "required-in-group"), - fmt.Sprintf(lang.PkgValidateErrComponentNameNotUnique, "duplicate"), - fmt.Sprintf(lang.PkgValidateErrGroupOneComponent, "a-group", "required-in-group"), - fmt.Sprintf(lang.PkgValidateErrGroupMultipleDefaults, "multi-default", "multi-default", "multi-default-2"), + fmt.Errorf(PkgValidateErrConstant, fmt.Errorf("provided value for constant %s does not match pattern %s", "BAD", "^good_val$")).Error(), + fmt.Sprintf(PkgValidateErrComponentReqDefault, "invalid"), + fmt.Sprintf(PkgValidateErrChartNameNotUnique, "chart1"), + fmt.Sprintf(PkgValidateErrManifestNameNotUnique, "manifest1"), + fmt.Sprintf(PkgValidateErrComponentReqGrouped, "required-in-group"), + fmt.Sprintf(PkgValidateErrComponentNameNotUnique, "duplicate"), + fmt.Sprintf(PkgValidateErrGroupOneComponent, "a-group", "required-in-group"), + fmt.Sprintf(PkgValidateErrGroupMultipleDefaults, "multi-default", "multi-default", "multi-default-2"), }, }, { @@ -123,11 +121,11 @@ func TestZarfPackageValidate(t *testing.T) { }, }, expectedErrs: []string{ - lang.PkgValidateErrInitNoYOLO, - lang.PkgValidateErrYOLONoOCI, - lang.PkgValidateErrYOLONoGit, - lang.PkgValidateErrYOLONoArch, - lang.PkgValidateErrYOLONoDistro, + PkgValidateErrInitNoYOLO, + PkgValidateErrYOLONoOCI, + PkgValidateErrYOLONoGit, + PkgValidateErrYOLONoArch, + PkgValidateErrYOLONoDistro, }, }, } @@ -163,12 +161,12 @@ func TestValidateManifest(t *testing.T) { { name: "long name", manifest: ZarfManifest{Name: longName, Files: []string{"a-file"}}, - expectedErrs: []string{fmt.Sprintf(lang.PkgValidateErrManifestNameLength, longName, ZarfMaxChartNameLength)}, + expectedErrs: []string{fmt.Sprintf(PkgValidateErrManifestNameLength, longName, ZarfMaxChartNameLength)}, }, { name: "no files or kustomize", manifest: ZarfManifest{Name: "nothing-there"}, - expectedErrs: []string{fmt.Sprintf(lang.PkgValidateErrManifestFileOrKustomize, "nothing-there")}, + expectedErrs: []string{fmt.Sprintf(PkgValidateErrManifestFileOrKustomize, "nothing-there")}, }, } for _, tt := range tests { @@ -268,23 +266,23 @@ func TestValidateChart(t *testing.T) { name: "long name", chart: ZarfChart{Name: longName, Namespace: "whatever", URL: "http://whatever", Version: "v1.0.0"}, expectedErrs: []string{ - fmt.Sprintf(lang.PkgValidateErrChartName, longName, ZarfMaxChartNameLength), + fmt.Sprintf(PkgValidateErrChartName, longName, ZarfMaxChartNameLength), }, }, { name: "no url, local path, version, or namespace", chart: ZarfChart{Name: "invalid"}, expectedErrs: []string{ - fmt.Sprintf(lang.PkgValidateErrChartNamespaceMissing, "invalid"), - fmt.Sprintf(lang.PkgValidateErrChartURLOrPath, "invalid"), - fmt.Sprintf(lang.PkgValidateErrChartVersion, "invalid"), + fmt.Sprintf(PkgValidateErrChartNamespaceMissing, "invalid"), + fmt.Sprintf(PkgValidateErrChartURLOrPath, "invalid"), + fmt.Sprintf(PkgValidateErrChartVersion, "invalid"), }, }, { name: "both url and local path", chart: ZarfChart{Name: "invalid", Namespace: "whatever", URL: "http://whatever", LocalPath: "wherever", Version: "v1.0.0"}, expectedErrs: []string{ - fmt.Sprintf(lang.PkgValidateErrChartURLOrPath, "invalid"), + fmt.Sprintf(PkgValidateErrChartURLOrPath, "invalid"), }, }, { @@ -361,7 +359,7 @@ func TestValidateComponentActions(t *testing.T) { Before: []ZarfComponentAction{ { Cmd: "echo 'invalid setVariable'", - SetVariables: []variables.Variable{{Name: "VAR"}}, + SetVariables: []Variable{{Name: "VAR"}}, }, }, }, @@ -403,10 +401,10 @@ func TestValidateComponentActions(t *testing.T) { }, }, expectedErrs: []string{ - fmt.Errorf(lang.PkgValidateErrAction, fmt.Errorf(lang.PkgValidateErrActionCmdWait, "create")).Error(), - fmt.Errorf(lang.PkgValidateErrAction, fmt.Errorf(lang.PkgValidateErrActionCmdWait, "deploy")).Error(), - fmt.Errorf(lang.PkgValidateErrAction, fmt.Errorf(lang.PkgValidateErrActionCmdWait, "remove")).Error(), - fmt.Errorf(lang.PkgValidateErrAction, fmt.Errorf(lang.PkgValidateErrActionCmdWait, "remove2")).Error(), + fmt.Errorf(PkgValidateErrAction, fmt.Errorf(PkgValidateErrActionCmdWait, "create")).Error(), + fmt.Errorf(PkgValidateErrAction, fmt.Errorf(PkgValidateErrActionCmdWait, "deploy")).Error(), + fmt.Errorf(PkgValidateErrAction, fmt.Errorf(PkgValidateErrActionCmdWait, "remove")).Error(), + fmt.Errorf(PkgValidateErrAction, fmt.Errorf(PkgValidateErrActionCmdWait, "remove2")).Error(), }, }, } @@ -444,8 +442,8 @@ func TestValidateComponentAction(t *testing.T) { Wait: &ZarfComponentActionWait{}, }, expectedErrs: []string{ - fmt.Sprintf(lang.PkgValidateErrActionCmdWait, "ls"), - lang.PkgValidateErrActionClusterNetwork, + fmt.Sprintf(PkgValidateErrActionCmdWait, "ls"), + PkgValidateErrActionClusterNetwork, }, }, { @@ -453,7 +451,7 @@ func TestValidateComponentAction(t *testing.T) { action: ZarfComponentAction{ Wait: &ZarfComponentActionWait{Cluster: &ZarfComponentActionWaitCluster{}, Network: &ZarfComponentActionWaitNetwork{}}, }, - expectedErrs: []string{fmt.Sprintf(lang.PkgValidateErrActionClusterNetwork)}, + expectedErrs: []string{fmt.Sprintf(PkgValidateErrActionClusterNetwork)}, }, } @@ -507,7 +505,7 @@ func TestValidateZarfComponent(t *testing.T) { Name: "neither", }, expectedErrs: []string{ - fmt.Sprintf(lang.PkgValidateErrImportDefinition, "neither", "neither a path nor a URL was provided"), + fmt.Sprintf(PkgValidateErrImportDefinition, "neither", "neither a path nor a URL was provided"), }, }, { @@ -520,7 +518,7 @@ func TestValidateZarfComponent(t *testing.T) { }, }, expectedErrs: []string{ - fmt.Sprintf(lang.PkgValidateErrImportDefinition, "both", "both a path and a URL were provided"), + fmt.Sprintf(PkgValidateErrImportDefinition, "both", "both a path and a URL were provided"), }, }, { @@ -532,7 +530,7 @@ func TestValidateZarfComponent(t *testing.T) { }, }, expectedErrs: []string{ - fmt.Sprintf(lang.PkgValidateErrImportDefinition, "abs-path", "path cannot be an absolute path"), + fmt.Sprintf(PkgValidateErrImportDefinition, "abs-path", "path cannot be an absolute path"), }, }, { @@ -544,7 +542,7 @@ func TestValidateZarfComponent(t *testing.T) { }, }, expectedErrs: []string{ - fmt.Sprintf(lang.PkgValidateErrImportDefinition, "bad-url", "URL is not a valid OCI URL"), + fmt.Sprintf(PkgValidateErrImportDefinition, "bad-url", "URL is not a valid OCI URL"), }, }, } diff --git a/src/config/lang/english.go b/src/config/lang/english.go index 8e821b03f2..1bca5f773a 100644 --- a/src/config/lang/english.go +++ b/src/config/lang/english.go @@ -30,7 +30,8 @@ const ( // Lint messages const ( - UnsetVarLintWarning = "There are templates that are not set and won't be evaluated during lint" + UnsetVarLintWarning = "There are templates that are not set and won't be evaluated during lint" + PkgValidateTemplateDeprecation = "Package template %q is using the deprecated syntax ###ZARF_PKG_VAR_%s###. This will be removed in Zarf v1.0.0. Please update to ###ZARF_PKG_TMPL_%s###." ) // Zarf CLI commands. @@ -619,38 +620,6 @@ const ( PkgCreateErrDifferentialNoVersion = "unable to create differential package. Please ensure both package versions are set" ) -// Package validate -const ( - PkgValidateTemplateDeprecation = "Package template %q is using the deprecated syntax ###ZARF_PKG_VAR_%s###. This will be removed in Zarf v1.0.0. Please update to ###ZARF_PKG_TMPL_%s###." - PkgValidateErrAction = "invalid action: %w" - PkgValidateErrActionCmdWait = "action %q cannot be both a command and wait action" - PkgValidateErrActionClusterNetwork = "a single wait action must contain only one of cluster or network" - PkgValidateErrChart = "invalid chart definition: %w" - PkgValidateErrChartName = "chart %q exceed the maximum length of %d characters" - PkgValidateErrChartNameNotUnique = "chart name %q is not unique" - PkgValidateErrChartNamespaceMissing = "chart %q must include a namespace" - PkgValidateErrChartURLOrPath = "chart %q must have either a url or localPath" - PkgValidateErrChartVersion = "chart %q must include a chart version" - PkgValidateErrComponentNameNotUnique = "component name %q is not unique" - PkgValidateErrComponentReqDefault = "component %q cannot be both required and default" - PkgValidateErrComponentReqGrouped = "component %q cannot be both required and grouped" - PkgValidateErrGroupMultipleDefaults = "group %q has multiple defaults (%q, %q)" - PkgValidateErrGroupOneComponent = "group %q only has one component (%q)" - PkgValidateErrConstant = "invalid package constant: %w" - PkgValidateErrImportDefinition = "invalid imported definition for %s: %s" - PkgValidateErrInitNoYOLO = "sorry, you can't YOLO an init package" - PkgValidateErrManifest = "invalid manifest definition: %w" - PkgValidateErrManifestFileOrKustomize = "manifest %q must have at least one file or kustomization" - PkgValidateErrManifestNameLength = "manifest %q exceed the maximum length of %d characters" - PkgValidateErrManifestNameNotUnique = "manifest name %q is not unique" - PkgValidateErrPkgConstantPattern = "provided value for constant %q does not match pattern %q" - PkgValidateErrVariable = "invalid package variable: %w" - PkgValidateErrYOLONoArch = "cluster architecture not allowed in YOLO" - PkgValidateErrYOLONoDistro = "cluster distros not allowed in YOLO" - PkgValidateErrYOLONoGit = "git repos not allowed in YOLO" - PkgValidateErrYOLONoOCI = "OCI images not allowed in YOLO" -) - // Collection of reusable error messages. var ( ErrInitNotFound = errors.New("this command requires a zarf-init package, but one was not found on the local system. Re-run the last command again without '--confirm' to download the package") diff --git a/src/internal/packager/helm/zarf.go b/src/internal/packager/helm/zarf.go index 9f72dce5a4..03e4db2ed0 100644 --- a/src/internal/packager/helm/zarf.go +++ b/src/internal/packager/helm/zarf.go @@ -23,7 +23,6 @@ import ( "github.com/zarf-dev/zarf/src/pkg/message" "github.com/zarf-dev/zarf/src/pkg/transform" "github.com/zarf-dev/zarf/src/pkg/utils" - "github.com/zarf-dev/zarf/src/pkg/variables" ) // UpdateZarfRegistryValues updates the Zarf registry deployment with the new state values @@ -103,7 +102,7 @@ func (h *Helm) UpdateZarfAgentValues(ctx context.Context) error { Namespace: "zarf", ReleaseName: release.Name, } - h.variableConfig.SetConstants([]variables.Constant{ + h.variableConfig.SetConstants([]v1alpha1.Constant{ { Name: "AGENT_IMAGE", Value: agentImage.Path, diff --git a/src/internal/packager/template/template.go b/src/internal/packager/template/template.go index 645982865e..872754ce4d 100644 --- a/src/internal/packager/template/template.go +++ b/src/internal/packager/template/template.go @@ -10,6 +10,7 @@ import ( "log/slog" "strings" + "github.com/zarf-dev/zarf/src/api/v1alpha1" "github.com/zarf-dev/zarf/src/types" "github.com/defenseunicorns/pkg/helpers/v2" @@ -26,7 +27,7 @@ const ( // GetZarfVariableConfig gets a variable configuration specific to Zarf func GetZarfVariableConfig() *variables.VariableConfig { - prompt := func(variable variables.InteractiveVariable) (value string, err error) { + prompt := func(variable v1alpha1.InteractiveVariable) (value string, err error) { if config.CommonOptions.Confirm { return variable.Default, nil } diff --git a/src/pkg/cluster/data.go b/src/pkg/cluster/data.go index 8f1687123a..7c57780a26 100644 --- a/src/pkg/cluster/data.go +++ b/src/pkg/cluster/data.go @@ -53,7 +53,7 @@ func (c *Cluster) HandleDataInjection(ctx context.Context, data v1alpha1.ZarfDat } // Get the OS shell to execute commands in - shell, shellArgs := exec.GetOSShell(exec.Shell{Windows: "cmd"}) + shell, shellArgs := exec.GetOSShell(v1alpha1.Shell{Windows: "cmd"}) if _, _, err := exec.Cmd(shell, append(shellArgs, "tar --version")...); err != nil { return fmt.Errorf("unable to execute tar, ensure it is installed in the $PATH: %w", err) diff --git a/src/pkg/interactive/prompt.go b/src/pkg/interactive/prompt.go index c22b05e449..5af5f9c451 100644 --- a/src/pkg/interactive/prompt.go +++ b/src/pkg/interactive/prompt.go @@ -8,8 +8,8 @@ import ( "fmt" "github.com/AlecAivazis/survey/v2" + "github.com/zarf-dev/zarf/src/api/v1alpha1" "github.com/zarf-dev/zarf/src/pkg/message" - "github.com/zarf-dev/zarf/src/pkg/variables" ) // PromptSigPassword prompts the user for the password to their private key @@ -23,7 +23,7 @@ func PromptSigPassword() ([]byte, error) { } // PromptVariable prompts the user for a value for a variable -func PromptVariable(variable variables.InteractiveVariable) (value string, err error) { +func PromptVariable(variable v1alpha1.InteractiveVariable) (value string, err error) { if variable.Description != "" { message.Question(variable.Description) } diff --git a/src/pkg/lint/schema_test.go b/src/pkg/lint/schema_test.go index 9e59dddcd4..675ae384b8 100644 --- a/src/pkg/lint/schema_test.go +++ b/src/pkg/lint/schema_test.go @@ -12,7 +12,6 @@ import ( goyaml "github.com/goccy/go-yaml" "github.com/stretchr/testify/require" "github.com/zarf-dev/zarf/src/api/v1alpha1" - "github.com/zarf-dev/zarf/src/pkg/variables" ) func TestZarfSchema(t *testing.T) { @@ -80,7 +79,7 @@ func TestZarfSchema(t *testing.T) { Before: []v1alpha1.ZarfComponentAction{ { Cmd: "echo 'invalid setVariable'", - SetVariables: []variables.Variable{{Name: "not_uppercase"}}, + SetVariables: []v1alpha1.Variable{{Name: "not_uppercase"}}, }, }, }, @@ -88,19 +87,19 @@ func TestZarfSchema(t *testing.T) { OnSuccess: []v1alpha1.ZarfComponentAction{ { Cmd: "echo 'invalid setVariable'", - SetVariables: []variables.Variable{{Name: "not_uppercase"}}, + SetVariables: []v1alpha1.Variable{{Name: "not_uppercase"}}, }, }, }, }, }, }, - Variables: []variables.InteractiveVariable{ + Variables: []v1alpha1.InteractiveVariable{ { - Variable: variables.Variable{Name: "not_uppercase"}, + Variable: v1alpha1.Variable{Name: "not_uppercase"}, }, }, - Constants: []variables.Constant{ + Constants: []v1alpha1.Constant{ { Name: "not_uppercase", }, diff --git a/src/pkg/packager/actions/actions.go b/src/pkg/packager/actions/actions.go index 4fdf71fdd5..983f2f6bde 100644 --- a/src/pkg/packager/actions/actions.go +++ b/src/pkg/packager/actions/actions.go @@ -70,7 +70,7 @@ func runAction(ctx context.Context, defaultCfg v1alpha1.ZarfComponentActionDefau d := "" action.Dir = &d action.Env = []string{} - action.SetVariables = []variables.Variable{} + action.SetVariables = []v1alpha1.Variable{} } if action.Description != "" { @@ -204,7 +204,7 @@ func convertWaitToCmd(_ context.Context, wait v1alpha1.ZarfComponentActionWait, } // Perform some basic string mutations to make commands more useful. -func actionCmdMutation(_ context.Context, cmd string, shellPref exec.Shell) (string, error) { +func actionCmdMutation(_ context.Context, cmd string, shellPref v1alpha1.Shell) (string, error) { zarfCommand, err := utils.GetFinalExecutableCommand() if err != nil { return cmd, err @@ -274,7 +274,7 @@ func actionGetCfg(_ context.Context, cfg v1alpha1.ZarfComponentActionDefaults, a return cfg } -func actionRun(ctx context.Context, cfg v1alpha1.ZarfComponentActionDefaults, cmd string, shellPref exec.Shell, spinner *message.Spinner) (string, error) { +func actionRun(ctx context.Context, cfg v1alpha1.ZarfComponentActionDefaults, cmd string, shellPref v1alpha1.Shell, spinner *message.Spinner) (string, error) { shell, shellArgs := exec.GetOSShell(shellPref) message.Debugf("Running command in %s: %s", shell, cmd) diff --git a/src/pkg/packager/composer/list.go b/src/pkg/packager/composer/list.go index 8d5531b85e..9d2bce9d97 100644 --- a/src/pkg/packager/composer/list.go +++ b/src/pkg/packager/composer/list.go @@ -16,7 +16,6 @@ import ( "github.com/zarf-dev/zarf/src/pkg/layout" "github.com/zarf-dev/zarf/src/pkg/packager/deprecated" "github.com/zarf-dev/zarf/src/pkg/utils" - "github.com/zarf-dev/zarf/src/pkg/variables" "github.com/zarf-dev/zarf/src/pkg/zoci" ) @@ -26,8 +25,8 @@ type Node struct { index int - vars []variables.InteractiveVariable - consts []variables.Constant + vars []v1alpha1.InteractiveVariable + consts []v1alpha1.Constant relativeToHead string originalPackageName string @@ -96,7 +95,7 @@ func (ic *ImportChain) Tail() *Node { } func (ic *ImportChain) append(c v1alpha1.ZarfComponent, index int, originalPackageName string, - relativeToHead string, vars []variables.InteractiveVariable, consts []variables.Constant) { + relativeToHead string, vars []v1alpha1.InteractiveVariable, consts []v1alpha1.Constant) { node := &Node{ ZarfComponent: c, index: index, @@ -313,8 +312,8 @@ func (ic *ImportChain) Compose(ctx context.Context) (composed *v1alpha1.ZarfComp } // MergeVariables merges variables from the import chain -func (ic *ImportChain) MergeVariables(existing []variables.InteractiveVariable) (merged []variables.InteractiveVariable) { - exists := func(v1 variables.InteractiveVariable, v2 variables.InteractiveVariable) bool { +func (ic *ImportChain) MergeVariables(existing []v1alpha1.InteractiveVariable) (merged []v1alpha1.InteractiveVariable) { + exists := func(v1 v1alpha1.InteractiveVariable, v2 v1alpha1.InteractiveVariable) bool { return v1.Name == v2.Name } @@ -330,8 +329,8 @@ func (ic *ImportChain) MergeVariables(existing []variables.InteractiveVariable) } // MergeConstants merges constants from the import chain -func (ic *ImportChain) MergeConstants(existing []variables.Constant) (merged []variables.Constant) { - exists := func(c1 variables.Constant, c2 variables.Constant) bool { +func (ic *ImportChain) MergeConstants(existing []v1alpha1.Constant) (merged []v1alpha1.Constant) { + exists := func(c1 v1alpha1.Constant, c2 v1alpha1.Constant) bool { return c1.Name == c2.Name } diff --git a/src/pkg/packager/composer/list_test.go b/src/pkg/packager/composer/list_test.go index 56e266738b..beff63b524 100644 --- a/src/pkg/packager/composer/list_test.go +++ b/src/pkg/packager/composer/list_test.go @@ -14,7 +14,6 @@ import ( "github.com/stretchr/testify/require" "github.com/zarf-dev/zarf/src/api/v1alpha1" "github.com/zarf-dev/zarf/src/api/v1alpha1/extensions" - "github.com/zarf-dev/zarf/src/pkg/variables" ) func TestNewImportChain(t *testing.T) { @@ -251,16 +250,16 @@ func TestMerging(t *testing.T) { t.Parallel() head := Node{ - vars: []variables.InteractiveVariable{ + vars: []v1alpha1.InteractiveVariable{ { - Variable: variables.Variable{Name: "TEST"}, + Variable: v1alpha1.Variable{Name: "TEST"}, Default: "head", }, { - Variable: variables.Variable{Name: "HEAD"}, + Variable: v1alpha1.Variable{Name: "HEAD"}, }, }, - consts: []variables.Constant{ + consts: []v1alpha1.Constant{ { Name: "TEST", Value: "head", @@ -271,16 +270,16 @@ func TestMerging(t *testing.T) { }, } tail := Node{ - vars: []variables.InteractiveVariable{ + vars: []v1alpha1.InteractiveVariable{ { - Variable: variables.Variable{Name: "TEST"}, + Variable: v1alpha1.Variable{Name: "TEST"}, Default: "tail", }, { - Variable: variables.Variable{Name: "TAIL"}, + Variable: v1alpha1.Variable{Name: "TAIL"}, }, }, - consts: []variables.Constant{ + consts: []v1alpha1.Constant{ { Name: "TEST", Value: "tail", @@ -297,30 +296,30 @@ func TestMerging(t *testing.T) { tests := []struct { name string ic *ImportChain - existingVars []variables.InteractiveVariable - existingConsts []variables.Constant - expectedVars []variables.InteractiveVariable - expectedConsts []variables.Constant + existingVars []v1alpha1.InteractiveVariable + existingConsts []v1alpha1.Constant + expectedVars []v1alpha1.InteractiveVariable + expectedConsts []v1alpha1.Constant }{ { name: "empty-ic", ic: &ImportChain{}, - existingVars: []variables.InteractiveVariable{ + existingVars: []v1alpha1.InteractiveVariable{ { - Variable: variables.Variable{Name: "TEST"}, + Variable: v1alpha1.Variable{Name: "TEST"}, }, }, - existingConsts: []variables.Constant{ + existingConsts: []v1alpha1.Constant{ { Name: "TEST", }, }, - expectedVars: []variables.InteractiveVariable{ + expectedVars: []v1alpha1.InteractiveVariable{ { - Variable: variables.Variable{Name: "TEST"}, + Variable: v1alpha1.Variable{Name: "TEST"}, }, }, - expectedConsts: []variables.Constant{ + expectedConsts: []v1alpha1.Constant{ { Name: "TEST", }, @@ -329,21 +328,21 @@ func TestMerging(t *testing.T) { { name: "no-existing", ic: testIC, - existingVars: []variables.InteractiveVariable{}, - existingConsts: []variables.Constant{}, - expectedVars: []variables.InteractiveVariable{ + existingVars: []v1alpha1.InteractiveVariable{}, + existingConsts: []v1alpha1.Constant{}, + expectedVars: []v1alpha1.InteractiveVariable{ { - Variable: variables.Variable{Name: "TEST"}, + Variable: v1alpha1.Variable{Name: "TEST"}, Default: "head", }, { - Variable: variables.Variable{Name: "HEAD"}, + Variable: v1alpha1.Variable{Name: "HEAD"}, }, { - Variable: variables.Variable{Name: "TAIL"}, + Variable: v1alpha1.Variable{Name: "TAIL"}, }, }, - expectedConsts: []variables.Constant{ + expectedConsts: []v1alpha1.Constant{ { Name: "TEST", Value: "head", @@ -359,16 +358,16 @@ func TestMerging(t *testing.T) { { name: "with-existing", ic: testIC, - existingVars: []variables.InteractiveVariable{ + existingVars: []v1alpha1.InteractiveVariable{ { - Variable: variables.Variable{Name: "TEST"}, + Variable: v1alpha1.Variable{Name: "TEST"}, Default: "existing", }, { - Variable: variables.Variable{Name: "EXISTING"}, + Variable: v1alpha1.Variable{Name: "EXISTING"}, }, }, - existingConsts: []variables.Constant{ + existingConsts: []v1alpha1.Constant{ { Name: "TEST", Value: "existing", @@ -377,22 +376,22 @@ func TestMerging(t *testing.T) { Name: "EXISTING", }, }, - expectedVars: []variables.InteractiveVariable{ + expectedVars: []v1alpha1.InteractiveVariable{ { - Variable: variables.Variable{Name: "TEST"}, + Variable: v1alpha1.Variable{Name: "TEST"}, Default: "existing", }, { - Variable: variables.Variable{Name: "EXISTING"}, + Variable: v1alpha1.Variable{Name: "EXISTING"}, }, { - Variable: variables.Variable{Name: "HEAD"}, + Variable: v1alpha1.Variable{Name: "HEAD"}, }, { - Variable: variables.Variable{Name: "TAIL"}, + Variable: v1alpha1.Variable{Name: "TAIL"}, }, }, - expectedConsts: []variables.Constant{ + expectedConsts: []v1alpha1.Constant{ { Name: "TEST", Value: "existing", diff --git a/src/pkg/packager/creator/template.go b/src/pkg/packager/creator/template.go index 65d0d56c99..1c5037302f 100644 --- a/src/pkg/packager/creator/template.go +++ b/src/pkg/packager/creator/template.go @@ -12,7 +12,6 @@ import ( "github.com/zarf-dev/zarf/src/config/lang" "github.com/zarf-dev/zarf/src/pkg/interactive" "github.com/zarf-dev/zarf/src/pkg/utils" - "github.com/zarf-dev/zarf/src/pkg/variables" ) // FillActiveTemplate merges user-specified variables into the configuration templates of a zarf.yaml. @@ -33,8 +32,8 @@ func FillActiveTemplate(pkg v1alpha1.ZarfPackage, setVariables map[string]string _, present := setVariables[key] if !present && !config.CommonOptions.Confirm { - setVal, err := interactive.PromptVariable(variables.InteractiveVariable{ - Variable: variables.Variable{Name: key}, + setVal, err := interactive.PromptVariable(v1alpha1.InteractiveVariable{ + Variable: v1alpha1.Variable{Name: key}, }) if err != nil { return err diff --git a/src/pkg/packager/deploy_test.go b/src/pkg/packager/deploy_test.go index d0802a4ac5..a1f32aa346 100644 --- a/src/pkg/packager/deploy_test.go +++ b/src/pkg/packager/deploy_test.go @@ -9,7 +9,6 @@ import ( "github.com/stretchr/testify/require" "github.com/zarf-dev/zarf/src/api/v1alpha1" "github.com/zarf-dev/zarf/src/pkg/packager/sources" - "github.com/zarf-dev/zarf/src/pkg/variables" "github.com/zarf-dev/zarf/src/types" ) @@ -216,7 +215,7 @@ func TestGenerateValuesOverrides(t *testing.T) { p, err := New(&types.PackagerConfig{DeployOpts: tt.deployOpts}, WithSource(&sources.TarballSource{})) require.NoError(t, err) for k, v := range tt.setVariables { - p.variableConfig.SetVariable(k, v, false, false, variables.RawVariableType) + p.variableConfig.SetVariable(k, v, false, false, v1alpha1.RawVariableType) } got, err := p.generateValuesOverrides(tt.chart, tt.componentName) diff --git a/src/pkg/packager/deprecated/pluralize-set-variable.go b/src/pkg/packager/deprecated/pluralize-set-variable.go index 04981f2790..2890038336 100644 --- a/src/pkg/packager/deprecated/pluralize-set-variable.go +++ b/src/pkg/packager/deprecated/pluralize-set-variable.go @@ -8,7 +8,6 @@ import ( "fmt" "github.com/zarf-dev/zarf/src/api/v1alpha1" - "github.com/zarf-dev/zarf/src/pkg/variables" ) func migrateSetVariableToSetVariables(c v1alpha1.ZarfComponent) (v1alpha1.ZarfComponent, string) { @@ -18,7 +17,7 @@ func migrateSetVariableToSetVariables(c v1alpha1.ZarfComponent) (v1alpha1.ZarfCo for i := range actions { if actions[i].DeprecatedSetVariable != "" && len(actions[i].SetVariables) < 1 { hasSetVariable = true - actions[i].SetVariables = []variables.Variable{ + actions[i].SetVariables = []v1alpha1.Variable{ { Name: actions[i].DeprecatedSetVariable, Sensitive: false, diff --git a/src/pkg/utils/exec/exec.go b/src/pkg/utils/exec/exec.go index 4ac90b6a92..3f76e3f23c 100644 --- a/src/pkg/utils/exec/exec.go +++ b/src/pkg/utils/exec/exec.go @@ -16,6 +16,8 @@ import ( "strings" "sync" "testing" + + "github.com/zarf-dev/zarf/src/api/v1alpha1" ) // Config is a struct for configuring the Cmd function. @@ -28,13 +30,6 @@ type Config struct { Stderr io.Writer } -// Shell represents the desired shell to use for a given command -type Shell struct { - Windows string `json:"windows,omitempty" jsonschema:"description=(default 'powershell') Indicates a preference for the shell to use on Windows systems (note that choosing 'cmd' will turn off migrations like touch -> New-Item),example=powershell,example=cmd,example=pwsh,example=sh,example=bash,example=gsh"` - Linux string `json:"linux,omitempty" jsonschema:"description=(default 'sh') Indicates a preference for the shell to use on Linux systems,example=sh,example=bash,example=fish,example=zsh,example=pwsh"` - Darwin string `json:"darwin,omitempty" jsonschema:"description=(default 'sh') Indicates a preference for the shell to use on macOS systems,example=sh,example=bash,example=fish,example=zsh,example=pwsh"` -} - // PrintCfg is a helper function for returning a Config struct with Print set to true. func PrintCfg() Config { return Config{Print: true} @@ -161,7 +156,7 @@ func LaunchURL(url string) error { } // GetOSShell returns the shell and shellArgs based on the current OS -func GetOSShell(shellPref Shell) (string, []string) { +func GetOSShell(shellPref v1alpha1.Shell) (string, []string) { var shell string var shellArgs []string powershellShellArgs := []string{"-Command", "$ErrorActionPreference = 'Stop';"} diff --git a/src/pkg/utils/wait.go b/src/pkg/utils/wait.go index a6727d9e35..dbcbbf0267 100644 --- a/src/pkg/utils/wait.go +++ b/src/pkg/utils/wait.go @@ -14,6 +14,7 @@ import ( "strings" "time" + "github.com/zarf-dev/zarf/src/api/v1alpha1" "github.com/zarf-dev/zarf/src/pkg/utils/exec" "github.com/zarf-dev/zarf/src/pkg/message" @@ -77,7 +78,7 @@ func ExecuteWait(waitTimeout, waitNamespace, condition, kind, identifier string, spinner := message.NewProgressSpinner(existMsg) // Get the OS shell to execute commands in - shell, shellArgs := exec.GetOSShell(exec.Shell{Windows: "cmd"}) + shell, shellArgs := exec.GetOSShell(v1alpha1.Shell{Windows: "cmd"}) defer spinner.Stop() diff --git a/src/pkg/variables/common.go b/src/pkg/variables/common.go index ae2bb5fac5..9c66662901 100644 --- a/src/pkg/variables/common.go +++ b/src/pkg/variables/common.go @@ -6,6 +6,8 @@ package variables import ( "log/slog" + + "github.com/zarf-dev/zarf/src/api/v1alpha1" ) // VariableConfig represents a value to be templated into a text file. @@ -14,14 +16,14 @@ type VariableConfig struct { applicationTemplates map[string]*TextTemplate setVariableMap SetVariableMap - constants []Constant + constants []v1alpha1.Constant - prompt func(variable InteractiveVariable) (value string, err error) + prompt func(variable v1alpha1.InteractiveVariable) (value string, err error) logger *slog.Logger } // New creates a new VariableConfig -func New(templatePrefix string, prompt func(variable InteractiveVariable) (value string, err error), logger *slog.Logger) *VariableConfig { +func New(templatePrefix string, prompt func(variable v1alpha1.InteractiveVariable) (value string, err error), logger *slog.Logger) *VariableConfig { return &VariableConfig{ templatePrefix: templatePrefix, applicationTemplates: make(map[string]*TextTemplate), @@ -37,6 +39,6 @@ func (vc *VariableConfig) SetApplicationTemplates(applicationTemplates map[strin } // SetConstants sets the constants for a variable config (templated as PREFIX_CONST_NAME) -func (vc *VariableConfig) SetConstants(constants []Constant) { +func (vc *VariableConfig) SetConstants(constants []v1alpha1.Constant) { vc.constants = constants } diff --git a/src/pkg/variables/templates.go b/src/pkg/variables/templates.go index e350d77c28..f0153d0baa 100644 --- a/src/pkg/variables/templates.go +++ b/src/pkg/variables/templates.go @@ -12,13 +12,14 @@ import ( "strings" "github.com/defenseunicorns/pkg/helpers/v2" + "github.com/zarf-dev/zarf/src/api/v1alpha1" ) // TextTemplate represents a value to be templated into a text file. type TextTemplate struct { Sensitive bool AutoIndent bool - Type VariableType + Type v1alpha1.VariableType Value string } @@ -97,7 +98,7 @@ func (vc *VariableConfig) ReplaceTextTemplate(path string) error { value = template.Value // Check if the value is a file type and load the value contents from the file - if template.Type == FileVariableType && value != "" { + if template.Type == v1alpha1.FileVariableType && value != "" { if isText, err := helpers.IsTextFile(value); err != nil || !isText { nonTextWarning := fmt.Sprintf("Refusing to load a non-text file for templating %s", templateKey) vc.logger.Warn(nonTextWarning) diff --git a/src/pkg/variables/templates_test.go b/src/pkg/variables/templates_test.go index 8becae631a..73c2ddd7e4 100644 --- a/src/pkg/variables/templates_test.go +++ b/src/pkg/variables/templates_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/stretchr/testify/require" + "github.com/zarf-dev/zarf/src/api/v1alpha1" ) var start = ` @@ -79,7 +80,7 @@ func TestReplaceTextTemplate(t *testing.T) { setVariableMap: SetVariableMap{ "REPLACE_ME": {Value: "VAR_REPLACED"}, }, - constants: []Constant{{Name: "REPLACE_ME", Value: "CONST_REPLACED"}}, + constants: []v1alpha1.Constant{{Name: "REPLACE_ME", Value: "CONST_REPLACED"}}, applicationTemplates: map[string]*TextTemplate{ "###PREFIX_APP_REPLACE_ME###": {Value: "APP_REPLACED"}, }, @@ -92,7 +93,7 @@ func TestReplaceTextTemplate(t *testing.T) { setVariableMap: SetVariableMap{ "REPLACE_ME": {Value: "VAR_REPLACED\nVAR_SECOND"}, }, - constants: []Constant{{Name: "REPLACE_ME", Value: "CONST_REPLACED\nCONST_SECOND"}}, + constants: []v1alpha1.Constant{{Name: "REPLACE_ME", Value: "CONST_REPLACED\nCONST_SECOND"}}, applicationTemplates: map[string]*TextTemplate{ "###PREFIX_APP_REPLACE_ME###": {Value: "APP_REPLACED\nAPP_SECOND"}, }, @@ -103,9 +104,9 @@ func TestReplaceTextTemplate(t *testing.T) { vc: VariableConfig{ templatePrefix: "PREFIX", setVariableMap: SetVariableMap{ - "REPLACE_ME": {Value: "VAR_REPLACED\nVAR_SECOND", Variable: Variable{AutoIndent: true}}, + "REPLACE_ME": {Value: "VAR_REPLACED\nVAR_SECOND", Variable: v1alpha1.Variable{AutoIndent: true}}, }, - constants: []Constant{{Name: "REPLACE_ME", Value: "CONST_REPLACED\nCONST_SECOND", AutoIndent: true}}, + constants: []v1alpha1.Constant{{Name: "REPLACE_ME", Value: "CONST_REPLACED\nCONST_SECOND", AutoIndent: true}}, applicationTemplates: map[string]*TextTemplate{ "###PREFIX_APP_REPLACE_ME###": {Value: "APP_REPLACED\nAPP_SECOND", AutoIndent: true}, }, @@ -116,11 +117,11 @@ func TestReplaceTextTemplate(t *testing.T) { vc: VariableConfig{ templatePrefix: "PREFIX", setVariableMap: SetVariableMap{ - "REPLACE_ME": {Value: "testdata/file.txt", Variable: Variable{Type: FileVariableType}}, + "REPLACE_ME": {Value: "testdata/file.txt", Variable: v1alpha1.Variable{Type: v1alpha1.FileVariableType}}, }, - constants: []Constant{{Name: "REPLACE_ME", Value: "CONSTs Don't Support File"}}, + constants: []v1alpha1.Constant{{Name: "REPLACE_ME", Value: "CONSTs Don't Support File"}}, applicationTemplates: map[string]*TextTemplate{ - "###PREFIX_APP_REPLACE_ME###": {Value: "testdata/file.txt", Type: FileVariableType}, + "###PREFIX_APP_REPLACE_ME###": {Value: "testdata/file.txt", Type: v1alpha1.FileVariableType}, }, }, wantContents: file, diff --git a/src/pkg/variables/types.go b/src/pkg/variables/types.go deleted file mode 100644 index 6b3be2863b..0000000000 --- a/src/pkg/variables/types.go +++ /dev/null @@ -1,83 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: 2021-Present The Zarf Authors - -// Package variables contains functions for interacting with variables -package variables - -import ( - "fmt" - "regexp" - - "github.com/zarf-dev/zarf/src/config/lang" -) - -// VariableType represents a type of a Zarf package variable -type VariableType string - -const ( - // RawVariableType is the default type for a Zarf package variable - RawVariableType VariableType = "raw" - // FileVariableType is a type for a Zarf package variable that loads its contents from a file - FileVariableType VariableType = "file" -) - -var ( - // IsUppercaseNumberUnderscore is a regex for uppercase, numbers and underscores. - // https://regex101.com/r/tfsEuZ/1 - IsUppercaseNumberUnderscore = regexp.MustCompile(`^[A-Z0-9_]+$`).MatchString -) - -// Variable represents a variable that has a value set programmatically -type Variable struct { - // The name to be used for the variable - Name string `json:"name" jsonschema:"pattern=^[A-Z0-9_]+$"` - // Whether to mark this variable as sensitive to not print it in the log - Sensitive bool `json:"sensitive,omitempty"` - // Whether to automatically indent the variable's value (if multiline) when templating. Based on the number of chars before the start of ###ZARF_VAR_. - AutoIndent bool `json:"autoIndent,omitempty"` - // An optional regex pattern that a variable value must match before a package deployment can continue. - Pattern string `json:"pattern,omitempty"` - // Changes the handling of a variable to load contents differently (i.e. from a file rather than as a raw variable - templated files should be kept below 1 MiB) - Type VariableType `json:"type,omitempty" jsonschema:"enum=raw,enum=file"` -} - -// InteractiveVariable is a variable that can be used to prompt a user for more information -type InteractiveVariable struct { - Variable `json:",inline"` - // A description of the variable to be used when prompting the user a value - Description string `json:"description,omitempty"` - // The default value to use for the variable - Default string `json:"default,omitempty"` - // Whether to prompt the user for input for this variable - Prompt bool `json:"prompt,omitempty"` -} - -// Constant are constants that can be used to dynamically template K8s resources or run in actions. -type Constant struct { - // The name to be used for the constant - Name string `json:"name" jsonschema:"pattern=^[A-Z0-9_]+$"` - // The value to set for the constant during deploy - Value string `json:"value"` - // A description of the constant to explain its purpose on package create or deploy confirmation prompts - Description string `json:"description,omitempty"` - // Whether to automatically indent the variable's value (if multiline) when templating. Based on the number of chars before the start of ###ZARF_CONST_. - AutoIndent bool `json:"autoIndent,omitempty"` - // An optional regex pattern that a constant value must match before a package can be created. - Pattern string `json:"pattern,omitempty"` -} - -// SetVariable tracks internal variables that have been set during this run of Zarf -type SetVariable struct { - Variable `json:",inline"` - // The value the variable is currently set with - Value string `json:"value"` -} - -// Validate runs all validation checks on a package constant. -func (c Constant) Validate() error { - if !regexp.MustCompile(c.Pattern).MatchString(c.Value) { - return fmt.Errorf(lang.PkgValidateErrPkgConstantPattern, c.Name, c.Pattern) - } - - return nil -} diff --git a/src/pkg/variables/variables.go b/src/pkg/variables/variables.go index 1f2e6a8480..929cb13182 100644 --- a/src/pkg/variables/variables.go +++ b/src/pkg/variables/variables.go @@ -7,19 +7,21 @@ package variables import ( "fmt" "regexp" + + "github.com/zarf-dev/zarf/src/api/v1alpha1" ) // SetVariableMap represents a map of variable names to their set values -type SetVariableMap map[string]*SetVariable +type SetVariableMap map[string]*v1alpha1.SetVariable // GetSetVariable gets a variable set within a VariableConfig by its name -func (vc *VariableConfig) GetSetVariable(name string) (variable *SetVariable, ok bool) { +func (vc *VariableConfig) GetSetVariable(name string) (variable *v1alpha1.SetVariable, ok bool) { variable, ok = vc.setVariableMap[name] return variable, ok } // PopulateVariables handles setting the active variables within a VariableConfig's SetVariableMap -func (vc *VariableConfig) PopulateVariables(variables []InteractiveVariable, presetVariables map[string]string) error { +func (vc *VariableConfig) PopulateVariables(variables []v1alpha1.InteractiveVariable, presetVariables map[string]string) error { for name, value := range presetVariables { vc.SetVariable(name, value, false, false, "") } @@ -62,9 +64,9 @@ func (vc *VariableConfig) PopulateVariables(variables []InteractiveVariable, pre } // SetVariable sets a variable in a VariableConfig's SetVariableMap -func (vc *VariableConfig) SetVariable(name, value string, sensitive bool, autoIndent bool, varType VariableType) { - vc.setVariableMap[name] = &SetVariable{ - Variable: Variable{ +func (vc *VariableConfig) SetVariable(name, value string, sensitive bool, autoIndent bool, varType v1alpha1.VariableType) { + vc.setVariableMap[name] = &v1alpha1.SetVariable{ + Variable: v1alpha1.Variable{ Name: name, Sensitive: sensitive, AutoIndent: autoIndent, diff --git a/src/pkg/variables/variables_test.go b/src/pkg/variables/variables_test.go index 901fdf4ba9..07442e97f5 100644 --- a/src/pkg/variables/variables_test.go +++ b/src/pkg/variables/variables_test.go @@ -7,94 +7,96 @@ import ( "testing" "github.com/stretchr/testify/require" + + "github.com/zarf-dev/zarf/src/api/v1alpha1" ) func TestPopulateVariables(t *testing.T) { type test struct { vc VariableConfig - vars []InteractiveVariable + vars []v1alpha1.InteractiveVariable presets map[string]string wantErr bool wantVars SetVariableMap } - prompt := func(_ InteractiveVariable) (value string, err error) { return "Prompt", nil } + prompt := func(_ v1alpha1.InteractiveVariable) (value string, err error) { return "Prompt", nil } tests := []test{ { vc: VariableConfig{setVariableMap: SetVariableMap{}}, - vars: []InteractiveVariable{{Variable: Variable{Name: "NAME"}}}, + vars: []v1alpha1.InteractiveVariable{{Variable: v1alpha1.Variable{Name: "NAME"}}}, presets: map[string]string{}, - wantVars: SetVariableMap{"NAME": {Variable: Variable{Name: "NAME"}}}, + wantVars: SetVariableMap{"NAME": {Variable: v1alpha1.Variable{Name: "NAME"}}}, }, { vc: VariableConfig{setVariableMap: SetVariableMap{}}, - vars: []InteractiveVariable{ - {Variable: Variable{Name: "NAME"}, Default: "Default"}, + vars: []v1alpha1.InteractiveVariable{ + {Variable: v1alpha1.Variable{Name: "NAME"}, Default: "Default"}, }, presets: map[string]string{}, wantVars: SetVariableMap{ - "NAME": {Variable: Variable{Name: "NAME"}, Value: "Default"}, + "NAME": {Variable: v1alpha1.Variable{Name: "NAME"}, Value: "Default"}, }, }, { vc: VariableConfig{setVariableMap: SetVariableMap{}}, - vars: []InteractiveVariable{ - {Variable: Variable{Name: "NAME"}, Default: "Default"}, + vars: []v1alpha1.InteractiveVariable{ + {Variable: v1alpha1.Variable{Name: "NAME"}, Default: "Default"}, }, presets: map[string]string{"NAME": "Set"}, wantVars: SetVariableMap{ - "NAME": {Variable: Variable{Name: "NAME"}, Value: "Set"}, + "NAME": {Variable: v1alpha1.Variable{Name: "NAME"}, Value: "Set"}, }, }, { vc: VariableConfig{setVariableMap: SetVariableMap{}}, - vars: []InteractiveVariable{ - {Variable: Variable{Name: "NAME", Sensitive: true, AutoIndent: true, Type: FileVariableType}}, + vars: []v1alpha1.InteractiveVariable{ + {Variable: v1alpha1.Variable{Name: "NAME", Sensitive: true, AutoIndent: true, Type: v1alpha1.FileVariableType}}, }, presets: map[string]string{}, wantVars: SetVariableMap{ - "NAME": {Variable: Variable{Name: "NAME", Sensitive: true, AutoIndent: true, Type: FileVariableType}}, + "NAME": {Variable: v1alpha1.Variable{Name: "NAME", Sensitive: true, AutoIndent: true, Type: v1alpha1.FileVariableType}}, }, }, { vc: VariableConfig{setVariableMap: SetVariableMap{}}, - vars: []InteractiveVariable{ - {Variable: Variable{Name: "NAME", Sensitive: true, AutoIndent: true, Type: FileVariableType}}, + vars: []v1alpha1.InteractiveVariable{ + {Variable: v1alpha1.Variable{Name: "NAME", Sensitive: true, AutoIndent: true, Type: v1alpha1.FileVariableType}}, }, presets: map[string]string{"NAME": "Set"}, wantVars: SetVariableMap{ - "NAME": {Variable: Variable{Name: "NAME", Sensitive: true, AutoIndent: true, Type: FileVariableType}, Value: "Set"}, + "NAME": {Variable: v1alpha1.Variable{Name: "NAME", Sensitive: true, AutoIndent: true, Type: v1alpha1.FileVariableType}, Value: "Set"}, }, }, { vc: VariableConfig{setVariableMap: SetVariableMap{}, prompt: prompt}, - vars: []InteractiveVariable{ - {Variable: Variable{Name: "NAME"}, Prompt: true}, + vars: []v1alpha1.InteractiveVariable{ + {Variable: v1alpha1.Variable{Name: "NAME"}, Prompt: true}, }, presets: map[string]string{}, wantVars: SetVariableMap{ - "NAME": {Variable: Variable{Name: "NAME"}, Value: "Prompt"}, + "NAME": {Variable: v1alpha1.Variable{Name: "NAME"}, Value: "Prompt"}, }, }, { vc: VariableConfig{setVariableMap: SetVariableMap{}, prompt: prompt}, - vars: []InteractiveVariable{ - {Variable: Variable{Name: "NAME"}, Default: "Default", Prompt: true}, + vars: []v1alpha1.InteractiveVariable{ + {Variable: v1alpha1.Variable{Name: "NAME"}, Default: "Default", Prompt: true}, }, presets: map[string]string{}, wantVars: SetVariableMap{ - "NAME": {Variable: Variable{Name: "NAME"}, Value: "Prompt"}, + "NAME": {Variable: v1alpha1.Variable{Name: "NAME"}, Value: "Prompt"}, }, }, { vc: VariableConfig{setVariableMap: SetVariableMap{}, prompt: prompt}, - vars: []InteractiveVariable{ - {Variable: Variable{Name: "NAME"}, Prompt: true}, + vars: []v1alpha1.InteractiveVariable{ + {Variable: v1alpha1.Variable{Name: "NAME"}, Prompt: true}, }, presets: map[string]string{"NAME": "Set"}, wantVars: SetVariableMap{ - "NAME": {Variable: Variable{Name: "NAME"}, Value: "Set"}, + "NAME": {Variable: v1alpha1.Variable{Name: "NAME"}, Value: "Set"}, }, }, } @@ -132,18 +134,18 @@ func TestCheckVariablePattern(t *testing.T) { }, { vc: VariableConfig{ - setVariableMap: SetVariableMap{"NAME": &SetVariable{Value: "name"}}, + setVariableMap: SetVariableMap{"NAME": &v1alpha1.SetVariable{Value: "name"}}, }, name: "NAME", pattern: "n[^a]me", wantErrMsg: "provided value for variable \"NAME\" does not match pattern \"n[^a]me\"", }, { vc: VariableConfig{ - setVariableMap: SetVariableMap{"NAME": &SetVariable{Value: "name"}}, + setVariableMap: SetVariableMap{"NAME": &v1alpha1.SetVariable{Value: "name"}}, }, name: "NAME", pattern: "n[a-z]me", wantErrMsg: "", }, { vc: VariableConfig{ - setVariableMap: SetVariableMap{"NAME": &SetVariable{Value: "name"}}, + setVariableMap: SetVariableMap{"NAME": &v1alpha1.SetVariable{Value: "name"}}, }, name: "NAME", pattern: "n[a-z-bad-pattern", wantErrMsg: "error parsing regexp: missing closing ]: `[a-z-bad-pattern`", }, }