diff --git a/0048-schema-update-process/README.md b/0048-schema-update-process/README.md index 5f130b0..65841c1 100644 --- a/0048-schema-update-process/README.md +++ b/0048-schema-update-process/README.md @@ -186,7 +186,7 @@ As a package creator, I want to create and publish packages using the newer API #### Story 3 -As a package creator, I want to update my package definition to the v1beta1 schema, so I run `zarf dev upgrade-schema --to v1beta1` with a zarf.yaml in my current directory and it outputs the converted package definition to stdout. +As a package creator, I want to update my package definition to the v1beta1 schema, so I run `zarf dev upgrade-schema . --to v1beta1 > zarf.yaml` against the directory containing my zarf.yaml. The command writes the converted definition to stdout, replacing my existing zarf.yaml. #### Story 4 @@ -472,6 +472,10 @@ not need to be as detailed as the proposal, but should include enough information to express the idea and why it was not acceptable. --> +### Use only one API version + +Instead of introducing a new schema, we could over time remove and introduce fields on the existing package schema. This was rejected as we believe a new schema will provide a more intuitive experience for developers and maintainers. For instance, attempting to implement the proposed Helm changes would result in a large, confusing schema. Likewise, it would be difficult to change defaults, such as moving from required to optional, since we wouldn't have a new API version to signal that there is a breaking change. Additionally, removing deprecated fields such as `dataInjections` would require a major version change. Keeping the API version and CLI version decoupled allows flexibility to make the changes we need. + ### Public Facing Internal Type Rather than updating functions to accept a newer version of the schema, Zarf could have a publicly facing internal type that has every field from every version and use that throughout the SDK. The upside of this approach is that we would avoid breaking changes throughout the lifetime of the SDK. The downside is that it would make it easy for anyone using the SDK to set deprecated fields. It would also make it confusing and unclear which fields attach to which versions. diff --git a/0048-schema-update-process/zep.yaml b/0048-schema-update-process/zep.yaml index c7a5052..6a25c77 100644 --- a/0048-schema-update-process/zep.yaml +++ b/0048-schema-update-process/zep.yaml @@ -12,19 +12,17 @@ approvers: - "@zarf-dev" see-also: - # Will add the v1 schema here when applicable + - "/0051-v1beta1-schema" replaces: # The target maturity stage in the current dev cycle for this ZEP. -stage: alpha +stage: "" # The most recent milestone for which work toward delivery of this ZEP has been # done. This can be the current (upcoming) milestone, if it is being actively # worked on. -latest-milestone: "v0.65.0" +latest-milestone: "v0.75.0" # The milestone at which this feature was, or is targeted to be, at each stage. milestone: - alpha: "v0.65.0" - beta: "v0.65.0" - stable: "v0.65.0" + stable: "TBD" diff --git a/0051-v1beta1-schema/README.md b/0051-v1beta1-schema/README.md new file mode 100644 index 0000000..d64dbcd --- /dev/null +++ b/0051-v1beta1-schema/README.md @@ -0,0 +1,708 @@ + + +# ZEP-0051: v1beta1 schema + + + + +- [Summary](#summary) +- [Motivation](#motivation) + - [Goals](#goals) + - [Non-Goals](#non-goals) +- [Proposal](#proposal) + - [User Stories (Optional)](#user-stories-optional) + - [Story 1](#story-1) + - [Story 2](#story-2) + - [Risks and Mitigations](#risks-and-mitigations) +- [Design Details](#design-details) + - [Test Plan](#test-plan) + - [Prerequisite testing updates](#prerequisite-testing-updates) + - [Unit tests](#unit-tests) + - [e2e tests](#e2e-tests) + - [Graduation Criteria](#graduation-criteria) + - [Upgrade / Downgrade Strategy](#upgrade--downgrade-strategy) + - [Version Skew Strategy](#version-skew-strategy) +- [Implementation History](#implementation-history) +- [Drawbacks](#drawbacks) +- [Alternatives](#alternatives) + + +## Summary + + + +Several fields in the v1alpha1 ZarfPackageConfig should be restructured to provide a more intuitive experience. Other fields that have a poor user experience and add unnecessary overhead to Zarf should be removed. Introducing a new schema version, v1beta1, provides the opportunity to make these changes. + +## Motivation + + + +There are several open issues requesting enhancements to the schema. The general theme of these changes is to make it easier to create Zarf packages. +- [Refactor charts definition in zarf.yaml #2245](https://github.com/zarf-dev/zarf/issues/2245) +- [Breaking Change: make components required by default #2059](https://github.com/zarf-dev/zarf/issues/2059) +- [Use kstatus as the engine behind zarf tools wait-for and .wait.cluster #4077](https://github.com/zarf-dev/zarf/issues/4077) + +Additionally, users often struggle to use data injections. Usually, they would be better served by using a Kubernetes native solution [#3926](https://github.com/zarf-dev/zarf/issues/3926). + +### Goals + + + +- Detail the schema changes in the ZarfPackageConfig from v1alpha1 to v1beta1. + +### Non-Goals + + + +- Discuss how the Zarf codebase will shift to handle multiple API versions. This is detailed in [0048-schema-update-process](../0048-schema-update-process/README.md). + +## Proposal + + + +Zarf will determine the schema of the package definition using the existing optional field `apiVersion`. Until v1alpha is removed, if `apiVersion` is not set, then Zarf will assume it is a v1alpha1 package. `apiVersion` will be a required field in v1beta1 and all future schemas. + +Type names will remove the prefix Zarf where applicable. For example, the type `ZarfMetadata` will become `Metadata`. This has no impact on the package yaml. + +Users will be able to upgrade their package definitions using `zarf dev upgrade-schema`, which writes the converted definition to stdout. + +The v1beta1 schema will remove, replace, and rename several fields. View this [zarf.yaml](zarf.yaml) to see a package definition with reasonable values for each key. + +### Schema changes + +#### Removed Fields + +If a package has these fields defined, then `zarf dev upgrade-schema` will error and print a recommendation for an alternative. + +- `.components.[x].group` will be removed. Similar functionality was introduced with the field `components[x].target.flavor`. This shifts component selection to the create side, and is the recommended replacement. +- `.components.[x].default` will be removed. It was used to determine the default `.components[x].group`. It also gave the default to the (Y/N) prompt during interactive deploys, this use was secondary and not important enough to keep the field around. +- `.components.[x].dataInjections` will be removed. https://docs.zarf.dev/best-practices/data-injections-migration/ details migrating off of this field. +- `.components.[x].charts.[x].variables` will be removed. Users are encouraged to use [Zarf values](../0021-zarf-values/) instead. +- `.variables` and `.constants` will be removed. Users are encouraged to use [Zarf values](../0021-zarf-values/) instead. While values will always be mutable on deploy, creators will be able to choose which values in their chart are mutable using `sourcePath/targetPath`. Similarly, creators decide which fields in manifests are mutable through values. +- `.components.[x].actions.[onAny].setVariable` and `.components.[x].actions.[onAny].setVariables` will be removed. The existing `.components.[x].actions.[onAny].setValues` field is the replacement. +- `.metadata.yolo` will be removed. Its successor is connected deployments [#4580](https://github.com/zarf-dev/zarf/issues/4580). +- `.components.[x].only.cluster.distro` will be removed. This field was never used for anything and there are currently no plans to use it. + +#### Replaced / Restructured Fields + +`zarf dev upgrade-schema` will automatically migrate these fields. + +- `.components.[x].actions.[onAny].after` will be combined with and replaced by `actions.[onAny].onSuccess`. Any `after` actions will be prepended to the `actions.[onAny].onSuccess` list. +- `.components.[x].scripts` will be removed. This field is already deprecated and will be migrated to the existing field `.components.[x].actions`. +- `.components.[x].only.cluster.architecture` will be inlined to `.components.[x].target.architecture`. This is more accurate as the field checks the `.metadata.architecture` on create, rather than the cluster during deploy. Note that `.only` was renamed to `.target`. Since `.cluster.distro` will be removed, the `.cluster` parent field will be deleted. +- `.metadata` fields `image`, `source`, `documentation`, `url`, `authors`, and `vendor` will be removed. `zarf dev upgrade-schema` will move these fields under `.metadata.annotations`, which is a generic map of strings. +- `.components.[x].healthChecks` will be removed and appended to `.components.[x].actions.onDeploy.onSuccess.wait.cluster`. This will be accompanied by a behavior change in `zarf tools wait-for` to perform kstatus-style readiness checks when `.wait.cluster.condition` is empty. See [wait changes](#wait-changes). +- `.components.[x].charts` will be restructured to move fields into different sub-objects depending on the method of consuming the chart. See [Helm Chart Changes](#zarf-helm-chart-changes). +- `.components.[x].images` will move from a list of strings to a list of objects. The `Image` object will have a required field, `name`, and an optional enum, `source`. Allowed values for `source` will be `daemon` and `registry`. Zarf will no longer fall back to pulling images from the Docker Daemon. During component imports, the merge strategy will change from a simple append to a merge based on `name`. `source` and any future fields will favor the base component value if set, and otherwise use the imported component value. +- `.components.[x].import.name` will be removed given that components will only be importable from component config files so there is not a name to select. See [ZarfComponentConfig](#zarfcomponentconfig). +- `.components.[x].import.path` and `.components.[x].import.url` will be changed into `.components.[x].import.local.[x].path` and `.components.[x].import.remote.[x].url`. All entries from both are combined when applying component compatibility rules. See [ZarfComponentConfig](#zarfcomponentconfig). These fields are a list of objects instead of a list of strings to enable future sibling fields. For instance, we may introduce a field `.components.[x].import.remote.[x].verify` to enable verifying the signature of signed remote components. +- `.components.[x].manifests.[x].kustomizations`, `.components.[x].manifests.[x].kustomizeAllowAnyDirectory`, and `.components.[x].manifests.[x].enableKustomizePlugins` will be moved into a `.components.[x].manifests.[x].kustomize` sub-object. The fields become `kustomize.files`, `kustomize.allowAnyDirectory`, and `kustomize.enablePlugins` respectively. + +#### Renamed Fields + +`zarf dev upgrade-schema` will automatically migrate these fields. + +- `.metadata.aggregateChecksum` will move to `.build.aggregateChecksum`. +- `.build.terminal` will be renamed to `.build.hostname`. +- `.components.[x].manifests.[x].noWait` and `.components.[x].charts.[x].noWait` will be renamed to `skipWait`. +- `.components[x].required` will be renamed to `.components[x].optional`. `optional` will default to false. Since `required` currently defaults to false, components will now default to being required. +- `.components.[x].actions.[default/onAny].maxRetries` will be renamed to `.components.[x].actions.[default/onAny].retries`. +- `.components.[x].actions.[default/onAny].mute` will be renamed to `.components.[x].actions.[default/onAny].silent`. +- `.components.[x].manifests.[x].template`, `.components.[x].files.[x].template`, and `.components.[x].actions.[onAny].template` will be renamed to `enableValues`. +- `.components.[x].charts.[x].schemaValidation` will be renamed to `skipSchemaValidation`. The field now defaults to `false` instead of `true`, so schema validation continues to run by default. +- `.metadata.allowNamespaceOverride` will be renamed to `preventNamespaceOverride`. The field now defaults to `false` instead of `true`, so namespace overrides continue to be allowed by default. +- `.components.[x].only` will be renamed to `.components.[x].target`. +- `.components.[x].only.localOS` will be renamed to `.components.[x].target.os`. +- `.components.[x].repos` will be renamed to `.components.[x].repositories`. +- `.components.[x].files.[x].target` will be renamed to `.components.[x].files.[x].destination`. +- `.components.[x].files.[x].shasum` will be renamed to `.components.[x].files.[x].checksum`. The field accepts the format `:` (e.g. `sha256:abc123`); if no algorithm prefix is provided, sha256 is assumed. + +### New Fields + +- `.components.[x].service` will be introduced to avoid magic names in Init package components. See [Zarf Services](#zarf-services) for more details. + +### Behavior Changes + +#### Wait Changes + +There will be a behavior change in `.components[x].actions.[onAny].wait.cluster`. In the v1alpha1 ZarfPackageConfig, when `.cluster.condition` is empty, Zarf waits until the resource exists. In the v1beta1 schema, when `.cluster.condition` is empty, Zarf will wait for the resource to be ready using kstatus readiness checks. When migrating from v1alpha1 to v1beta1, if `cluster.condition` is not set, then it will be automatically populated with "exists" for backwards compatibility with existing packages. + +#### Zarf Services + +In the v1alpha1 schema, Zarf looks at init component names to determine when to run certain logic. For instance, the injector is always run when an init component has the name "zarf-seed-registry". These magical names have caused confusion for custom init package creators, [#4528](https://github.com/zarf-dev/zarf/issues/4528), and leave little room for configurability. + +A new `service` key under components will make the inherent coupling between the init package and the Zarf CLI more transparent. The field is an enum with the allowed values `registry`, `seed-registry`, `injector`, `agent`, and `git-server`. + +View the full schema in [package.go](package.go#L200). + +```yaml +- name: zarf-registry + service: registry +- name: zarf-agent + service: agent + ... +``` + +### ZarfInitConfig will be Removed + +The `Kind` "ZarfInitConfig" will be removed. Every package will be of kind "ZarfPackageConfig". `zarf init` will default to deploying a package called `zarf-package-init--.tar.zst`. A template will be created that exposes the CLI version, so a `zarf.tpl.yaml` file could set the `.metadata.version` field to `[[ .cli.version ]]`. If a package called `zarf-package-init--.tar.zst` is not found in the cache or current directory, Zarf will prompt the user to pull the default zarf-dev init package. `zarf init` will continue to accept custom packages, for example, `zarf init `. If no component in the package declares a `.service`, Zarf will error and ask the user to run `zarf package deploy` instead. + +### ZarfComponentConfig + +The v1beta1 APIVersion will introduce a new `Kind` alongside ZarfPackageConfig called ZarfComponentConfig. ZarfComponentConfig files will allow declaring a component to be imported from other packages. It will have its own schema, and this schema will be verified on create and publish. ZarfComponentConfigs will be importable only by v1beta1 packages. Components from other ZarfPackageConfigs will not be importable in v1beta1 packages. + +Each ZarfComponentConfig declares exactly one component under the `component` field. If a user wants multiple variations of a component differentiated by flavor, OS, or architecture, they create one ZarfComponentConfig file per variation and set the `.component.target` field on each. View the ZarfComponentConfig schema in [design details](#zarf-component-config-schema). + +The component in a ZarfComponentConfig will be able to import another ZarfComponentConfig. Cyclical imports will result in an error. ZarfComponentConfig files will not have a default filename such as zarf.yaml. This will encourage users to give their files descriptive names and promote a flatter directory structure as users will not default to having a new folder for each component. ZarfComponentConfigs will be able to define their own values and valuesSchema. + +`.import.local` is a list of local file path references to ZarfComponentConfig files; directories are not accepted. `.import.remote` is a list of `oci://` URL references to remote component configs pulled at create time. All entries from both fields are combined when applying compatibility rules: when more than one entry is given, every referenced component must share the same name, and at most one of them must be compatible with the active package target (flavor, OS, architecture) at create time, otherwise Zarf will error. + +The `zarf dev` commands that accept a directory containing a `zarf.yaml` (lint, inspect, and find-images) will accept component config files. For example, `zarf dev inspect definition my-component-config.yaml`. + +#### Remote Components + +Skeleton packages will be replaced by remote components. Instead of publishing an entire package, users will be able to publish a ZarfComponentConfig. This component will behave similarly to Skeleton packages in that local resources will be published alongside it, while remote resources will be pulled at create time. + +Remote components will be published using the new command `zarf component publish `. This command will have the flag `--flavor` to publish a component whose `.component.target.flavor` matches the supplied value. + +Unlike Skeleton packages, which are published with unresolved templates, remote components must be fully templated before publishing. By templating before publish, we avoid issues with validating a non-templated package ([#4491](https://github.com/zarf-dev/zarf/issues/4491)) and stay aligned with the overall [Package Templates](#package-templates) strategy. + +### Package Templates + +The Zarf v1alpha1 schema allows for package templates during create using the ###ZARF_PKG_TMPL_*### format. This format will be replaced in the v1beta1 schema with Go templating. Additionally, instead of templating on create, a new command `zarf dev template` will be introduced. This command will take in a zarf.tpl.yaml file, and will output a zarf.gen.yaml file based on the Go templating result. The command will accept a flag `--set` to set templates and a flag `--set-file` which will accept a values style file to define templates. + +The `.gen` extension will be used to easily discern between generated and included packages. It will also make it simple to ignore these files within Git repositories. When `zarf package create`, or any other relevant command, is run on a directory, it will first look for a `zarf.yaml`, then fall back to a `zarf.gen.yaml`. + +`zarf dev template` will have logic to follow local component imports. For any entry in `.import.local` whose `path` points to a file called `.tpl.yaml`, Zarf will template the `.tpl.yaml` file and rewrite the entry to `.gen.yaml`. Users who prefer to template in separate steps may set their import path entries to `.gen.yaml` directly. Zarf will template imports after the current file is finished templating, so a user will be able to template a value into an entry of `.import.local` and Zarf will template the resulting file. + +Package templates will be required to have a value; otherwise the command will fail. + +The delimiter for Go templates during `zarf dev template` will be `[[ ]]`. This will separate package templates from the standard Go template delimiter `{{ }}`, which is used during on-deploy actions. + +### User Stories (Optional) + + + +#### Story 1 + +As a user of Helm charts in my package, I have the following existing `zarf.yaml`: + +```yaml +kind: ZarfPackageConfig +metadata: + name: helm-charts +components: + - name: demo-helm-charts + required: true + charts: + - name: podinfo-local + version: 6.4.0 + namespace: podinfo-from-local-chart + localPath: chart + valuesFiles: + - values.yaml + + - name: podinfo-oci + version: 6.4.0 + namespace: podinfo-from-oci + url: oci://ghcr.io/stefanprodan/charts/podinfo + valuesFiles: + - values.yaml + + - name: podinfo-git + version: 6.4.0 + namespace: podinfo-from-git + url: https://github.com/stefanprodan/podinfo.git + gitPath: charts/podinfo + valuesFiles: + - values.yaml + + - name: podinfo-repo + version: 6.4.0 + namespace: podinfo-from-repo + url: https://stefanprodan.github.io/podinfo + name: podinfo + releaseName: cool-release-name + valuesFiles: + - values.yaml +``` + +I want to upgrade to the v1beta1 schema, so I run `zarf dev upgrade-schema . > zarf.yaml`, which produces: + +```yaml +apiVersion: zarf.dev/v1beta1 +kind: ZarfPackageConfig +metadata: + name: helm-charts +components: + - name: demo-helm-charts + optional: false # Changed from `required: true` + charts: + - name: podinfo-local + namespace: podinfo-from-local-chart + local: + path: chart # Changed from `localPath` + # version field removed - uses version from local chart.yaml + valuesFiles: + - values.yaml + - name: podinfo-oci + namespace: podinfo-from-oci + oci: + url: oci://ghcr.io/stefanprodan/charts/podinfo + version: 6.4.0 + valuesFiles: + - values.yaml + + - name: podinfo-git + namespace: podinfo-from-git + git: + url: https://github.com/stefanprodan/podinfo.git@6.4.0 + path: charts/podinfo # Changed from `gitPath` + # version field removed - uses version from chart.yaml at git tag + valuesFiles: + - values.yaml + + - name: podinfo-repo + namespace: podinfo-from-repo + helmRepository: + url: https://stefanprodan.github.io/podinfo + name: podinfo # Changed from `repoName` + version: 6.4.0 + releaseName: cool-release-name + valuesFiles: + - values.yaml +``` + +#### Story 2 + +As a package creator, I have a logging component that I maintain locally and want to use a monitoring component published by my team to an OCI registry. I combine both into a single v1beta1 package. + +First, I define my local logging component in `logging.yaml`: + +```yaml +apiVersion: zarf.dev/v1beta1 +kind: ZarfComponentConfig +metadata: + name: logging +component: + charts: + - name: loki + namespace: logging + local: + path: loki-chart + valuesFiles: + - loki-values.yaml +``` + +My teammate has published a monitoring component to our registry. Its source file, `monitoring.yaml`, looked like this before publishing: + +```yaml +apiVersion: zarf.dev/v1beta1 +kind: ZarfComponentConfig +metadata: + name: monitoring + version: 1.0.0 +component: + charts: + - name: kube-prometheus-stack + namespace: monitoring + helmRepository: + url: https://prometheus-community.github.io/helm-charts + name: kube-prometheus-stack + version: 60.0.0 + valuesFiles: + - prometheus-values.yaml +``` + +They published it with: + +```bash +zarf component publish monitoring.yaml oci://ghcr.io/my-org/components +``` + +Now I create a v1beta1 package that imports both components -- the local one by file path and the remote one by URL: + +```yaml +apiVersion: zarf.dev/v1beta1 +kind: ZarfPackageConfig +metadata: + name: observability + description: Combines logging and monitoring into a single package + +components: + - name: logging + import: + local: + - path: logging.yaml + - name: monitoring + import: + remote: + - url: oci://ghcr.io/my-org/components/monitoring:1.0.0 +``` + +I can then create my package as usual: + +```bash +zarf package create +``` + +#### Story 3 + +As a package creator, I want to template image references and metadata into my package at build time. I write a `zarf.tpl.yaml` that uses Go templates with the `[[ ]]` delimiter: + +```yaml +apiVersion: zarf.dev/v1beta1 +kind: ZarfPackageConfig +metadata: + name: app + description: "app [[ .ENVIRONMENT ]]" + +components: + - name: app + charts: + - name: app + namespace: app + oci: + url: oci://ghcr.io/my-org/charts/my-app + version: 1.0.0 + images: + - name: [[ .MY_IMAGE ]] +``` + +I generate a `zarf.gen.yaml` for a specific release: + +```bash +zarf dev template --set ENVIRONMENT=personal --set MY_IMAGE=ghcr.io/my-org/my-image:0.0.1 +``` + +This produces `zarf.gen.yaml`: + +```yaml +apiVersion: zarf.dev/v1beta1 +kind: ZarfPackageConfig +metadata: + name: app + description: "app personal" + +components: + - name: app + charts: + - name: app + namespace: app + oci: + url: oci://ghcr.io/my-org/charts/my-app + version: 1.0.0 + images: + - name: ghcr.io/my-org/my-image:0.0.1 +``` + +I can then create my package from the generated file: + +```bash +zarf package create +``` + +### Risks and Mitigations + + + +The field `.components.[x].dataInjections` will be removed without a direct replacement in the schema. The docs website added a [migration page](https://docs.zarf.dev/best-practices/data-injections-migration/) to inform users how to switch. + +The alpha field `.components.[x].charts.[x].variables` has seen significant adoption and there will be no automatic conversion to its replacement Zarf values. There must be documentation on how users can utilize Zarf values as an alternative to chart variables. + +## Design Details + + + +### Zarf Helm Chart Changes + +The `Chart` object will be restructured as seen in [package.go](package.go#L242-L308). Exactly one of sub-objects `helmRepository`, `git`, `oci`, or `local` is required for each entry in `components.[x].charts`. The fields `localPath`, `gitPath`, `URL`, and `repoName` will be removed from the top level of `components.[x].charts`. See [#2245](https://github.com/zarf-dev/zarf/issues/2245). + +During conversion, Zarf will detect the method of consuming the chart and create the proper sub-objects. If a git repo is used, then `@` + the `.version` value will be appended to `.git.URL`. This is consistent with the current Zarf behavior. + +Zarf uses the top-level `version` field to determine where in the package layout file structure it will place charts. This makes the field necessary for deploy, and therefore it must be carried over using the strategy defined in the removed fields section of [0048-schema-update-process](../0048-schema-update-process/README.md#converting-removed-fields). Newer versions of Zarf will ensure that Zarf works whether or not `version` is set. Packages created with the v1beta1 schema will leave `version` empty, and therefore will not work with earlier versions of Zarf. When support is dropped for v1alpha1 packages, the `version` field will be dropped entirely. Note that this process is applied to internal conversion so that there is no change in behavior when v1alpha1 packages use function signatures that contain v1beta1 objects. `zarf dev upgrade-schema` will simply move the top-level `version` field to the right sub-object, or drop it when not applicable. + +### Zarf Component Config Schema + +View the schema for the Zarf component config in [componentConfig.go](componentConfig.go). + +### Test Plan + + + +[X] I/we understand the owners of the involved components may require updates to +existing tests to make this code solid enough prior to committing the changes necessary +to implement this proposal. + +There will be e2e tests for creating, deploying, and publishing a v1beta1 package. Existing tests will shift to use the v1beta1 schema over time. + +### Graduation Criteria + + + +The v1beta1 schema will not have an alpha/beta/GA phase. It will follow the graduation criteria laid out in [0048-schema-update-process](../0048-schema-update-process/README.md#graduation-criteria). + +Deprecation: +- This schema will likely be deprecated one day in favor of a v1 schema. It will not be deprecated until after the next schema version is generally available. Once deprecated, Zarf will still support the v1beta1 schema for at least one year. + +### Upgrade / Downgrade Strategy + + + +See the proposal section in [0048-schema-update-process](../0048-schema-update-process/README.md#proposal). + +### Version Skew Strategy + + + +See the version skew strategy in [0048-schema-update-process](../0048-schema-update-process/README.md#version-skew-strategy). + +## Implementation History + + + +- 2025-10-21: Proposal submitted +- 2026-02-12: Introduced Zarf Component Config, package templating changes, and Zarf services +- 2026-05-06: Simplified ZarfComponentConfig to one component per file; moved variants to alternatives + +## Drawbacks + + + +### Component Import Reworks +Removing the ability to import components from packages directly, and instead requiring Zarf Component Config files, will require a sizable portion of the user base to rewrite files. This rewrite should leave users with a clearer directory structure, enhanced package validation, and a more intuitive import system. + +Removing the ability to import from ZarfPackageConfig files will add some friction to standalone packages that are also imported. For instance, the [k3s sub-package](https://github.com/zarf-dev/zarf/blob/main/packages/distros/k3s/zarf.yaml) in the init package is deployable as a standalone package and imported by the init package. The proposed system would require creating a component config as well as a separate standalone k3s package that imports the component config to maintain the current structure. This drawback is deemed necessary to avoid packages that are only meant for import and not deployable as a standalone package. This has caused confusion among many users, and forcing creators to explicitly make a sub-package deployable will avoid this issue. + +### Component Config + +There is an implicit ordering in a zarf.yaml file: the first component in a list is installed, then the second, and so forth. By asking users to break apart their zarf.yaml files into Zarf Component Config files, they may lose this implicit ordering, and it could be more confusing to determine the order of components. + +## Alternatives + + + +### Component Config Schema + +#### Variants in ZarfComponentConfig + +A previous version of this proposal allowed a ZarfComponentConfig to declare either a `.component` field or a `.variants[]` field. The `.component` field was a single object representing a component importable by any package. The `.variants` field was a list of components where each entry had to specify a `.target` block (e.g. flavors, OSes, or architectures) to differentiate itself from the other entries. Zarf would error if two entries under `.variants` shared the same target. The `zarf component publish` command would have grown a `--all-variants` flag to publish every variant in one file at once. + +This was rejected in favor of "exactly one component per file" to keep the mental model simple. With variants, a single file could expand into many components depending on flavor/OS/arch, and authors had to reason about which entry applied where. Forcing a 1:1 file-to-component mapping makes the import tree easy to follow at the cost of a few extra files for components with multiple targets. + +#### List of Components + +Another possibility for the [component config schema](#zarf-component-config-schema) was to have a list of components under a `.components[]` field, where each entry must specify a `.target` block. This was rejected since a major change in this system is that `ZarfComponentConfig` files represent a single component. The plural `.components[]` key would likely confuse users on this aspect. + +#### Variants Extend Base Component + +Another possibility for the [component config schema](#zarf-component-config-schema) is to have a single `.component` field that can be extended by a list of `.variants`. The `.component` field would be required, and could be imported or published as defined. It could also be extended using the `.variants` field. The logic for extending would exactly mirror the [component import logic](https://docs.zarf.dev/ref/components/#component-imports); the variant would import the base component. + +This would be especially useful when there are multiple configurations of a chart, such as the example below. Each flavor prescribes its own values file and images, but otherwise is the same. A similar situation is seen in the [k3s sub-package](https://github.com/zarf-dev/zarf/blob/main/packages/distros/k3s/zarf.yaml) of the main Zarf repository. The only differences between the two k3s components are the files that vary by architecture. + +```yaml +apiVersion: zarf.dev/v1beta1 +kind: ZarfComponentConfig +metadata: + name: grafana +component: + - charts: + - name: grafana-config + namespace: grafana + local: + path: chart + valuesFiles: + - chart/values.yaml +variants: + - target: + flavor: upstream + charts: + - name: grafana + valuesFiles: + - values/upstream-values.yaml + images: + - name: docker.io/grafana/grafana:12.4.2 + + - target: + flavor: enterprise + charts: + - name: grafana + valuesFiles: + - values/enterprise-values.yaml + images: + - name: enterprise.corp.org/grafana/grafana:12.4.2 +``` + +### Remote Component Templating + +Remote components cannot be templated during import; this is a removed feature from its predecessor Skeleton packages. This allows Zarf to validate the component before it's published ([#4491](https://github.com/zarf-dev/zarf/issues/4491)) and is necessary since package templating now occurs before create. A potential alternative is a templated remote component where `zarf dev template oci://ghcr.io/` would download the component from OCI and template it. The user would then be able to import the component from their local directory. This was rejected because it adds complexity for a niche use case. This could be a future enhancement if the demand exists. + +### Component Level Action Defaults + +Action defaults could be set once at the component level rather than separately under each action set (`onCreate`, `onDeploy`, `onRemove`). This would reduce the schema's surface area. + +This was rejected. Create and deploy often run on separate hosts and have different jobs: `onCreate` actions typically pull files or load images as docker tars, while `onDeploy` actions typically run `kubectl` or stand up a cluster. Sharing defaults across that boundary creates an awkward mental model. The [example v1beta1 zarf.yaml](./zarf.yaml) is large because every action set has its own defaults block, but in practice actions are an escape hatch used sparingly. It is rare for a real component to define both `onCreate` and `onDeploy` actions. \ No newline at end of file diff --git a/0051-v1beta1-schema/componentConfig.go b/0051-v1beta1-schema/componentConfig.go new file mode 100644 index 0000000..d574ce5 --- /dev/null +++ b/0051-v1beta1-schema/componentConfig.go @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2021-Present The Zarf Authors + +package v1beta1 + +// ComponentConfig is the top-level structure of a Zarf component config file. +type ComponentConfig struct { + // The API version of the component config. + APIVersion string `json:"apiVersion" jsonschema:"enum=zarf.dev/v1beta1"` + // The kind of component config. + Kind PackageKind `json:"kind" jsonschema:"enum=ZarfComponentConfig,default=ZarfComponentConfig"` + // Component metadata. + Metadata ComponentMetadata `json:"metadata"` + // The single component this config defines. + Component Component `json:"component"` + // Values imports Zarf values files for templating and overriding Helm values. + Values Values `json:"values,omitempty"` + // Zarf-generated publish data for the component config. + PublishData ComponentPublishData `json:"publishData,omitempty"` +} + +// ImportableComponent is a reduced component definition used in component configs. +type ImportableComponent struct { + // Import a component from another Zarf component config. + Import ComponentImport `json:"import,omitempty"` + // Filter when this component is included in package creation or deployment. + Target ComponentTarget `json:"target,omitempty"` + // Kubernetes manifests to be included in a generated Helm chart on package deploy. + Manifests []Manifest `json:"manifests,omitempty"` + // Helm charts to install during package deploy. + Charts []Chart `json:"charts,omitempty"` + // Files or folders to place on disk during package deployment. + Files []File `json:"files,omitempty"` + // List of OCI images to include in the package. + Images []Image `json:"images,omitempty"` + // List of tar archives of images to include in the package. + ImageArchives []ImageArchive `json:"imageArchives,omitempty"` + // List of git repositories to include in the package. + Repositories []string `json:"repositories,omitempty"` + // Custom commands to run at various stages of a package lifecycle. + Actions ComponentActions `json:"actions,omitempty"` + // The Zarf CLI service this component provides, such as the registry, injector, or agent. + Service Service `json:"service,omitempty" jsonschema:"enum=registry,enum=seed-registry,enum=injector,enum=agent,enum=git-server"` +} + +// ComponentMetadata holds metadata about a component config. +type ComponentMetadata struct { + // Name to identify this component config. + Name string `json:"name" jsonschema:"pattern=^[a-z0-9][a-z0-9\\-]*$"` + // Additional information about this component config. + Description string `json:"description,omitempty"` + // Generic string to track the component config version. + Version string `json:"version,omitempty"` + // Annotations contains arbitrary metadata about the component config. + Annotations map[string]string `json:"annotations,omitempty"` +} + +// ComponentPublishData is written during publish to track details of the component config. +type ComponentPublishData struct { + // The version of Zarf used to build this component config. + ZarfVersion string `json:"zarfVersion"` + // Any migrations that have been run on this component config. + Migrations []string `json:"migrations,omitempty"` + // Requirements for specific package operations. + VersionRequirements []VersionRequirement `json:"versionRequirements,omitempty"` +} diff --git a/0051-v1beta1-schema/package.go b/0051-v1beta1-schema/package.go new file mode 100644 index 0000000..4cb6b85 --- /dev/null +++ b/0051-v1beta1-schema/package.go @@ -0,0 +1,439 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2021-Present The Zarf Authors + +// Package v1beta1 holds the definition of the v1beta1 Zarf Package. +package v1beta1 + +// PackageKind is an enum of the different kinds of Zarf packages. +type PackageKind string + +const ( + // ZarfPackageConfig is the default kind of Zarf package. + ZarfPackageConfig PackageKind = "ZarfPackageConfig" + // APIVersion is the api version of this package. + APIVersion string = "zarf.dev/v1beta1" +) + +// Package is the top-level structure of a Zarf package definition. +type Package struct { + // The API version of the Zarf package. + APIVersion string `json:"apiVersion" jsonschema:"enum=zarf.dev/v1beta1"` + // The kind of Zarf package. + Kind PackageKind `json:"kind" jsonschema:"enum=ZarfPackageConfig"` + // Package metadata. + Metadata PackageMetadata `json:"metadata,omitempty"` + // Zarf-generated package build data. + Build BuildData `json:"build,omitempty"` + // List of components to deploy in this package. + Components []Component `json:"components" jsonschema:"minItems=1"` + // Values imports Zarf values files for templating and overriding Helm values. + Values Values `json:"values,omitempty"` + // Documentation files included in the package. + Documentation map[string]string `json:"documentation,omitempty"` +} + +// Metadata holds information about the package. +type PackageMetadata struct { + // Name to identify this Zarf package. + Name string `json:"name" jsonschema:"pattern=^[a-z0-9][a-z0-9\\-]*$"` + // Additional information about this Zarf package. + Description string `json:"description,omitempty"` + // Generic string set by a package author to track the package version. + Version string `json:"version,omitempty"` + // Disable compression of this package. + Uncompressed bool `json:"uncompressed,omitempty"` + // The target cluster architecture for this package. + Architecture string `json:"architecture,omitempty" jsonschema:"example=arm64,example=amd64"` + // Annotations are key-value pairs that can be used to store metadata about the package. + Annotations map[string]string `json:"annotations,omitempty"` + // Prevent namespace overrides for this package. + PreventNamespaceOverride bool `json:"preventNamespaceOverride,omitempty"` +} + +// BuildData is written during package create to track details of the created package. +type BuildData struct { + // The machine name that created this package. + Hostname string `json:"hostname,omitempty"` + // The username who created this package. + User string `json:"user,omitempty"` + // The architecture this package was created on. + Architecture string `json:"architecture"` + // The timestamp when this package was created. + Timestamp string `json:"timestamp"` + // The version of Zarf used to build this package. + Version string `json:"version"` + // Any migrations that have been run on this package. + Migrations []string `json:"migrations,omitempty"` + // Any registry domains that were overridden on package create when pulling images. + RegistryOverrides map[string]string `json:"registryOverrides,omitempty"` + // Whether this package was created with differential components. + Differential bool `json:"differential,omitempty"` + // The flavor of Zarf used to build this package. + Flavor string `json:"flavor,omitempty"` + // Requirements for specific Zarf versions needed to deploy this package. + VersionRequirements []VersionRequirement `json:"versionRequirements,omitempty"` + // Checksum of a checksums.txt file that contains checksums all the layers within the package. + AggregateChecksum string `json:"aggregateChecksum,omitempty"` +} + +// VersionRequirement specifies a minimum Zarf version needed and the reason for the requirement. +type VersionRequirement struct { + // The minimum version of Zarf required. + Version string `json:"version"` + // The reason this version is required. + Reason string `json:"reason"` +} + +// Values defines values files and schema for templating and overriding Helm values. +type Values struct { + // List of values file paths to include. + Files []string `json:"files,omitempty"` + // Path to a JSON schema file for validating values. + Schema string `json:"schema,omitempty"` +} + +// Component is the primary functional grouping of assets to deploy by Zarf. +type Component struct { + // The name of the component. + Name string `json:"name" jsonschema:"pattern=^[a-z0-9][a-z0-9\\-]*$"` + // Message to include during package deploy describing the purpose of this component. + Description string `json:"description,omitempty"` + // Do not install this component unless explicitly requested. Defaults to false, meaning the component is required. + Optional bool `json:"optional,omitempty"` + // Filter when this component is included in package creation or deployment. + Target ComponentTarget `json:"target,omitempty"` + // Import a component from another Zarf component config. + Import ComponentImport `json:"import,omitempty"` + // The Zarf CLI service this component provides, such as the registry, injector, or agent. + Service Service `json:"service,omitempty" jsonschema:"enum=registry,enum=seed-registry,enum=injector,enum=agent,enum=git-server"` + // Kubernetes manifests to be included in a generated Helm chart on package deploy. + Manifests []Manifest `json:"manifests,omitempty"` + // Helm charts to install during package deploy. + Charts []Chart `json:"charts,omitempty"` + // Files or folders to place on disk during package deployment. + Files []File `json:"files,omitempty"` + // List of OCI images to include in the package. + Images []Image `json:"images,omitempty"` + // List of tar archives of images to include in the package. + ImageArchives []ImageArchive `json:"imageArchives,omitempty"` + // List of git repositories to include in the package. + Repositories []string `json:"repositories,omitempty"` + // Custom commands to run at various stages of a package lifecycle. + Actions ComponentActions `json:"actions,omitempty"` +} + +// ComponentTarget filters a component to only apply for a given local OS, architecture, or flavor. +type ComponentTarget struct { + // Only deploy component to specified OS. + OS string `json:"os,omitempty" jsonschema:"enum=linux,enum=darwin,enum=windows"` + // Only include component for the given package architecture. + Architecture string `json:"architecture,omitempty" jsonschema:"enum=amd64,enum=arm64"` + // Only include this component when a matching '--flavor' is specified on 'zarf package create'. + Flavor string `json:"flavor,omitempty"` +} + +// ComponentImport is a reference to an imported Zarf component config. +type ComponentImport struct { + // Local file path references to component config files to import. + Local []ComponentImportLocal `json:"local,omitempty"` + // OCI URL references to remote component config files to import; pulled at create time. + Remote []ComponentImportRemote `json:"remote,omitempty"` +} + +// ComponentImportLocal is a local file path reference to a component config. +type ComponentImportLocal struct { + // The local file path to the component config. + Path string `json:"path"` +} + +// ComponentImportRemote is a remote OCI URL reference to a component config. +type ComponentImportRemote struct { + // The OCI URL of the remote component config. + URL string `json:"url"` +} + +// Service identifies which Zarf CLI service a component provides. +type Service string +const ( + ServiceRegistry Service = "registry" + ServiceSeedRegistry Service = "seed-registry" + ServiceInjector Service = "injector" + ServiceAgent Service = "agent" + ServiceGitServer Service = "git-server" +) + +// ServerSideApplyMode controls when server-side apply is used during deploy. +type ServerSideApplyMode string +const ( + ServerSideApplyEnabled ServerSideApplyMode = "true" + ServerSideApplyDisabled ServerSideApplyMode = "false" + ServerSideApplyAuto ServerSideApplyMode = "auto" +) + +// KustomizeManifest defines kustomization settings for a manifest. +type KustomizeManifest struct { + // List of local kustomization paths or remote URLs to include in the package. + Files []string `json:"files,omitempty"` + // Allow traversing directory above the current directory if needed for kustomization. + AllowAnyDirectory bool `json:"allowAnyDirectory,omitempty"` + // Enable kustomize plugins when building manifests. + EnablePlugins bool `json:"enablePlugins,omitempty"` +} + +// Manifest defines raw manifests Zarf will deploy as a helm chart. +type Manifest struct { + // A name to give this collection of manifests; this will become the name of the dynamically-created helm chart. + Name string `json:"name" jsonschema:"maxLength=40"` + // The namespace to deploy the manifests to. + Namespace string `json:"namespace,omitempty"` + // List of local K8s YAML files or remote URLs to deploy (in order). + Files []string `json:"files,omitempty"` + // Kustomize settings for this manifest. + Kustomize *KustomizeManifest `json:"kustomize,omitempty"` + // Whether to not wait for manifest resources to be ready before continuing. + SkipWait bool `json:"skipWait,omitempty"` + // Controls whether Server-Side Apply (SSA) or client-side apply (CSA) is used during deploy. + // - "true": always use SSA + // - "false": always use CSA + // - "auto": use SSA for fresh installs; for upgrades, match whichever strategy + // was used when the chart was first installed + // Defaults to "auto" when omitted. + ServerSideApply ServerSideApplyMode `json:"serverSideApply,omitempty"` + // EnableValues enables go-template processing on these manifests during deploy. + EnableValues bool `json:"enableValues,omitempty"` +} + +// Chart defines a helm chart to be deployed. +type Chart struct { + // The name of the chart within Zarf; note that this must be unique and does not need to be the same as the name in the chart repository. + Name string `json:"name"` + // The version of the chart. This field is removed from the schema, but kept as a backwards compatibility shim so v1alpha1 packages can be converted to v1beta1. + version string + // The Helm repository where the chart is stored. + HelmRepository *HelmRepositorySource `json:"helmRepository,omitempty"` + // The Git repository where the chart is stored. + Git *GitSource `json:"git,omitempty"` + // The local path where the chart is stored. + Local *LocalSource `json:"local,omitempty"` + // The OCI registry where the chart is stored. + OCI *OCISource `json:"oci,omitempty"` + // The namespace to deploy the chart to. + Namespace string `json:"namespace,omitempty"` + // The name of the Helm release to create (defaults to the Zarf name of the chart). + ReleaseName string `json:"releaseName,omitempty"` + // Whether to not wait for chart resources to be ready before continuing. + SkipWait bool `json:"skipWait,omitempty"` + // List of local values file paths or remote URLs to include in the package; these will be merged together when deployed. + ValuesFiles []string `json:"valuesFiles,omitempty"` + // List of value sources mapped to their Helm override targets. + Values []ChartValue `json:"values,omitempty"` + // Skip validation of the chart's values against its JSON schema. Defaults to false. + SkipSchemaValidation bool `json:"skipSchemaValidation,omitempty"` + // Controls whether Helm uses Server-Side Apply (SSA) or client-side apply (CSA) when deploying this chart. + // - "true": always use SSA + // - "false": always use CSA + // - "auto": use SSA for fresh installs; for upgrades, match whichever strategy + // was used when the chart was first installed + // Defaults to "auto" when omitted. + ServerSideApply ServerSideApplyMode `json:"serverSideApply,omitempty"` +} + +// ChartValue maps a values source path to a Helm chart target path. +type ChartValue struct { + // The source path for the value. + SourcePath string `json:"sourcePath"` + // The target path within the Helm chart values. + TargetPath string `json:"targetPath"` +} + +// HelmRepositorySource represents a Helm chart stored in a Helm repository. +type HelmRepositorySource struct { + // The name of a chart within a Helm repository. + Name string `json:"name,omitempty"` + // The URL of the chart repository where the Helm chart is stored. + URL string `json:"url"` + // The version of the chart in the Helm repository. + Version string `json:"version"` +} + +// GitSource represents a Helm chart stored in a Git repository. +type GitSource struct { + // The URL of the Git repository where the Helm chart is stored. + URL string `json:"url"` + // The subdirectory containing the chart within a Git repo. + Path string `json:"path,omitempty"` +} + +// LocalSource represents a Helm chart stored locally. +type LocalSource struct { + // The path to a local chart's folder or .tgz archive. + Path string `json:"path"` +} + +// OCISource represents a Helm chart stored in an OCI registry. +type OCISource struct { + // The URL of the OCI registry where the Helm chart is stored. + URL string `json:"url"` + // The version of the chart in the OCI registry. + Version string `json:"version"` +} + +// File defines a file to deploy. +type File struct { + // Local folder or file path or remote URL to pull into the package. + Source string `json:"source"` + // Optional checksum of the file in the format : (e.g. sha256:abc123). Defaults to sha256 if no algorithm is specified. + Checksum string `json:"checksum,omitempty"` + // The absolute or relative path where the file or folder should be copied to during package deploy. + Destination string `json:"destination"` + // Determines if the file should be made executable during package deploy. + Executable bool `json:"executable,omitempty"` + // List of symlinks to create during package deploy. + Symlinks []string `json:"symlinks,omitempty"` + // Local folder or file to be extracted from a 'source' archive. + ExtractPath string `json:"extractPath,omitempty"` + // EnableValues enables go-template processing on this file during deploy. + EnableValues bool `json:"enableValues,omitempty"` +} + +// Image defines an OCI image to include in the package. +type Image struct { + // The image reference. + Name string `json:"name"` + // The source to pull the image from. Defaults to "registry". + Source string `json:"source,omitempty" jsonschema:"enum=registry,enum=daemon"` +} + +// ImageArchive defines a tar archive of images to include in the package. +type ImageArchive struct { + // The path to the tar archive. + Path string `json:"path"` + // The list of images contained in the archive. + Images []string `json:"images"` +} + +// ComponentActions are ActionSets that map to different Zarf package operations. +type ComponentActions struct { + // Actions to run during package creation. + OnCreate ComponentActionSet `json:"onCreate,omitempty"` + // Actions to run during package deployment. + OnDeploy ComponentActionSet `json:"onDeploy,omitempty"` + // Actions to run during package removal. + OnRemove ComponentActionSet `json:"onRemove,omitempty"` +} + +// ComponentActionSet is a set of actions to run during a Zarf package operation. +type ComponentActionSet struct { + // Default configuration for all actions in this set. + Defaults ComponentActionDefaults `json:"defaults,omitempty"` + // Actions to run at the start of an operation. + Before []ComponentAction `json:"before,omitempty"` + // Actions to run at the end of an operation if it succeeds. + OnSuccess []ComponentAction `json:"onSuccess,omitempty"` + // Actions to run if any operation in this set fails. + OnFailure []ComponentAction `json:"onFailure,omitempty"` +} + +// ComponentActionDefaults sets the default configs for child actions. +type ComponentActionDefaults struct { + // Hide the output of commands during execution (default false). + Silent bool `json:"silent,omitempty"` + // Default timeout in seconds for commands (default to 0, no timeout). + MaxTotalSeconds int32 `json:"maxTotalSeconds,omitempty"` + // Retry commands a given number of times if they fail (default 0). + Retries int32 `json:"retries,omitempty"` + // Working directory for commands (default CWD). + Dir string `json:"dir,omitempty"` + // Additional environment variables for commands. + Env []string `json:"env,omitempty"` + // Indicates a preference for a shell for the provided cmd to be executed in on supported operating systems. + Shell Shell `json:"shell,omitempty"` +} + +// ComponentAction represents a single action to run during a Zarf package operation. +type ComponentAction struct { + // Hide the output of the command during package deployment (default false). + Silent *bool `json:"silent,omitempty"` + // Timeout in seconds for the command (default to 0, no timeout for cmd actions and 300, 5 minutes for wait actions). + MaxTotalSeconds *int32 `json:"maxTotalSeconds,omitempty"` + // Retry the command if it fails up to a given number of times (default 0). + Retries int32 `json:"retries,omitempty"` + // The working directory to run the command in (default is CWD). + Dir *string `json:"dir,omitempty"` + // Additional environment variables to set for the command. + Env []string `json:"env,omitempty"` + // The command to run. Must specify either cmd or wait for the action to do anything. + Cmd string `json:"cmd,omitempty"` + // Indicates a preference for a shell for the provided cmd. + Shell *Shell `json:"shell,omitempty"` + // An array of values to set with the output of the command. + SetValues []SetValue `json:"setValues,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. + Wait *ComponentActionWait `json:"wait,omitempty"` + // EnableValues enables go-template processing on the cmd field. + EnableValues bool `json:"enableValues,omitempty"` +} + +// SetValueType declares the expected input back from the cmd, allowing structured data to be parsed. +type SetValueType string + +const ( + // SetValueYAML enables YAML parsing. + SetValueYAML SetValueType = "yaml" + // SetValueJSON enables JSON parsing. + SetValueJSON SetValueType = "json" + // SetValueString sets the raw value. + SetValueString SetValueType = "string" +) + +// SetValue declares a value that can be set during a package deploy. +type SetValue struct { + // Key represents which value to assign to. + Key string `json:"key,omitempty"` + // Value is the current value at the key. + Value any `json:"value,omitempty"` + // Type declares the kind of data being stored in the value. + Type SetValueType `json:"type,omitempty"` +} + +// ComponentActionWait specifies a condition to wait for before continuing. +type ComponentActionWait struct { + // Wait for a condition to be met in the cluster before continuing. Only one of cluster or network can be specified. + Cluster *ComponentActionWaitCluster `json:"cluster,omitempty"` + // Wait for a condition to be met on the network before continuing. Only one of cluster or network can be specified. + Network *ComponentActionWaitNetwork `json:"network,omitempty"` +} + +// ComponentActionWaitCluster specifies a cluster-level condition to wait for. +type ComponentActionWaitCluster struct { + // The kind of resource to wait for. + Kind string `json:"kind" jsonschema:"example=Pod,example=Deployment"` + // The name of the resource or selector to wait for. + Name string `json:"name" jsonschema:"example=podinfo,example=app=podinfo"` + // The namespace of the resource to wait for. + Namespace string `json:"namespace,omitempty"` + // The condition or jsonpath state to wait for; defaults to kstatus readiness checks. + Condition string `json:"condition,omitempty" jsonschema:"example=Available,'{.status.availableReplicas}'=23"` +} + +// ComponentActionWaitNetwork specifies a network-level condition to wait for. +type ComponentActionWaitNetwork struct { + // The protocol to wait for. + Protocol string `json:"protocol" jsonschema:"enum=tcp,enum=http,enum=https"` + // The address to wait for. + Address string `json:"address" jsonschema:"example=localhost:8080,example=1.1.1.1"` + // The HTTP status code to wait for if using http or https. + Code int32 `json:"code,omitempty" jsonschema:"example=200,example=404"` +} + +// Shell represents the desired shell to use for a given command. +type Shell struct { + // Windows shell preference. + Windows string `json:"windows,omitempty" jsonschema:"example=powershell,example=cmd,example=pwsh"` + // Linux shell preference. + Linux string `json:"linux,omitempty" jsonschema:"example=sh,example=bash,example=zsh"` + // Darwin (macOS) shell preference. + Darwin string `json:"darwin,omitempty" jsonschema:"example=sh,example=bash,example=zsh"` +} diff --git a/0051-v1beta1-schema/zarf.yaml b/0051-v1beta1-schema/zarf.yaml new file mode 100644 index 0000000..6422510 --- /dev/null +++ b/0051-v1beta1-schema/zarf.yaml @@ -0,0 +1,379 @@ +kind: ZarfPackageConfig +apiVersion: zarf.dev/v1beta1 +metadata: + name: everything-zarf-package + description: A zarf package with a non empty value for every key - this demonstrates the changes proposed by ZEP-0051 + version: v1.0.0 + uncompressed: false + architecture: amd64 + annotations: # Most of these annotations are v1alpha1 fields that were removed in favor of a choose your own adventure map + authors: cool-kidz + documentation: https://my-package-documentation.com + source: https://my-git-server/my-package + url: https://my-package-website.com + vendor: my-vendor + image: https://my-image-url-to-use-in-deprecated-zarf-ui + anyway-you-want-it: thats-the-way-you-need-it + preventNamespaceOverride: false +build: # Everything here is created by Zarf not by users + hostname: my-computer + user: my-user + architecture: amd64 + timestamp: 2021-09-01T00:00:00Z + version: v1.0.0 + migrations: + - scripts-to-actions + registryOverrides: + gcr.io: my-gcr.com + differential: true + flavor: cool-flavor + versionRequirements: + - version: v0.68.0 + reason: This package contains image archives which will only be recognized on v0.68.0+ + aggregateChecksum: shasum # this is moved from .metadata +values: + files: + - values.yaml + schema: values-schema.json +documentation: + my-file: "file.md" +components: +- name: a-component + description: Zarf description + optional: false # Component is required (default behavior) + target: + os: darwin + architecture: amd64 + flavor: a-flavor # this will only be used when there are multiple components + import: + local: + - path: ./components/a-component.yaml + remote: + - url: oci://ghcr.io/my-org/components/a-component:1.0.0 + service: registry # one of: registry, seed-registry, injector, agent, git-server + manifests: + - name: manifest + namespace: manifest-ns + files: + - a-file.yaml + kustomize: + files: + - a-kustomization.yaml + allowAnyDirectory: false + enablePlugins: true + skipWait: true + serverSideApply: "auto" + enableValues: true # Enable go-template processing + charts: + - name: chart + namespace: chart-ns + releaseName: chart-release + skipWait: false + valuesFiles: + - values.yaml + values: + - sourcePath: ".app.name" + targetPath: ".appName" + skipSchemaValidation: false + serverSideApply: "auto" + helmRepository: # Only one of helmRepository, git, oci, or local is allowed + url: https://stefanprodan.github.io/podinfo + name: podinfo # replaces repoName since it's only applicable in this situation + version: 6.4.0 + git: + url: https://github.com/stefanprodan/podinfo.git@6.4.0 + path: charts/podinfo + oci: + url: oci://ghcr.io/stefanprodan/charts/podinfo + version: 6.4.0 + local: + path: chart + files: + - source: source-file.txt + destination: target-file.txt + checksum: sha256:abc123 + executable: false + symlinks: + - /path/to/symlink + extractPath: /path/to/extract + enableValues: false # Disable go-template processing for this file + images: + - name: podinfo:v1 + source: "registry" # optional - registry is the default value + imageArchives: + - path: podinfo.tar + images: + - podinfo:local + repositories: + - https://github.com/zarf-dev/zarf + actions: + onCreate: + defaults: + silent: true + maxTotalSeconds: 60 + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + shell: + darwin: sh + linux: sh + windows: powershell + before: + - silent: false + maxTotalSeconds: 60 + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + enableValues: true + shell: + darwin: sh + linux: sh + windows: powershell + setValues: + - key: .json + description: action-description + wait: + cluster: # Only one of cluster / network can be used + kind: pod + name: my-pod + namespace: pod-ns + condition: ready + network: + protocol: http + address: github.com + code: 200 + onSuccess: + - silent: false + maxTotalSeconds: 60 + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + enableValues: true + shell: + darwin: sh + linux: sh + windows: powershell + setValues: + - key: .json + description: action-description + wait: + cluster: # Only one of cluster / network can be used + kind: pod + name: my-pod + namespace: pod-ns + condition: ready + network: + protocol: http + address: github.com + code: 200 + onFailure: + - silent: false + maxTotalSeconds: 60 + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + enableValues: true + shell: + darwin: sh + linux: sh + windows: powershell + setValues: + - key: .json + description: action-description + wait: + cluster: # Only one of cluster / network can be used + kind: pod + name: my-pod + namespace: pod-ns + condition: ready + network: + protocol: http + address: github.com + code: 200 + onDeploy: + defaults: + silent: true + maxTotalSeconds: 60 + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + shell: + darwin: sh + linux: sh + windows: powershell + before: + - silent: false + maxTotalSeconds: 60 + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + enableValues: true + shell: + darwin: sh + linux: sh + windows: powershell + setValues: + - key: .json + description: action-description + wait: + cluster: # Only one of cluster / network can be used + kind: pod + name: my-pod + namespace: pod-ns + condition: ready + network: + protocol: http + address: github.com + code: 200 + onSuccess: + - silent: false + maxTotalSeconds: 60 + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + enableValues: true + shell: + darwin: sh + linux: sh + windows: powershell + setValues: + - key: .json + description: action-description + wait: + cluster: # Only one of cluster / network can be used + kind: pod + name: my-pod + namespace: pod-ns + condition: ready + network: + protocol: http + address: github.com + code: 200 + onFailure: + - silent: false + maxTotalSeconds: 60 + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + enableValues: true + shell: + darwin: sh + linux: sh + windows: powershell + setValues: + - key: .json + description: action-description + wait: + cluster: # Only one of cluster / network can be used + kind: pod + name: my-pod + namespace: pod-ns + condition: ready + network: + protocol: http + address: github.com + code: 200 + onRemove: + defaults: + silent: true + maxTotalSeconds: 60 + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + shell: + darwin: sh + linux: sh + windows: powershell + before: + - silent: false + maxTotalSeconds: 60 + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + enableValues: true + shell: + darwin: sh + linux: sh + windows: powershell + setValues: + - key: .json + description: action-description + wait: + cluster: # Only one of cluster / network can be used + kind: pod + name: my-pod + namespace: pod-ns + condition: ready + network: + protocol: http + address: github.com + code: 200 + onSuccess: + - silent: false + maxTotalSeconds: 60 + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + enableValues: true + shell: + darwin: sh + linux: sh + windows: powershell + setValues: + - key: .json + description: action-description + wait: + cluster: # Only one of cluster / network can be used + kind: pod + name: my-pod + namespace: pod-ns + condition: ready + network: + protocol: http + address: github.com + code: 200 + onFailure: + - silent: false + maxTotalSeconds: 60 + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + enableValues: true + shell: + darwin: sh + linux: sh + windows: powershell + setValues: + - key: .json + description: action-description + wait: + cluster: # Only one of cluster / network can be used + kind: pod + name: my-pod + namespace: pod-ns + condition: ready + network: + protocol: http + address: github.com + code: 200 diff --git a/0051-v1beta1-schema/zep.yaml b/0051-v1beta1-schema/zep.yaml new file mode 100644 index 0000000..d598ef7 --- /dev/null +++ b/0051-v1beta1-schema/zep.yaml @@ -0,0 +1,27 @@ +schema-version: 1.0.0 + +title: v1beta1 schema +zep-number: 0051 +authors: + - "@austinabro321" +status: implementable +creation-date: 2025-10-21 +reviewers: + - "@zarf-dev" +approvers: + - "@zarf-dev" + +see-also: + - "/0048-schema-upgrade-process" + +# The target maturity stage in the current dev cycle for this ZEP. +stage: "" + +# The most recent milestone for which work toward delivery of this ZEP has been +# done. This can be the current (upcoming) milestone, if it is being actively +# worked on. +latest-milestone: "" + +# The milestone at which this feature was, or is targeted to be, at each stage. +milestone: + stable: "TBD""