diff --git a/README.md b/README.md index 8f49de8ee0..4593158b2c 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ Zarf eliminates the [complexity of air gap software delivery](https://www.itopst ## 🛠️ Configurable Features -- Customizable [variables and package templates](https://docs.zarf.dev/ref/variables/) with defaults and user prompting +- Customizable [variables and package templates](https://docs.zarf.dev/ref/values/) with defaults and user prompting - [Composable packages](https://docs.zarf.dev/ref/components/#component-imports) to include multiple sub-packages/components - Component-level OS/architecture filtering @@ -67,7 +67,7 @@ To discover more about Zarf and explore its features, please visit [docs.zarf.de - [packages](https://docs.zarf.dev/ref/packages) - [components](https://docs.zarf.dev/ref/components) - [actions](https://docs.zarf.dev/ref/actions) -- [variables](https://docs.zarf.dev/ref/variables) +- [variables](https://docs.zarf.dev/ref/values) - [SBOMs](https://docs.zarf.dev/ref/sboms) - and more! diff --git a/hack/check-zarf-docs-and-schema.sh b/hack/check-zarf-docs-and-schema.sh index 93f4e4be0e..216d64b0ea 100755 --- a/hack/check-zarf-docs-and-schema.sh +++ b/hack/check-zarf-docs-and-schema.sh @@ -1,9 +1,9 @@ #!/usr/bin/env sh -if [ -z "$(git status -s docs/ zarf.schema.json)" ]; then +if [ -z "$(git status -s ./site/src/content/docs/commands/ ./zarf.schema.json)" ]; then echo "Success!" exit 0 else - git diff docs/ zarf.schema.json + git diff ./site/src/content/docs/commands/ ./zarf.schema.json exit 1 fi diff --git a/site/src/assets/what-is-zarf/appliance.drawio.png b/site/src/assets/what-is-zarf/appliance.drawio.png deleted file mode 100644 index 13ccfe81cc..0000000000 Binary files a/site/src/assets/what-is-zarf/appliance.drawio.png and /dev/null differ diff --git a/site/src/assets/what-is-zarf/existing-cluster.drawio.png b/site/src/assets/what-is-zarf/existing-cluster.drawio.png deleted file mode 100644 index b6fee8f4d0..0000000000 Binary files a/site/src/assets/what-is-zarf/existing-cluster.drawio.png and /dev/null differ diff --git a/site/src/assets/what-is-zarf/how-to-use-zarf.drawio.png b/site/src/assets/what-is-zarf/how-to-use-zarf.drawio.png deleted file mode 100644 index 7b62e58243..0000000000 Binary files a/site/src/assets/what-is-zarf/how-to-use-zarf.drawio.png and /dev/null differ diff --git a/site/src/assets/what-is-zarf/utility-cluster.drawio.png b/site/src/assets/what-is-zarf/utility-cluster.drawio.png deleted file mode 100644 index 26f289ea9f..0000000000 Binary files a/site/src/assets/what-is-zarf/utility-cluster.drawio.png and /dev/null differ diff --git a/site/src/assets/zarf-bubbles.svg b/site/src/assets/zarf-bubbles.svg index db3cafa5ba..8381dd3501 100644 --- a/site/src/assets/zarf-bubbles.svg +++ b/site/src/assets/zarf-bubbles.svg @@ -1,12 +1,12 @@ + inkscape:current-layer="svg385" /> + id="g239" + transform="matrix(1.9997122,0,0,1.9832754,0.21874591,2.675931)"> + id="rect1" + x="0" + y="0" + style="fill:url(#paint0_radial_260_33984)" /> + id="path1" + style="fill:url(#paint1_linear_260_33984)" /> + id="path2" + style="fill:url(#paint2_linear_260_33984)" /> + id="path10" + style="fill:url(#paint10_linear_260_33984)" /> @@ -1167,70 +1174,72 @@ + id="path118" + style="fill:url(#paint17_linear_260_33984)" /> + id="path126" + style="fill:url(#paint22_linear_260_33984)" /> + id="path138" + style="fill:url(#paint24_linear_260_33984)" /> + id="path139" + style="fill:url(#paint25_linear_260_33984)" /> + id="path140" + style="fill:url(#paint26_linear_260_33984)" /> + id="path141" + style="fill:url(#paint27_linear_260_33984)" /> + id="path142" + style="fill:url(#paint28_linear_260_33984)" /> + id="path143" + style="fill:url(#paint29_linear_260_33984)" /> + id="path144" + style="fill:url(#paint30_linear_260_33984)" /> + id="path146" + style="fill:url(#paint32_linear_260_33984)" /> + id="path154" + style="fill:url(#paint37_linear_260_33984)" /> + id="path155" + style="fill:url(#paint38_linear_260_33984)" /> + id="path160" + style="fill:url(#paint41_linear_260_33984)" /> + id="path222" + style="fill:url(#paint51_linear_260_33984)" /> @@ -1989,7 +2010,7 @@ cy="0" r="1" gradientUnits="userSpaceOnUse" - gradientTransform="translate(380 80) rotate(90) scale(80 380)"> + gradientTransform="matrix(0,80,-380,0,380,80)"> @@ -2027,10 +2048,10 @@ @@ -2173,9 +2194,9 @@ @@ -2242,9 +2263,9 @@ @@ -2275,10 +2296,10 @@ @@ -2310,9 +2331,9 @@ @@ -2347,7 +2368,7 @@ cy="0" r="1" gradientUnits="userSpaceOnUse" - gradientTransform="translate(290.309 151.696) rotate(-90.2101) scale(28.0849 214.546)"> + gradientTransform="matrix(-0.10298532,-28.084711,214.54456,-0.78672485,290.309,151.696)"> @@ -2357,9 +2378,9 @@ @@ -2417,9 +2438,9 @@ @@ -2938,9 +2959,9 @@ + fill="#ffffff" + id="rect384" + x="0" + y="0" /> + fill="#ffffff" + transform="translate(0,-125)" + id="rect385" + x="0" + y="0" /> { if (!include) { @@ -16,6 +16,21 @@ const json = await import("../assets/zarf.schema.json"); // @ts-expect-error - We don't import a TS type for the schema, but we know it's structured correctly const itemSchema = json.definitions[item]; +if (unwrap) { + unwrap.forEach((wrapped: string) => { + if (itemSchema.properties[wrapped]) { + // @ts-expect-error - We don't import a TS type for the schema, but we know it's structured correctly + const wrappedSchema = json.definitions[wrapped]; + + delete itemSchema.properties[wrapped] + itemSchema.required = itemSchema.required.filter((elem: string) => elem != wrapped) + + itemSchema.properties = {...wrappedSchema.properties, ...itemSchema.properties} + itemSchema.required = [...wrappedSchema.required, ...itemSchema.required] + } + }); +} + const properties = Object.keys(itemSchema.properties) .filter(includesElement) .sort() diff --git a/site/src/content/docs/faq.mdx b/site/src/content/docs/faq.mdx index 024399c23e..1e662a262a 100644 --- a/site/src/content/docs/faq.mdx +++ b/site/src/content/docs/faq.mdx @@ -144,4 +144,4 @@ Typically you should not deploy a Zarf package in YOLO mode if the cluster has a A `skeleton` package is a bare-bones Zarf package definition alongside its associated local files and manifests that has been published to an OCI registry. These packages are intended for use with [component composability](/ref/components) to provide versioned imports for components that you wish to mix and match or modify with merge-overrides across multiple separate packages. -Skeleton packages have not been run through the `zarf package create` process yet, and thus do not have any remote resources included (no images, repos, or remote manifests and files) thereby retaining any [create-time package configuration templates](/ref/variables) as they were defined in the original `zarf.yaml` (i.e. untemplated). +Skeleton packages have not been run through the `zarf package create` process yet, and thus do not have any remote resources included (no images, repos, or remote manifests and files) thereby retaining any [create-time package configuration templates](/ref/values) as they were defined in the original `zarf.yaml` (i.e. untemplated). diff --git a/site/src/content/docs/ref/components.mdx b/site/src/content/docs/ref/components.mdx index 1eaa0bcfb4..aff22ada6e 100644 --- a/site/src/content/docs/ref/components.mdx +++ b/site/src/content/docs/ref/components.mdx @@ -117,7 +117,7 @@ Zarf dynamically generates a Helm Chart from the named manifest entries that you :::note -Kustomizations are handled a bit differently than normal manifests in that Zarf will automatically run `kustomize build` on them during `zarf package create`, thus rendering the Kustomization into a single manifest file. This prevents needing to grab any remote Kustomization resources during `zarf package deploy` but also means that any Zarf [`variables`](/ref/variables/) will only apply to the rendered manifest not the `kustomize build` process. +Kustomizations are handled a bit differently than normal manifests in that Zarf will automatically run `kustomize build` on them during `zarf package create`, thus rendering the Kustomization into a single manifest file. This prevents needing to grab any remote Kustomization resources during `zarf package deploy` but also means that any Zarf [`variables`](/ref/values/) will only apply to the rendered manifest not the `kustomize build` process. ::: @@ -222,7 +222,7 @@ The `import` key in Zarf supports two modes to pull in a component: :::caution -The import `path` or `url` must be statically defined at create time. You cannot use [package templates](/ref/variables/#create-time-package-configuration-templates) within them. +The import `path` or `url` must be statically defined at create time. You cannot use [package templates](/ref/create/#package-templates) within them. ::: diff --git a/site/src/content/docs/ref/create.mdx b/site/src/content/docs/ref/create.mdx index b53ea157bc..422a887035 100644 --- a/site/src/content/docs/ref/create.mdx +++ b/site/src/content/docs/ref/create.mdx @@ -96,3 +96,46 @@ You do not need to create init configs by yourself unless you want to customize To deploy a Zarf Package, you can use the command `zarf package deploy`. This will prompt you to select from all of the files in your current directory that match the name `zarf-package-*.tar.zst`. Alternatively, if you already know which package you want to deploy, you can simply use the command `zarf package deploy {PACKAGE_NAME}`. During the deployment process, Zarf will leverage the infrastructure created during the 'init' process (such as the Docker registry and Git server) to push all the necessary images and repositories required for the package to operate. + + +### Package Templates + +:::caution + +`zarf package create` templates only template `###ZARF_PKG_TMPL_*###` entries the `zarf.yaml` file while `zarf package deploy` only templates other `manifests`, `charts`, `files`, and `actions`. To learn more about using deployment values see the [Deployment Values](/ref/values) page. + +::: + +You can also specify `zarf.yaml` package configuration templates at package create time by including `###_ZARF_PKG_TMPL_*###` as the value for any string-type data in your package definition. These values are discovered during `zarf package create` and will always be prompted for if not using `--confirm` or `--set`. An example of this is below: + +```yaml +kind: ZarfPackageConfig +metadata: + name: 'pkg-variables' + description: 'Prompt for a variables during package create' + +constants: + - name: PROMPT_IMAGE + value: '###ZARF_PKG_TMPL_PROMPT_ON_CREATE###' + +components: + - name: zarf-prompt-image + required: true + images: + - '###ZARF_PKG_TMPL_PROMPT_ON_CREATE###' +``` + +:::caution + +It is not recommended to use package configuration templates for any `sensitive` data as this will be baked into the package as plain text. Please use a deploy-time variable with the `sensitive` key set instead. + +::: + +:::note + +You can only template string values in this way as non-string values will not marshal/unmarshal properly through the yaml. + +Additionally, you cannot template the component import path using package configuration templates + +::: + diff --git a/site/src/content/docs/ref/values.mdx b/site/src/content/docs/ref/values.mdx new file mode 100644 index 0000000000..e356501d14 --- /dev/null +++ b/site/src/content/docs/ref/values.mdx @@ -0,0 +1,151 @@ +--- +title: Deployment Values +sidebar: + order: 35 +--- + +import Properties from '@components/SchemaItemProperties.astro'; +import ExampleYAML from "@components/ExampleYAML.astro"; + +Deploy time values are used throughout the Zarf deployment process to template `manifests` and `files`, set Helm values, or provide environment variables for `actions`, and can be provided by the `zarf package deploy` user, the `zarf package create` user, or internally by Zarf itself. + +## Using Values + +### Value Templates + +Values can be templated into `manifests`, `files`, and `charts` using a unique `###ZARF_###` syntax anywhere in the file, where `` is the name of the value along with any associated prefix in ALL_CAPS (see [Setting Values](#setting-values) below). This allows you to punch through other templating engines to place values precisely where you want in a given file. You can also control how this templating happens when defining a value such as with the `autoIndent` key on `constants` and `variables`. A simple value template for a [Zarf Variable](#variables-zarf_var_) named `DATABASE_USERNAME` would look like the following: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: db-configmap +data: + username: ###ZARF_VAR_DATABASE_USERNAME### +``` + +### Helm Chart Mapping + +[Zarf Variables](#variables-zarf_var_) can also be mapped directly to Helm values within a given `charts` definition. This is done with the `variables` key within a chart that allows you to take a Zarf Variable `name` and map it to a YAML path within that chart's Helm values. This chart's Helm value will then be set to the current value of the Zarf Variable at the time of it's installation. + +```yaml + charts: + - name: wordpress + version: 16.0.4 + namespace: wordpress + localPath: chart + variables: + - name: DATABASE_USERNAME + path: db.username +``` + + + +:::caution + +Chart `variables` in Zarf are currently an `alpha` feature and may be subject to change in later versions of Zarf. + +::: + +### Environment Variables + +Zarf `actions` can also pull values from the shell's environment when running a `cmd`. These values are available under the same `ZARF_` as value templates (without any `#`s) and can be used like the below: + +```yaml + actions: + onDeploy: + after: + - cmd: echo ${ZARF_VAR_DATABASE_USERNAME} +``` + +## Setting Values + +:::note + +All value `name` fields must match the regex pattern `^[A-Z0-9_]+$` ([Test](https://regex101.com/r/BG5ZqW/1)). + +::: + +### Variables (`ZARF_VAR_`) + +Variables are dynamic values that are set by the `zarf package deploy` user or internally by the outputs of `actions`. When used they are prefixed with `ZARF_VAR`, and they can be defined with the top-level `variables` key for input by the user: + +```yaml +variables: + name: DATABASE_USERNAME + description: 'The username for the database' +``` + + + +Or can be set within an `actions` `setVariables` key to take the value from the standard output of the given `cmd`: + +```yaml +components: + - name: set-variable-example + actions: + onDeploy: + after: + - cmd: echo "username-value" + setVariables: + - name: DATABASE_USERNAME +``` + + + +:::note + +Variables with `type: file` will be set to the filepath when used in `actions` (see [Environment Variables](#environment-variables) above) due to constraints on the size of environment variables in the shell. This also allows for additional processing of the file by its filename. + +::: + +:::tip + + +For user-specified variables, you can specify a `default` value for the variable to take in case a user does not provide one on deploy, and can specify whether to `prompt` the user for the variable when not using the `--confirm` or `--set` flags. + +```yaml +variables: + name: DATABASE_USERNAME + default: 'postgres' + prompt: true +``` + +When not specifying `default`, `prompt`, `sensitive`, `autoIndent`, or `type` Zarf will default to `default: ""`, `prompt: false`, `sensitive: false`, `autoIndent: false`, and `type: "raw"` + +::: + +### Constants (`ZARF_CONST_`) + +Constants are static values that are set by the `zarf package create` user and are used as a way to bake in a common value that the package creator would like to template or use within the deployment process. They are useful to centralize the setting of resources that will be baked into the package (such as image references) to have a singular place to update potentially many downstream references. They are set with a top-level `constants` key as in the below: + +```yaml +constants: + name: DATABASE_USERNAME + description: 'The username for the database' + value: 'postgres' +``` + + + + +### Internal Values (`ZARF_`) + +In addition to user supplied Variables, and Constants, Zarf maintains a list of internal variables that components can use for more advanced functionality (such as within [`init` packages](/ref/init-package)). Below are the current values Zarf supports: + +- `STORAGE_CLASS`: Storage class specified for persistent storage within the Kubernetes cluster (maps to `--storage-class` on `zarf init`) +- `REGISTRY`: Address of the registry server used during `zarf init` (maps to `--registry-url` on `zarf init`) +- `NODEPORT`: Nodeport used for a registry internal to the cluster (maps to `--nodeport` on `zarf init`) +- `REGISTRY_AUTH_PUSH`: Password for pushing images to the registry (maps to `--registry-push-password` on `zarf init`) +- `REGISTRY_AUTH_PULL`: Password for pulling images from the registry (maps to `--registry-pull-password` on `zarf init`) +- `GIT_PUSH`: Username utilized for pushing changes to the Git server (maps to `--git-push-username` on `zarf init`) +- `GIT_AUTH_PUSH`: Password required for pushing changes to the Git server (maps to `--git-push-password` on `zarf init`) +- `GIT_PULL`: Username employed for pulling changes from the Git server (maps to `--git-pull-username` on `zarf init`) +- `GIT_AUTH_PULL`: Password required for pulling changes from the Git server (maps to `--git-pull-password` on `zarf init`) +- `DATA_INJECTION_MARKER`: The marker used within a `dataInjection` target Pod `spec` that Zarf uses to track a data injection + +:::note + +Because files can be deployed without a Kubernetes cluster, some built-in values such as `###ZARF_REGISTRY###` may not be available if no previous component has required access to the cluster. If you need one of these built-in values, a prior component will need to have been called that requires access to the cluster, such as `images`, `repos`, `charts`, `manifests`, or `dataInjections`. + +::: diff --git a/site/src/content/docs/ref/variables.mdx b/site/src/content/docs/ref/variables.mdx deleted file mode 100644 index f649117081..0000000000 --- a/site/src/content/docs/ref/variables.mdx +++ /dev/null @@ -1,153 +0,0 @@ ---- -title: Variables -sidebar: - order: 35 ---- - -import Properties from '@components/SchemaItemProperties.astro'; -import ExampleYAML from "@components/ExampleYAML.astro"; - -:::note - -Because files can be deployed without a Kubernetes cluster, some built-in variables such as `###ZARF_REGISTRY###` may not be available if no previous component has required access to the cluster. If you need one of these built-in variables, a prior component will need to have been called that requires access to the cluster, such as `images`, `repos`, `manifests`, `dataInjections`. - -::: - -## Deploy-Time Variables and Constants - -To use variables and constants at deploy time you need to have two things: - -1. a manifest that you want to template a value in -2. a defined variable in the `zarf.yaml` file from `variables` or `setVariable` - -The manifest should have your desired variable name in ALL CAPS prefixed with `###ZARF_VAR` for `variables` or prefixed with `###ZARF_CONST` for `constants` and suffixed with `###`. For example in a configmap that took a variable named `DATABASE_USERNAME` you would provide the following: - -```yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: db-configmap -data: - username: ###ZARF_VAR_DATABASE_USERNAME### -``` - -In the `zarf.yaml`, you would need to define the variable in the `variables` section or as output from an action with `setVariable` with the same `name` as above. Or for a constant you would use the `constants` section. For the same example as above, you would have the following for a variable defined by the deploy user: - -```yaml -variables: - name: DATABASE_USERNAME - description: 'The username for the database' -``` - -And the following for a variable defined as an output from an action: - -```yaml -components: - - name: set-variable-example - actions: - onDeploy: - after: - - cmd: echo "username-value" - setVariables: - - name: DATABASE_USERNAME -``` - -Zarf `variables` can also have additional fields that describe how Zarf will handle them which are described below: - - - -:::note - -Variables with `type: file` will be set to the filepath in `actions` due to constraints on the size of environment variables in the shell. This also allows for additional processing of the file by its filename. - -::: - -:::note - -The fields `default`, `description` and `prompt` are not available on `setVariables` since they always take the standard output of an action command and will not be interacted with directly by a deploy user. - -::: - -Zarf `constants` are similar but have fewer options as they are static by the time `zarf package deploy` is run: - - - -:::note - -All names must match the regex pattern `^[A-Z0-9_]+$` [Test](https://regex101.com/r/BG5ZqW/1)). - -::: - -:::tip - -When not specifying `default`, `prompt`, `sensitive`, `autoIndent`, or `type` Zarf will default to `default: ""`, `prompt: false`, `sensitive: false`, `autoIndent: false`, and `type: "raw"` - -::: - -For user-specified variables, you can also specify a `default` value for the variable to take in case a user does not provide one on deploy, and can specify whether to `prompt` the user for the variable when not using the `--confirm` or `--set` flags. - -```yaml -variables: - name: DATABASE_USERNAME - default: 'postgres' - prompt: true -``` - -:::note - -Variables that do not have a default, are not `--set` and are not prompted for during deploy will be replaced with an empty string in manifests/charts/files - -::: - -For constants, you must specify the value they will use at package create. These values cannot be overridden with `--set` during `zarf package deploy`, but you can use package template variables (described below) to variablize them during `zarf package create`. - -```yaml -constants: - name: DATABASE_TABLE - value: 'users' -``` - -:::note - -`zarf package create` only templates the `zarf.yaml` file, and `zarf package deploy` only templates other manifests, charts and files - -::: - -## Create-Time Package Configuration Templates - -You can also specify package configuration templates at package create time by including `###_ZARF_PKG_TMPL_*###` as the value for any string-type data in your package definition. These values are discovered during `zarf package create` and will always be prompted for if not using `--confirm` or `--set`. An example of this is below: - -```yaml -kind: ZarfPackageConfig -metadata: - name: 'pkg-variables' - description: 'Prompt for a variables during package create' - -constants: - - name: PROMPT_IMAGE - value: '###ZARF_PKG_TMPL_PROMPT_ON_CREATE###' - -components: - - name: zarf-prompt-image - required: true - images: - - '###ZARF_PKG_TMPL_PROMPT_ON_CREATE###' -``` - -:::caution - -It is not recommended to use package configuration templates for any `sensitive` data as this will be baked into the package as plain text. Please use a deploy-time variable with the `sensitive` key set instead. - -::: - -:::note - -You can only template string values in this way as non-string values will not marshal/unmarshal properly through the yaml. - -::: - -:::note - -You cannot template the component import path using package configuration templates - -::: diff --git a/site/src/content/docs/tutorials/0-creating-a-zarf-package.mdx b/site/src/content/docs/tutorials/0-creating-a-zarf-package.mdx index 3cb69d2cb0..859c479056 100644 --- a/site/src/content/docs/tutorials/0-creating-a-zarf-package.mdx +++ b/site/src/content/docs/tutorials/0-creating-a-zarf-package.mdx @@ -120,7 +120,7 @@ Zarf has more `dev` commands you can learn about on the [dev CLI docs page](/ref We now have a deployable package definition, but it is currently not very configurable and might not fit every environment we want to deploy it to. If we deployed it as-is we would always have a Zarf Blog and a `zarf` user with an autogenerated password. -To resolve this, we can add configuration options with [Zarf Deploy-Time Variables](/ref/examples/variables/). For this package we will add a `variables` section to our `zarf.yaml` above `components` that will allow us to setup the user and the blog. +To resolve this, we can add configuration options with [Zarf Variables](/ref/examples/values/#variables-zarf_var_). For this package we will add a `variables` section to our `zarf.yaml` above `components` that will allow us to setup the user and the blog. ```yaml variables: diff --git a/site/src/content/docs/tutorials/10-package-create-differential.mdx b/site/src/content/docs/tutorials/10-package-create-differential.mdx index bd9f6c071b..176424805c 100644 --- a/site/src/content/docs/tutorials/10-package-create-differential.mdx +++ b/site/src/content/docs/tutorials/10-package-create-differential.mdx @@ -1,5 +1,5 @@ --- -title: Create Differential Packages with Zarf +title: Create Differential Packages sidebar: order: 10 --- diff --git a/src/cmd/tools/zarf.go b/src/cmd/tools/zarf.go index 54f5b18067..363fe87c63 100644 --- a/src/cmd/tools/zarf.go +++ b/src/cmd/tools/zarf.go @@ -18,6 +18,7 @@ import ( "github.com/defenseunicorns/zarf/src/config/lang" "github.com/defenseunicorns/zarf/src/internal/packager/git" "github.com/defenseunicorns/zarf/src/internal/packager/helm" + "github.com/defenseunicorns/zarf/src/internal/packager/template" "github.com/defenseunicorns/zarf/src/pkg/cluster" "github.com/defenseunicorns/zarf/src/pkg/message" "github.com/defenseunicorns/zarf/src/pkg/packager/sources" @@ -138,7 +139,7 @@ var updateCredsCmd = &cobra.Command{ } // Update Zarf 'init' component Helm releases if present - h := helm.NewClusterOnly(&types.PackagerConfig{State: newState}, c) + h := helm.NewClusterOnly(&types.PackagerConfig{}, template.GetZarfVariableConfig(), newState, c) if slices.Contains(args, message.RegistryKey) && newState.RegistryInfo.InternalRegistry { err = h.UpdateZarfRegistryValues() diff --git a/src/extensions/bigbang/bigbang.go b/src/extensions/bigbang/bigbang.go index 9445354046..e71954f7d0 100644 --- a/src/extensions/bigbang/bigbang.go +++ b/src/extensions/bigbang/bigbang.go @@ -18,6 +18,7 @@ import ( "github.com/defenseunicorns/zarf/src/pkg/layout" "github.com/defenseunicorns/zarf/src/pkg/message" "github.com/defenseunicorns/zarf/src/pkg/utils" + "github.com/defenseunicorns/zarf/src/pkg/variables" "github.com/defenseunicorns/zarf/src/types" "github.com/defenseunicorns/zarf/src/types/extensions" fluxHelmCtrl "github.com/fluxcd/helm-controller/api/v2beta1" @@ -94,7 +95,7 @@ func Run(YOLO bool, tmpPaths *layout.ComponentPaths, c types.ZarfComponent) (typ }, path.Join(tmpPaths.Temp, bb), path.Join(tmpPaths.Temp, bb, "values"), - helm.WithPackageConfig(&types.PackagerConfig{}), + helm.WithVariableConfig(&variables.VariableConfig{}), ) // Download the chart from Git and save it to a temporary directory. diff --git a/src/internal/packager/helm/chart.go b/src/internal/packager/helm/chart.go index 0e10c0ef99..72b80d65f9 100644 --- a/src/internal/packager/helm/chart.go +++ b/src/internal/packager/helm/chart.go @@ -44,12 +44,6 @@ func (h *Helm) InstallOrUpgradeChart() (types.ConnectStrings, string, error) { h.chart.ReleaseName = h.chart.Name } - // Do not wait for the chart to be ready if data injections are present. - if len(h.component.DataInjections) > 0 { - spinner.Updatef("Data injections detected, not waiting for chart to be ready") - h.chart.NoWait = true - } - // Setup K8s connection. err := h.createActionConfig(h.chart.Namespace, spinner) if err != nil { diff --git a/src/internal/packager/helm/common.go b/src/internal/packager/helm/common.go index 335fbf0f6c..6fba4d364e 100644 --- a/src/internal/packager/helm/common.go +++ b/src/internal/packager/helm/common.go @@ -17,6 +17,7 @@ import ( "github.com/defenseunicorns/zarf/src/config" "github.com/defenseunicorns/zarf/src/pkg/cluster" "github.com/defenseunicorns/zarf/src/pkg/message" + "github.com/defenseunicorns/zarf/src/pkg/variables" "github.com/defenseunicorns/zarf/src/types" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" @@ -29,19 +30,20 @@ type Helm struct { chartPath string valuesPath string - cfg *types.PackagerConfig - component types.ZarfComponent - cluster *cluster.Cluster - timeout time.Duration - retries int + cfg *types.PackagerConfig + cluster *cluster.Cluster + timeout time.Duration + retries int kubeVersion string chartOverride *chart.Chart valuesOverrides map[string]any - settings *cli.EnvSettings - actionConfig *action.Configuration + settings *cli.EnvSettings + actionConfig *action.Configuration + variableConfig *variables.VariableConfig + state *types.ZarfState } // Modifier is a function that modifies the Helm config. @@ -64,12 +66,14 @@ func New(chart types.ZarfChart, chartPath string, valuesPath string, mods ...Mod } // NewClusterOnly returns a new Helm config struct geared toward interacting with the cluster (not packages) -func NewClusterOnly(cfg *types.PackagerConfig, cluster *cluster.Cluster) *Helm { +func NewClusterOnly(cfg *types.PackagerConfig, variableConfig *variables.VariableConfig, state *types.ZarfState, cluster *cluster.Cluster) *Helm { return &Helm{ - cfg: cfg, - cluster: cluster, - timeout: config.ZarfDefaultTimeout, - retries: config.ZarfDefaultRetries, + cfg: cfg, + variableConfig: variableConfig, + state: state, + cluster: cluster, + timeout: config.ZarfDefaultTimeout, + retries: config.ZarfDefaultRetries, } } @@ -133,10 +137,11 @@ func NewFromZarfManifest(manifest types.ZarfManifest, manifestPath, packageName, } // WithDeployInfo adds the necessary information to deploy a given chart -func WithDeployInfo(component types.ZarfComponent, cfg *types.PackagerConfig, cluster *cluster.Cluster, valuesOverrides map[string]any, timeout time.Duration, retries int) Modifier { +func WithDeployInfo(cfg *types.PackagerConfig, variableConfig *variables.VariableConfig, state *types.ZarfState, cluster *cluster.Cluster, valuesOverrides map[string]any, timeout time.Duration, retries int) Modifier { return func(h *Helm) { - h.component = component h.cfg = cfg + h.variableConfig = variableConfig + h.state = state h.cluster = cluster h.valuesOverrides = valuesOverrides h.timeout = timeout @@ -151,10 +156,10 @@ func WithKubeVersion(kubeVersion string) Modifier { } } -// WithPackageConfig sets the packager config for the chart -func WithPackageConfig(cfg *types.PackagerConfig) Modifier { +// WithVariableConfig sets the variable config for the chart +func WithVariableConfig(variableConfig *variables.VariableConfig) Modifier { return func(h *Helm) { - h.cfg = cfg + h.variableConfig = variableConfig } } diff --git a/src/internal/packager/helm/post-render.go b/src/internal/packager/helm/post-render.go index ed3dec5d9f..7cee486d99 100644 --- a/src/internal/packager/helm/post-render.go +++ b/src/internal/packager/helm/post-render.go @@ -13,7 +13,6 @@ import ( "github.com/defenseunicorns/pkg/helpers" "github.com/defenseunicorns/zarf/src/config" - "github.com/defenseunicorns/zarf/src/internal/packager/template" "github.com/defenseunicorns/zarf/src/pkg/message" "github.com/defenseunicorns/zarf/src/pkg/utils" "github.com/defenseunicorns/zarf/src/types" @@ -29,22 +28,11 @@ type renderer struct { *Helm connectStrings types.ConnectStrings namespaces map[string]*corev1.Namespace - values template.Values } func (h *Helm) newRenderer() (*renderer, error) { message.Debugf("helm.NewRenderer()") - valueTemplate, err := template.Generate(h.cfg) - if err != nil { - return nil, err - } - - // TODO (@austinabro321) this should be cleaned up after https://github.com/defenseunicorns/zarf/pull/2276 gets merged - if h.cfg.State == nil { - valueTemplate.SetState(&types.ZarfState{}) - } - namespaces := make(map[string]*corev1.Namespace) if h.cluster != nil { namespaces[h.chart.Namespace] = h.cluster.NewZarfManagedNamespace(h.chart.Namespace) @@ -54,7 +42,6 @@ func (h *Helm) newRenderer() (*renderer, error) { Helm: h, connectStrings: make(types.ConnectStrings), namespaces: namespaces, - values: *valueTemplate, }, nil } @@ -71,7 +58,7 @@ func (r *renderer) Run(renderedManifests *bytes.Buffer) (*bytes.Buffer, error) { } // Run the template engine against the chart output - if _, err := template.ProcessYamlFilesInPath(tempDir, r.component, r.values); err != nil { + if err := r.variableConfig.ReplaceTextTemplate(path); err != nil { return nil, fmt.Errorf("error templating the helm chart: %w", err) } @@ -143,12 +130,12 @@ func (r *renderer) adoptAndUpdateNamespaces() error { } // If the package is marked as YOLO and the state is empty, skip the secret creation for this namespace - if r.cfg.Pkg.Metadata.YOLO && r.cfg.State.Distro == "YOLO" { + if r.cfg.Pkg.Metadata.YOLO && r.state.Distro == "YOLO" { continue } // Create the secret - validRegistrySecret := c.GenerateRegistryPullCreds(name, config.ZarfImagePullSecretName, r.cfg.State.RegistryInfo) + validRegistrySecret := c.GenerateRegistryPullCreds(name, config.ZarfImagePullSecretName, r.state.RegistryInfo) // Try to get a valid existing secret currentRegistrySecret, _ := c.GetSecret(name, config.ZarfImagePullSecretName) @@ -159,7 +146,7 @@ func (r *renderer) adoptAndUpdateNamespaces() error { } // Generate the git server secret - gitServerSecret := c.GenerateGitPullCreds(name, config.ZarfGitServerSecretName, r.cfg.State.GitServer) + gitServerSecret := c.GenerateGitPullCreds(name, config.ZarfGitServerSecretName, r.state.GitServer) // Create or update the zarf git server secret if _, err := c.CreateOrUpdateSecret(gitServerSecret); err != nil { diff --git a/src/internal/packager/helm/repo.go b/src/internal/packager/helm/repo.go index d91349a5c6..97582a64b9 100644 --- a/src/internal/packager/helm/repo.go +++ b/src/internal/packager/helm/repo.go @@ -376,7 +376,7 @@ func (h *Helm) listAvailableChartsAndVersions(pull *action.Pull) error { versions += entry.Version + separator } - versions = message.Truncate(versions, 75, false) + versions = helpers.Truncate(versions, 75, false) chartData = append(chartData, []string{name, versions}) } diff --git a/src/internal/packager/helm/zarf.go b/src/internal/packager/helm/zarf.go index b8d42c3f84..c6db1299eb 100644 --- a/src/internal/packager/helm/zarf.go +++ b/src/internal/packager/helm/zarf.go @@ -7,23 +7,25 @@ package helm import ( "fmt" + "github.com/defenseunicorns/zarf/src/internal/packager/template" "github.com/defenseunicorns/zarf/src/pkg/cluster" "github.com/defenseunicorns/zarf/src/pkg/k8s" "github.com/defenseunicorns/zarf/src/pkg/message" "github.com/defenseunicorns/zarf/src/pkg/transform" "github.com/defenseunicorns/zarf/src/pkg/utils" + "github.com/defenseunicorns/zarf/src/pkg/variables" "github.com/defenseunicorns/zarf/src/types" "helm.sh/helm/v3/pkg/action" ) // UpdateZarfRegistryValues updates the Zarf registry deployment with the new state values func (h *Helm) UpdateZarfRegistryValues() error { - pushUser, err := utils.GetHtpasswdString(h.cfg.State.RegistryInfo.PushUsername, h.cfg.State.RegistryInfo.PushPassword) + pushUser, err := utils.GetHtpasswdString(h.state.RegistryInfo.PushUsername, h.state.RegistryInfo.PushPassword) if err != nil { return fmt.Errorf("error generating htpasswd string: %w", err) } - pullUser, err := utils.GetHtpasswdString(h.cfg.State.RegistryInfo.PullUsername, h.cfg.State.RegistryInfo.PullPassword) + pullUser, err := utils.GetHtpasswdString(h.state.RegistryInfo.PullUsername, h.state.RegistryInfo.PullPassword) if err != nil { return fmt.Errorf("error generating htpasswd string: %w", err) } @@ -90,10 +92,7 @@ func (h *Helm) UpdateZarfAgentValues() error { Namespace: "zarf", ReleaseName: release.Name, } - h.component = types.ZarfComponent{ - Name: "zarf-agent", - } - h.cfg.Pkg.Constants = []types.ZarfPackageConstant{ + h.variableConfig.SetConstants([]variables.Constant{ { Name: "AGENT_IMAGE", Value: currentAgentImage.Path, @@ -102,9 +101,14 @@ func (h *Helm) UpdateZarfAgentValues() error { Name: "AGENT_IMAGE_TAG", Value: currentAgentImage.Tag, }, + }) + applicationTemplates, err := template.GetZarfTemplates("zarf-agent", h.state) + if err != nil { + return fmt.Errorf("error setting up the templates: %w", err) } + h.variableConfig.SetApplicationTemplates(applicationTemplates) - err := h.UpdateReleaseValues(map[string]interface{}{}) + err = h.UpdateReleaseValues(map[string]interface{}{}) if err != nil { return fmt.Errorf("error updating the release values: %w", err) } diff --git a/src/internal/packager/images/push.go b/src/internal/packager/images/push.go index 5daff0a14f..900b925651 100644 --- a/src/internal/packager/images/push.go +++ b/src/internal/packager/images/push.go @@ -91,7 +91,7 @@ func (i *ImageConfig) PushToZarfRegistry() error { } for refInfo, img := range refInfoToImage { - refTruncated := message.Truncate(refInfo.Reference, 55, true) + refTruncated := helpers.Truncate(refInfo.Reference, 55, true) progressBar.UpdateTitle(fmt.Sprintf("Pushing %s", refTruncated)) // If this is not a no checksum image push it for use with the Zarf agent diff --git a/src/internal/packager/template/template.go b/src/internal/packager/template/template.go index 19eed3e93c..f823c567f8 100644 --- a/src/internal/packager/template/template.go +++ b/src/internal/packager/template/template.go @@ -5,96 +5,52 @@ package template import ( - "bufio" "encoding/base64" "fmt" - "os" - "regexp" + "log/slog" "strings" "github.com/defenseunicorns/zarf/src/types" "github.com/defenseunicorns/pkg/helpers" "github.com/defenseunicorns/zarf/src/config" + "github.com/defenseunicorns/zarf/src/pkg/interactive" "github.com/defenseunicorns/zarf/src/pkg/message" "github.com/defenseunicorns/zarf/src/pkg/utils" + "github.com/defenseunicorns/zarf/src/pkg/variables" ) -// 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 - htpasswd string -} - -// Generate returns a Values struct with the values to be used in the template. -func Generate(cfg *types.PackagerConfig) (*Values, error) { - message.Debug("template.Generate()") - var generated Values - - if cfg == nil { - return nil, fmt.Errorf("config is nil") - } - - generated.config = cfg - - if cfg.State == nil { - return &generated, nil - } - - regInfo := cfg.State.RegistryInfo - - // Only calculate this for internal registries to allow longer external passwords - if regInfo.InternalRegistry { - pushUser, err := utils.GetHtpasswdString(regInfo.PushUsername, regInfo.PushPassword) - if err != nil { - return nil, fmt.Errorf("error generating htpasswd string: %w", err) - } +const ( + depMarkerOld = "DATA_INJECTON_MARKER" + depMarkerNew = "DATA_INJECTION_MARKER" +) - pullUser, err := utils.GetHtpasswdString(regInfo.PullUsername, regInfo.PullPassword) - if err != nil { - return nil, fmt.Errorf("error generating htpasswd string: %w", err) +// GetZarfVariableConfig gets a variable configuration specific to Zarf +func GetZarfVariableConfig() *variables.VariableConfig { + prompt := func(variable variables.InteractiveVariable) (value string, err error) { + if config.CommonOptions.Confirm { + return variable.Default, nil } - - generated.htpasswd = fmt.Sprintf("%s\\n%s", pushUser, pullUser) + return interactive.PromptVariable(variable) } - return &generated, nil + return variables.New( + "zarf", + deprecatedKeys(), + prompt, + slog.New(message.ZarfHandler{})) } -// Ready returns true if the Values struct is ready to be used in the template. -func (values *Values) Ready() bool { - return values.config.State != nil -} +// GetZarfTemplates returns the template keys and values to be used for templating. +func GetZarfTemplates(componentName string, state *types.ZarfState) (templateMap map[string]*variables.TextTemplate, err error) { + templateMap = make(map[string]*variables.TextTemplate) -// SetState sets the state -func (values *Values) SetState(state *types.ZarfState) { - values.config.State = state -} - -// GetVariables returns the variables to be used in the template. -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" - deprecations = map[string]string{ - fmt.Sprintf("###ZARF_%s###", depMarkerOld): fmt.Sprintf("###ZARF_%s###", depMarkerNew), - } - - if values.config.State != nil { - regInfo := values.config.State.RegistryInfo - gitInfo := values.config.State.GitServer + if state != nil { + regInfo := state.RegistryInfo + gitInfo := state.GitServer builtinMap := map[string]string{ - "STORAGE_CLASS": values.config.State.StorageClass, + "STORAGE_CLASS": state.StorageClass, // Registry info "REGISTRY": regInfo.Address, @@ -109,34 +65,35 @@ func (values *Values) GetVariables(component types.ZarfComponent) (templateMap m "GIT_AUTH_PULL": gitInfo.PullPassword, } - // Include the data injection marker template if the component has data injections - if len(component.DataInjections) > 0 { - // Preserve existing misspelling for backwards compatibility - builtinMap[depMarkerOld] = config.GetDataInjectionMarker() - builtinMap[depMarkerNew] = config.GetDataInjectionMarker() - } + // Preserve existing misspelling for backwards compatibility + builtinMap[depMarkerOld] = config.GetDataInjectionMarker() + builtinMap[depMarkerNew] = config.GetDataInjectionMarker() // Don't template component-specific variables for every component - switch component.Name { + switch componentName { case "zarf-agent": - agentTLS := values.config.State.AgentTLS + agentTLS := state.AgentTLS builtinMap["AGENT_CRT"] = base64.StdEncoding.EncodeToString(agentTLS.Cert) builtinMap["AGENT_KEY"] = base64.StdEncoding.EncodeToString(agentTLS.Key) builtinMap["AGENT_CA"] = base64.StdEncoding.EncodeToString(agentTLS.CA) case "zarf-seed-registry", "zarf-registry": builtinMap["SEED_REGISTRY"] = fmt.Sprintf("%s:%s", helpers.IPV4Localhost, config.ZarfSeedPort) - builtinMap["HTPASSWD"] = values.htpasswd + htpasswd, err := generateHtpasswd(®Info) + if err != nil { + return templateMap, err + } + builtinMap["HTPASSWD"] = htpasswd builtinMap["REGISTRY_SECRET"] = regInfo.Secret case "logging": - builtinMap["LOGGING_AUTH"] = values.config.State.LoggingSecret + builtinMap["LOGGING_AUTH"] = state.LoggingSecret } // 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))] = &TextTemplate{ + templateMap[strings.ToUpper(fmt.Sprintf("###ZARF_%s###", key))] = &variables.TextTemplate{ Value: value, } @@ -149,131 +106,39 @@ 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))] = &TextTemplate{ - Value: variable.Value, - Sensitive: variable.Sensitive, - AutoIndent: variable.AutoIndent, - Type: variable.Type, - } - } - - 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))] = &TextTemplate{ - Value: constant.Value, - AutoIndent: constant.AutoIndent, - } - } - debugPrintTemplateMap(templateMap) - message.Debugf("deprecations = %#v", deprecations) - return templateMap, deprecations + return templateMap, nil } -// Apply renders the template and writes the result to the given path. -func (values *Values) Apply(component types.ZarfComponent, path string, ignoreReady bool) error { - // If Apply() is called before all values are loaded, fail unless ignoreReady is true - if !values.Ready() && !ignoreReady { - return fmt.Errorf("template.Apply() called before template.Generate()") +// deprecatedKeys returns a map of template keys that are deprecated +func deprecatedKeys() map[string]string { + return map[string]string{ + fmt.Sprintf("###ZARF_%s###", depMarkerOld): fmt.Sprintf("###ZARF_%s###", depMarkerNew), } - - templateMap, deprecations := values.GetVariables(component) - err := ReplaceTextTemplate(path, templateMap, deprecations, "###ZARF_[A-Z0-9_]+###") - - return err } -// 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