diff --git a/.github/workflows/publish-application-packages.yml b/.github/workflows/publish-application-packages.yml index 3944aa0abb..680fd2eee4 100644 --- a/.github/workflows/publish-application-packages.yml +++ b/.github/workflows/publish-application-packages.yml @@ -5,22 +5,24 @@ permissions: on: workflow_dispatch: - inputs: - branchName: - description: "Branch to build the packages from" - required: true - default: "main" jobs: publish-packages: runs-on: ubuntu-latest permissions: + id-token: write packages: write steps: - name: "Checkout Repo" uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Auth with AWS + uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2 with: - ref: ${{ github.event.inputs.branchName }} + role-to-assume: ${{ secrets.AWS_KMS_ROLE }} + role-session-name: ${{ github.job || github.event.client_payload.pull_request.head.sha || github.sha }} + aws-region: us-east-2 + role-duration-seconds: 3600 - name: Install The Latest Release Version of Zarf uses: defenseunicorns/setup-zarf@10e539efed02f75ec39eb8823e22a5c795f492ae #v1.0.1 @@ -39,12 +41,8 @@ jobs: zarf package create -o build -a arm64 examples/dos-games --signing-key=awskms:///${{ secrets.COSIGN_AWS_KMS_KEY }} --confirm # Publish a the signed dos-games package - zarf package publish ./build/zarf-package-dos-games-amd64-1.0.0.tar.zst oci://ghcr.io/zarf-dev/packages --key=https://zarf.dev/cosign.pub - zarf package publish ./build/zarf-package-dos-games-arm64-1.0.0.tar.zst oci://ghcr.io/zarf-dev/packages --key=https://zarf.dev/cosign.pub + zarf package publish ./build/zarf-package-dos-games-amd64-1.1.0.tar.zst oci://ghcr.io/zarf-dev/packages --key=https://zarf.dev/cosign.pub + zarf package publish ./build/zarf-package-dos-games-arm64-1.1.0.tar.zst oci://ghcr.io/zarf-dev/packages --key=https://zarf.dev/cosign.pub # Publish a skeleton of the dos-games package zarf package publish examples/dos-games oci://ghcr.io/zarf-dev/packages - env: - AWS_REGION: ${{ secrets.COSIGN_AWS_REGION }} - AWS_ACCESS_KEY_ID: ${{ secrets.COSIGN_AWS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.COSIGN_AWS_ACCESS_KEY }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 52b3d38b31..dd5943aeb6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,6 +12,7 @@ jobs: build-release: runs-on: ubuntu-latest permissions: + id-token: write packages: write steps: # Checkout the repo and setup the tooling for this job @@ -53,13 +54,18 @@ jobs: rm build/zarf-linux-arm64 echo ZARF_AGENT_IMAGE_DIGEST=$(docker buildx imagetools inspect ghcr.io/zarf-dev/zarf/agent:$GITHUB_REF_NAME --format '{{ json . }}' | jq -r .manifest.digest) >> $GITHUB_ENV + - name: Auth with AWS + uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2 + with: + role-to-assume: ${{ secrets.AWS_KMS_ROLE }} + role-session-name: ${{ github.job || github.event.client_payload.pull_request.head.sha || github.sha }} + aws-region: us-east-2 + role-duration-seconds: 3600 + - name: "Zarf Agent: Sign the Image" run: cosign sign --key awskms:///${{ secrets.COSIGN_AWS_KMS_KEY }} -a release-engineer=https://github.com/${{ github.actor }} -a version=$GITHUB_REF_NAME ghcr.io/zarf-dev/zarf/agent@$ZARF_AGENT_IMAGE_DIGEST -y env: COSIGN_EXPERIMENTAL: 1 - AWS_REGION: ${{ secrets.COSIGN_AWS_REGION }} - AWS_ACCESS_KEY_ID: ${{ secrets.COSIGN_AWS_KEY_ID }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.COSIGN_AWS_ACCESS_KEY }} # Builds init packages since GoReleaser won't handle this for us - name: Build init-packages For Release diff --git a/.github/workflows/scan-codeql.yml b/.github/workflows/scan-codeql.yml index 3c7bff10ec..d1815026c9 100644 --- a/.github/workflows/scan-codeql.yml +++ b/.github/workflows/scan-codeql.yml @@ -53,7 +53,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@29d86d22a34ea372b1bbf3b2dced2e25ca6b3384 # v3.26.1 + uses: github/codeql-action/init@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: languages: ${{ matrix.language }} config-file: ./.github/codeql.yaml @@ -62,6 +62,6 @@ jobs: run: make build-cli-linux-amd - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@29d86d22a34ea372b1bbf3b2dced2e25ca6b3384 # v3.26.1 + uses: github/codeql-action/analyze@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml index 9ece5ff012..16bd6597df 100644 --- a/.github/workflows/scorecard.yaml +++ b/.github/workflows/scorecard.yaml @@ -44,6 +44,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@29d86d22a34ea372b1bbf3b2dced2e25ca6b3384 # v3.26.1 + uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: sarif_file: results.sarif diff --git a/.github/workflows/test-import.yaml b/.github/workflows/test-import.yaml index 72ad5e770f..c5571593fe 100644 --- a/.github/workflows/test-import.yaml +++ b/.github/workflows/test-import.yaml @@ -18,7 +18,7 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 - name: Setup Go - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 with: go-version-file: 'go.mod' cache: true @@ -28,7 +28,7 @@ jobs: cd $(mktemp -d) echo "$GO_MAIN" > main.go go mod init github.com/zarf-dev/test-import - go mod edit -replace github.com/zarf-dev/zarf=github.com/${{ github.repository }}@${COMMIT_SHA:0:12} + go mod edit -replace github.com/zarf-dev/zarf=github.com/${{ github.event.pull_request.head.repo.full_name }}@${COMMIT_SHA:0:12} go mod tidy cat go.mod | grep -q ${COMMIT_SHA:0:12} go run main.go diff --git a/.golangci.yaml b/.golangci.yaml index 6f46337df5..6423df7732 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,5 +1,5 @@ run: - timeout: 5m + timeout: 10m linters: disable-all: true enable: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ad9ea6d447..22ea750052 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -40,15 +40,3 @@ repos: types: [go] language: golang pass_filenames: false - - repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.14.0 - hooks: - - id: check-jsonschema - name: "Validate Zarf Configs Against Schema" - files: "zarf.yaml" - types: [yaml] - args: ["--schemafile", "zarf.schema.json"] - exclude: | - (?x)^( - src/test/packages/12-lint/.* - )$ diff --git a/adr/0026-schema.md b/adr/0026-schema.md new file mode 100644 index 0000000000..d62d1d4041 --- /dev/null +++ b/adr/0026-schema.md @@ -0,0 +1,100 @@ +# 25. Zarf Schema for v1 + +Date: 2024-06-07 + +## Status + +Proposed + +## Terms +v0 = any version of Zarf prior to v1 + +## Context + +Zarf currently does not have explicit schema versions. Any schema changes are embedded into Zarf and can change with new versions. There are several examples of deprecated keys throughout Zarf's lifespan such as: + +- `setVariable` deprecated in favor of `setVariables` +- `scripts` deprecated in favor of `actions` +- `group` deprecated in favor of `flavor`, however these work functionally different and cannot be automatically migrated +- `cosignKeyPath` deprecated in favor of specifying the key path at create time + +Zarf has not disabled any deprecated keys thus far. On create the user is always warned when using a deprecated field, however the field still exists in the schema and functions properly. Some of the deprecated keys can be migrated automatically as the old item is a subset or directly related to it's replacement. For example, setVariable is automatically migrated to a single item list of setVariables. This migration occurs on the zarf.yaml fed into the package during package create, however the original field is not deleted from the packaged zarf.yaml because the Zarf binary used in the airgap or delivery environment is not assumed to have the new schema fields that the deprecated key was migrated to. + +The release of v1 will provide an opportunity to delete deprecated features that Zarf has warned will be dropped in v1. + +Creating a v1 schema will allow Zarf to establish a contract with it's user base that features will be supported long term. When a feature is deprecated in the v1 schema, it will remain usable in the schema for the lifetime of v1. + +## Decision + +Zarf will begin having proper schema versions. A top level key, `apiVersion`, will be introduced to allow users to specify the schema. At the release of v1 the only valid user input for `apiVersion` will be v1. Zarf will not allow users to build using the v0 schema. `zarf package create` will fail if the user has deprecated keys or if `apiVersion` is missing and the user will be instructed to run the new `zarf dev migrate-schema` command. `zarf dev migrate-schema` will automatically migrate deprecated fields in the users `zarf.yaml` where possible. It will also add the apiVersion key and set it to v1. + +The existing go types which comprise the Zarf schema will be moved to types/alpha and will never change. An updated copy of these types without the deprecated fields will be created in a package types/v1 and any future schema changes will affect these objects. Internally, Zarf will introduce translation functions which will take the alpha schema and return the v1 schema. From that point on, all function signatures that have a struct that is included in the Zarf schema will change from `types.structName` to `v1.structName`. + +All deprecated features will cause an error on create. Deprecated features with a direct migration path will still be deployed if the package was created with schema v1, as migrations will add the non deprecated fields. If a feature does not have a direct automatic migration path (cosignKeyPath & groups) the package will fail on deploy. This will happen until the alpha schema is entirely removed from Zarf, which will happen one year after v1 is released. + +At create time Zarf will package both a `zarf.yaml` and a `zarfv1.yaml`. If a `zarfv1.yaml` exists Zarf will use that. If a `zarfv1.yaml` does not exist, then Zarf will know that the package was created prior to v1 and use the regular `zarf.yaml`. If the package is deployed with v0 it will read the `zarf.yaml` as normal even if the package has a `zarfv1.yaml`. This will make it simpler to drop deprecated items with migration paths from the v1 schema while remaining backwards compatible as those deprecated items will exist in the `zarf.yaml` + +When Zarf introduces new keys that they are not ready to promise long term support for they will mark them as experimental in the schema. A key is assumed to be stable if it's not experimental or deprecated. + +There are several other keys Zarf will deprecate which will have automated migrations to new fields +- `.metadata.aggregateChecksum` -> `.build.aggregateChecksum` +- `.components[x].required` -> `.components[x].optional`. Optional will default to false. This is a change in behavior as currently `required` defaults to true. However, automatic migration will work since we can set optional to true on any components which do not already have required. +- Metadata fields `image`, `source`, `documentation`, `url`, `authors`, `vendors` -> will become a map of `annotations` +- `noWait` -> `wait` which will default to true. This change will happen on both `.components.[x].manifests` and `components.[x].charts` +- `yolo` -> `airgap` which will default to true +- `.components.[x].actions.[default/onAny].maxRetries` -> `.components.[x].actions.[default/onAny].retries` +- `.components.[x].actions.[default/onAny].maxTotalSeconds` -> `.components.[x].actions.[default/onAny].timeout`, which must be in a [Go recognized duration string format](https://pkg.go.dev/time#ParseDuration) +- charts will change to avoid [current confusion with keys](https://github.com/defenseunicorns/zarf/issues/2245). Exactly one of the following fields will exist for each `components.[x].charts`. +```yaml + helm: + url: https://stefanprodan.github.io/podinfo + RepoName: podinfo # replaces repoName since it's only applicable for helm repos + + git: + url: https://stefanprodan.github.io/podinfo + path: charts/podinfo + + oci: + url: oci://ghcr.io/stefanprodan/charts/podinfo + + local: + path: chart +``` +- A new field called `.components.[x].healthChecks` will be introduced and will utilize [kstatus](https://github.com/kubernetes-sigs/cli-utils/blob/master/pkg/kstatus/README.md) to wait for resources to be ready after deployment. Read [#2718](https://github.com/zarf-dev/zarf/issues/2718) for more detail. +- a new field `.components.[x].wait` will be introduced. It will default to true, and automatically run `healthchecks` for every object in the cluster. +- `.components.[x].extensions` will be removed in favor of `zarf dev generate bigbang`. See #2875 + +### BDD scenarios +The following are [behavior driven development](https://en.wikipedia.org/wiki/Behavior-driven_development) scenarios to provide context of what Zarf will do in specific situations given the above decisions. + +#### v1 create with deprecated keys +- *Given* Zarf version is v1 +- *and* the `zarf.yaml` has no apiVersion or deprecated keys +- *when* the user runs `zarf package create` +- *then* they will receive an error and be told to run `zarf dev migrate-schema` or how to migrate off cosign key paths or how to use flavors over groups depending on the error + +#### v0 create -> v1 deploy +- *Given*: A package is created with Zarf v0 +- *and* that package has deprecated keys that can be automatically migrated (required, scripts, & set variables) +- *when* the package is deployed with Zarf v1 +- *then* the keys will be automatically migrated & the package will be deployed without error. + +#### v0 create with removed feature -> v1 deploy +- *Given*: A package is created with Zarf v0 +- *and* that package has deprecated keys that cannot be automatically migrated (groups, cosignKeyPath) +- *when* the package is deployed with Zarf v1 +- *then* then deploy of that package will fail and the user will be instructed to update their package + +#### v1 create -> v0 deploy +- *Given*: A package is created with Zarf v1 +- *and* that package uses keys that don't exist in v0 +- *when* the package is deployed with Zarf v0 +- *then* Zarf v0 will deploy the package without issues. Additionally if the users is on a v0 version of Zarf after issue [2728](https://github.com/defenseunicorns/zarf/issues/2728) has been implemented, if there are fields unrecognized by the v0 schema, then the user will be warned they are deploying a package that has features that do not exist in the current version of Zarf. + +## Consequences +- As long as the only deprecated features in a package have a migration path, and the package was built after the feature was deprecated so migrations were run, Zarf will be successful in both creating a package with v1 and deploying with v0, and creating a package with v0 and deploying with v1. +- Users of deprecated `group`, `cosignKeyPath`, and `action.Wait` keys outside of `onDeploy` might be frustrated if their packages, created v0, error out on Zarf v1, however this is preferable to unexpected behavior occurring in the cluster. +- Users may be frustrated that they have to run `zarf dev migrate-schema` to edit their `zarf.yaml` to remove the deprecated fields and add `apiVersion`. +- The Zarf codebase will contain two Zarf package objects, v1 and v0. Many fields on these objects will be unchanged across v0 and v1, however, v0 will not include new fields, and v1 will exclude deprecated fields. This approach is similar to the strategies used by [Kubernetes](https://github.com/kubernetes/api/tree/master/storage) and [flux](https://github.com/fluxcd/source-controller/tree/main/api) + +Look at [0026-schema.yaml](0026-schema.yaml) to see an example v1 zarf.yaml with, somewhat, reasonable & nonempty values for every key. diff --git a/adr/0026-schema.yaml b/adr/0026-schema.yaml new file mode 100644 index 0000000000..4240d79621 --- /dev/null +++ b/adr/0026-schema.yaml @@ -0,0 +1,507 @@ +kind: ZarfPackageConfig +apiVersion: v1 +metadata: + name: everything-zarf-package + description: A zarf package with a non empty value for every key - this is an invalid package and is here to exhibit the proposed v1 schema by 0026-schema.md + version: v1.0.0 + uncompressed: true + architecture: amd64 + airgap: true # changed from yolo + annotations: # All of these are v0 fields that will be deprecated in favor of a choose your own adventure label 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 +build: # Everything here is created by Zarf not be users + terminal: 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 + differentialPackageVersion: "v0.99.9" + differentialMissing: + - missing-component + flavor: cool-flavor + lastNonBreakingVersion: "v0.99.9" + aggregateChecksum: shasum # this is moved from .metadata +components: +- name: a-component + description: Zarf description + default: false # Austin to check if we remove this + only: + localOS: darwin + cluster: + architecture: amd64 + distros: + - ubuntu + flavor: a-flavor # this will only be used when there are multiple components + import: + name: other-component-name + path: ABCD # Only path or URL will be used, not both + url: oci:// + manifests: + - name: manifest + namespace: manifest-ns + files: + - a-file.yaml + kustomizeAllowAnyDirectory: false + kustomizations: + - a-kustomization.yaml + wait: false + charts: + - name: chart + namespace: chart-ns + version: v1.0.0 + releaseName: chart-release + wait: true + valuesFiles: + - values.yaml + variables: + - name: REPLICA_COUNT + description: "Override the number of pod replicas" + path: replicaCount + # Everything below this line is changing https://github.com/defenseunicorns/zarf/issues/2245 + helm: # Only one of helm, git, oci, url, or local is allowed + url: https://stefanprodan.github.io/podinfo + name: podinfo # replaces repoName since it's only applicable in this situation + git: + url: https://stefanprodan.github.io/podinfo + path: charts/podinfo + oci: + url: oci://ghcr.io/stefanprodan/charts/podinfo + local: + path: chart + dataInjections: + - source: zim-data + target: + namespace: my-namespace + selector: app=my-app + container: data-loader + path: /data + compress: true + files: + - source: source-file.txt + target: target-file.txt + shasum: shasum + executable: false + symlinks: + - /path/to/symlink + extractPath: /path/to/extract + images: + - podinfo@v1 + repos: + - https://github.com/defenseunicorns/zarf + wait: true + healthChecks: + - apiVersion: v1 + kind: pod + name: my-pod + namespace: my-namespace + actions: + onCreate: + defaults: + mute: true + timeout: 1m + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + shell: + darwin: sh + linux: sh + windows: powershell + before: + - mute: false + timeout: 1m + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + shell: + darwin: sh + linux: sh + windows: powershell + setVariables: + - name: VAR + sensitive: false + autoIndent: true + pattern: ".+" + type: raw + 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 + after: + - mute: false + timeout: 1m + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + shell: + darwin: sh + linux: sh + windows: powershell + setVariables: + - name: VAR + sensitive: false + autoIndent: true + pattern: ".+" + type: raw + 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: + - mute: false + timeout: 1m + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + shell: + darwin: sh + linux: sh + windows: powershell + setVariables: + - name: VAR + sensitive: false + autoIndent: true + pattern: ".+" + type: raw + 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: + - mute: false + timeout: 1m + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + shell: + darwin: sh + linux: sh + windows: powershell + setVariables: + - name: VAR + sensitive: false + autoIndent: true + pattern: ".+" + type: raw + 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: + mute: true + timeout: 1m + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + shell: + darwin: sh + linux: sh + windows: powershell + before: + - mute: false + timeout: 1m + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + shell: + darwin: sh + linux: sh + windows: powershell + setVariables: + - name: VAR + sensitive: false + autoIndent: true + pattern: ".+" + type: raw + 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 + after: + - mute: false + timeout: 1m + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + shell: + darwin: sh + linux: sh + windows: powershell + setVariables: + - name: VAR + sensitive: false + autoIndent: true + pattern: ".+" + type: raw + 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: + - mute: false + timeout: 1m + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + shell: + darwin: sh + linux: sh + windows: powershell + setVariables: + - name: VAR + sensitive: false + autoIndent: true + pattern: ".+" + type: raw + 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: + - mute: false + timeout: 1m + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + shell: + darwin: sh + linux: sh + windows: powershell + setVariables: + - name: VAR + sensitive: false + autoIndent: true + pattern: ".+" + type: raw + 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: + mute: true + timeout: 1m + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + shell: + darwin: sh + linux: sh + windows: powershell + before: + - mute: false + timeout: 1m + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + shell: + darwin: sh + linux: sh + windows: powershell + setVariables: + - name: VAR + sensitive: false + autoIndent: true + pattern: ".+" + type: raw + 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 + after: + - mute: false + timeout: 1m + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + shell: + darwin: sh + linux: sh + windows: powershell + setVariables: + - name: VAR + sensitive: false + autoIndent: true + pattern: ".+" + type: raw + 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: + - mute: false + timeout: 1m + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + shell: + darwin: sh + linux: sh + windows: powershell + setVariables: + - name: VAR + sensitive: false + autoIndent: true + pattern: ".+" + type: raw + 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: + - mute: false + timeout: 1m + retries: 0 + dir: dir + env: + - ENV_VAR=FOO + cmd: echo hello + shell: + darwin: sh + linux: sh + windows: powershell + setVariables: + - name: VAR + sensitive: false + autoIndent: true + pattern: ".+" + type: raw + 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 +constants: +- name: CONSTANT + value: constant-value + description: constant-value + autoIndent: false + pattern: ".+" +variables: +- name: VAR + sensitive: false + autoIndent: true + pattern: ".+" + type: raw + description: var + default: whatever + prompt: false diff --git a/cosign.pub b/cosign.pub index 6c8e8e4eb5..a2677f32b0 100644 --- a/cosign.pub +++ b/cosign.pub @@ -1,14 +1,14 @@ -----BEGIN PUBLIC KEY----- -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9u472y/wY0tjIiR2T6rY -zOG1q4qwx5ZdmnoGsiG0Zc3rYo2DMiuKciG0MI4opCf4IID7kfYOD4aWILymwFID -xW0L6pEbxknHRQacWZSf/qfA+aAcjbKOY3ZWU8/uLJJeq37Y4OLc17ThJ7ZOj1Yf -Uvj81Uz9ZWVW7kYY31vWCruJh4VxZLsUAmFc6CsQUtzSGordLhh1b1rDP6ZRAaIP -mQnniULogwIBqnUTkIVwxiRYG+V2a3IC5vqlBLQRQ3UOWQ9mgZcfcXuTA6Fh8bwO -2lG768UfI1RBYioXAgXbPwXK+kM3Idvjcr+X2F3VpYWhHTscMIQF0ERzK7BkRqRI -x9l/RRm5lP+9a1kt6giYtvX2OqEsWaG3lTen3ocwblaHRlmqnaiVBtAnVny6QDHX -9p1HPMD/NjWjZucxWMjtdL5FZxBywbJVlxhe7sFByMoBZYhea9vGGSn2M2Q9kPiq -Bgl6bKZdeYIhaKQ7wrNkS6YVHMIqqpCIUI6/YGYwnu0hodbjR0yA2LFx4TgFZAuY -uGEiRP4Oi7WEOPkjRjP7kPXGpEBB7ulZ/Wohq1B6pB1Odo8WlfJRAek319F2aqqh -J1c3YdZ/w3EvCLKd+Inp1UNbamb79UN6jtwhqwKw72YbZh/yP0rim49lQ++umwPX -JWqG8iY/UzGB/3ch4/Wb09UCAwEAAQ== ------END PUBLIC KEY----- \ No newline at end of file +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAr6pqXju3qrkVae35GIuG +F58+zMd5XGMVgPkxFrdrJZ/3Ag65y7j4QsrcCFkXYAYNIy9iZliXypsxrr3oajJv +EgLDAc0CqtWYa0tuT2kAP4YHzxVkLC8MZLhQ1fuj9QKylm3OIMf18ZAnp12upmK8 +SBvrYxtWfTOv4KBgRGdIO0U9M/dwNnodGosY0znyHD9dp1G7qRA7BNpOsuXoaLa/ +aSQ2X0icoq5N8BLLOl3/23w6nCV+G32HFD0/AurDZVMC8o6N91AkX3smfWINkNk+ +QUrCkjhlAMxtBPi2TCYB4PimOKLpO/q/hwfixkHJcx8zPY/UZCCJGrsOcdFdvN/M +FkxqVZ2vBv+8LaElSAmbzsjVpg4w3QMk/6fVuU2rBtwog7DekuV/J5SwGCyTfC/4 +R8SetTsEpYgtDWp8+vugcfZTg5+7rPnMfNG16HdwJoC+LnWbeot6X2ZepTu4CrkV +qCAfFlu9G9sy2ZrwT5gnFT9JoKPVRTgkYmADgSfF0njKjuFKfk+aEVIrKRCVbExe +VtfmM1A9OfP4vCtCKw7tE5fFhmAa5v2D6LS/rG2m99fbZjDdeK9y22OZZyUCZaUN +TM+VQTuY1bwXY0/XEhUHxP0Fzk2VGQVslwXgW305SzR8Yh/bTbE4pkNGpOta+4s2 +E5ZMlZgQX8x4gSfbxmBHgP0CAwEAAQ== +-----END PUBLIC KEY----- diff --git a/examples/composable-packages/zarf.yaml b/examples/composable-packages/zarf.yaml index 35dab0b33e..e1cca3388d 100644 --- a/examples/composable-packages/zarf.yaml +++ b/examples/composable-packages/zarf.yaml @@ -31,7 +31,7 @@ components: # default: false # the initial value overrides the child component import: # The URL to the skeleton package containing this component's package definition - url: oci://🦄/dos-games:1.0.0 + url: oci://ghcr.io/zarf-dev/packages/dos-games:1.1.0 # Example optional custom name to point to in the imported package (default is to use this component's name) name: baseline # Un'name'd Zarf primitives will be appended to the end of the primitive's list for that component. diff --git a/examples/dos-games/zarf.yaml b/examples/dos-games/zarf.yaml index 49d49f5be8..87042ca58d 100644 --- a/examples/dos-games/zarf.yaml +++ b/examples/dos-games/zarf.yaml @@ -2,7 +2,7 @@ kind: ZarfPackageConfig metadata: name: dos-games description: Simple example to load classic DOS games into K8s in the airgap - version: 1.0.0 + version: 1.1.0 components: - name: baseline diff --git a/packages/zarf-registry/zarf.yaml b/packages/zarf-registry/zarf.yaml index 2da03ccda6..190eeba4c0 100644 --- a/packages/zarf-registry/zarf.yaml +++ b/packages/zarf-registry/zarf.yaml @@ -111,7 +111,7 @@ components: architecture: amd64 files: # Rust Injector Binary - - source: https://zarf-public.s3-us-gov-west-1.amazonaws.com/injector/###ZARF_PKG_TMPL_INJECTOR_VERSION###/zarf-injector-amd64 + - source: https://zarf-init.s3.us-east-2.amazonaws.com/injector/###ZARF_PKG_TMPL_INJECTOR_VERSION###/zarf-injector-amd64 target: "###ZARF_TEMP###/zarf-injector" shasum: "###ZARF_PKG_TMPL_INJECTOR_AMD64_SHASUM###" executable: true @@ -126,7 +126,7 @@ components: architecture: arm64 files: # Rust Injector Binary - - source: https://zarf-public.s3-us-gov-west-1.amazonaws.com/injector/###ZARF_PKG_TMPL_INJECTOR_VERSION###/zarf-injector-arm64 + - source: https://zarf-init.s3.us-east-2.amazonaws.com/injector/###ZARF_PKG_TMPL_INJECTOR_VERSION###/zarf-injector-arm64 target: "###ZARF_TEMP###/zarf-injector" shasum: "###ZARF_PKG_TMPL_INJECTOR_ARM64_SHASUM###" executable: true diff --git a/site/src/content/docs/getting-started/index.mdx b/site/src/content/docs/getting-started/index.mdx index 2e294ab9b3..c2f4ca042b 100644 --- a/site/src/content/docs/getting-started/index.mdx +++ b/site/src/content/docs/getting-started/index.mdx @@ -23,7 +23,7 @@ import { Tabs, TabItem, LinkCard, CardGrid } from '@astrojs/starlight/components # (Select optional components as desired) # Now you are ready to deploy any Zarf Package, try out our Retro Arcade!! - zarf package deploy oci://🦄/dos-games:1.0.0 --key=https://zarf.dev/cosign.pub + zarf package deploy oci://ghcr.io/zarf-dev/packages/dos-games:1.1.0 --key=https://zarf.dev/cosign.pub # (Select 'Y' to confirm deployment) ``` @@ -39,7 +39,7 @@ import { Tabs, TabItem, LinkCard, CardGrid } from '@astrojs/starlight/components zarf init --confirm # Deploy the DOS Games package - zarf package deploy oci://🦄/dos-games:1.0.0 \ + zarf package deploy oci://ghcr.io/zarf-dev/packages/dos-games:1.1.0 \ --key=https://zarf.dev/cosign.pub \ --confirm ``` diff --git a/src/cmd/root.go b/src/cmd/root.go index 876efdf20e..62e0582c8e 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -96,7 +96,8 @@ func Execute(ctx context.Context) { if len(comps) > 1 && comps[1] == "tools" && slices.Contains(defaultPrintCmds, comps[2]) { cmd.PrintErrln(cmd.ErrPrefix(), err.Error()) } else { - pterm.Error.Println(err.Error()) + errParagraph := message.Paragraph(err.Error()) + pterm.Error.Println(errParagraph) } os.Exit(1) } diff --git a/src/cmd/tools/zarf.go b/src/cmd/tools/zarf.go index 7c6ba91e88..5a9cd11718 100644 --- a/src/cmd/tools/zarf.go +++ b/src/cmd/tools/zarf.go @@ -21,7 +21,6 @@ import ( "github.com/zarf-dev/zarf/src/cmd/common" "github.com/zarf-dev/zarf/src/config" "github.com/zarf-dev/zarf/src/config/lang" - "github.com/zarf-dev/zarf/src/internal/gitea" "github.com/zarf-dev/zarf/src/internal/packager/helm" "github.com/zarf-dev/zarf/src/internal/packager/template" "github.com/zarf-dev/zarf/src/pkg/cluster" @@ -70,7 +69,7 @@ var getCredsCmd = &cobra.Command{ } // TODO: Determine if this is actually needed. if state.Distro == "" { - return errors.New("Zarf state secret did not load properly") + return errors.New("zarf state secret did not load properly") } if len(args) > 0 { @@ -97,7 +96,7 @@ var updateCredsCmd = &cobra.Command{ } else { if !slices.Contains(validKeys, args[0]) { cmd.Help() - return fmt.Errorf("invalid service key specified, valid keys are: %s, %s, and %s", message.RegistryKey, message.GitKey, message.ArtifactKey) + return fmt.Errorf("invalid service key specified, valid key choices are: %v", validKeys) } } @@ -116,7 +115,7 @@ var updateCredsCmd = &cobra.Command{ } // TODO: Determine if this is actually needed. if oldState.Distro == "" { - return errors.New("Zarf state secret did not load properly") + return errors.New("zarf state secret did not load properly") } newState, err := cluster.MergeZarfState(oldState, updateCredsInitOpts, args) if err != nil { @@ -141,39 +140,29 @@ var updateCredsCmd = &cobra.Command{ if confirm { // Update registry and git pull secrets if slices.Contains(args, message.RegistryKey) { - c.UpdateZarfManagedImageSecrets(ctx, newState) - } - if slices.Contains(args, message.GitKey) { - c.UpdateZarfManagedGitSecrets(ctx, newState) - } - - // Update artifact token (if internal) - if slices.Contains(args, message.ArtifactKey) && newState.ArtifactServer.PushToken == "" && newState.ArtifactServer.IsInternal() { - tunnel, err := c.NewTunnel(cluster.ZarfNamespaceName, cluster.SvcResource, cluster.ZarfGitServerName, "", 0, cluster.ZarfGitServerPort) + err := c.UpdateZarfManagedImageSecrets(ctx, newState) if err != nil { return err } - _, err = tunnel.Connect(cmd.Context()) - if err != nil { - return err - } - defer tunnel.Close() - tunnelURL := tunnel.HTTPEndpoint() - giteaClient, err := gitea.NewClient(tunnelURL, oldState.GitServer.PushUsername, oldState.GitServer.PushPassword) + } + if slices.Contains(args, message.GitKey) { + err := c.UpdateZarfManagedGitSecrets(ctx, newState) if err != nil { return err } - err = tunnel.Wrap(func() error { - tokenSha1, err := giteaClient.CreatePackageRegistryToken(ctx) - if err != nil { - return err - } - newState.ArtifactServer.PushToken = tokenSha1 - return nil - }) + } + // TODO once Zarf is changed so the default state is empty for a service when it is not deployed + // and sufficient time has passed for users state to get updated we can remove this check + internalGitServerExists, err := c.InternalGitServerExists(cmd.Context()) + if err != nil { + return err + } + + // Update artifact token (if internal) + if slices.Contains(args, message.ArtifactKey) && newState.ArtifactServer.PushToken == "" && newState.ArtifactServer.IsInternal() && internalGitServerExists { + newState.ArtifactServer.PushToken, err = c.UpdateInternalArtifactServerToken(ctx, oldState.GitServer) if err != nil { - // Warn if we couldn't actually update the git server (it might not be installed and we should try to continue) - message.Warnf(lang.CmdToolsUpdateCredsUnableCreateToken, err.Error()) + return fmt.Errorf("unable to create the new Gitea artifact token: %w", err) } } @@ -193,35 +182,10 @@ var updateCredsCmd = &cobra.Command{ message.Warnf(lang.CmdToolsUpdateCredsUnableUpdateRegistry, err.Error()) } } - if slices.Contains(args, message.GitKey) && newState.GitServer.IsInternal() { - tunnel, err := c.NewTunnel(cluster.ZarfNamespaceName, cluster.SvcResource, cluster.ZarfGitServerName, "", 0, cluster.ZarfGitServerPort) - if err != nil { - return err - } - _, err = tunnel.Connect(cmd.Context()) - if err != nil { - return err - } - defer tunnel.Close() - tunnelURL := tunnel.HTTPEndpoint() - giteaClient, err := gitea.NewClient(tunnelURL, oldState.GitServer.PushUsername, oldState.GitServer.PushPassword) - if err != nil { - return err - } - err = tunnel.Wrap(func() error { - err := giteaClient.UpdateGitUser(ctx, newState.GitServer.PullUsername, newState.GitServer.PullPassword) - if err != nil { - return err - } - err = giteaClient.UpdateGitUser(ctx, newState.GitServer.PushUsername, newState.GitServer.PushPassword) - if err != nil { - return err - } - return nil - }) + if slices.Contains(args, message.GitKey) && newState.GitServer.IsInternal() && internalGitServerExists { + err := c.UpdateInternalGitServerSecret(cmd.Context(), oldState.GitServer, newState.GitServer) if err != nil { - // Warn if we couldn't actually update the git server (it might not be installed and we should try to continue) - message.Warnf(lang.CmdToolsUpdateCredsUnableUpdateGit, err.Error()) + return fmt.Errorf("unable to update Zarf Git Server values: %w", err) } } if slices.Contains(args, message.AgentKey) { diff --git a/src/config/lang/english.go b/src/config/lang/english.go index 1bca5f773a..50ce790c44 100644 --- a/src/config/lang/english.go +++ b/src/config/lang/english.go @@ -576,9 +576,7 @@ $ zarf tools update-creds artifact --artifact-push-username={USERNAME} --artifac CmdToolsUpdateCredsConfirmFlag = "Confirm updating credentials without prompting" CmdToolsUpdateCredsConfirmProvided = "Confirm flag specified, continuing without prompting." CmdToolsUpdateCredsConfirmContinue = "Continue with these changes?" - CmdToolsUpdateCredsUnableCreateToken = "Unable to create the new Gitea artifact token: %s" CmdToolsUpdateCredsUnableUpdateRegistry = "Unable to update Zarf Registry values: %s" - CmdToolsUpdateCredsUnableUpdateGit = "Unable to update Zarf Git Server values: %s" CmdToolsUpdateCredsUnableUpdateAgent = "Unable to update Zarf Agent TLS secrets: %s" CmdToolsUpdateCredsUnableUpdateCreds = "Unable to update Zarf credentials" diff --git a/src/internal/packager/helm/chart.go b/src/internal/packager/helm/chart.go index 134bb4c493..daf59902e5 100644 --- a/src/internal/packager/helm/chart.go +++ b/src/internal/packager/helm/chart.go @@ -91,6 +91,9 @@ func (h *Helm) InstallOrUpgradeChart(ctx context.Context) (types.ConnectStrings, return nil }, retry.Context(ctx), retry.Attempts(uint(h.retries)), retry.Delay(500*time.Millisecond)) if err != nil { + removeMsg := "if you need to remove the failed chart, use `zarf package remove`" + installErr := fmt.Errorf("unable to install chart after %d attempts: %w: %s", h.retries, err, removeMsg) + releases, _ := histClient.Run(h.chart.ReleaseName) previouslyDeployedVersion := 0 @@ -101,21 +104,18 @@ func (h *Helm) InstallOrUpgradeChart(ctx context.Context) (types.ConnectStrings, } } - removeMsg := "if you need to remove the failed chart, use `zarf package remove`" - // No prior releases means this was an initial install. if previouslyDeployedVersion == 0 { - return nil, "", fmt.Errorf("unable to install chart after %d attempts: %s", h.retries, removeMsg) + return nil, "", installErr } // Attempt to rollback on a failed upgrade. spinner.Updatef("Performing chart rollback") err = h.rollbackChart(h.chart.ReleaseName, previouslyDeployedVersion) if err != nil { - return nil, "", fmt.Errorf("unable to upgrade chart after %d attempts and unable to rollback: %s", h.retries, removeMsg) + return nil, "", fmt.Errorf("%w: unable to rollback: %w", installErr, err) } - - return nil, "", fmt.Errorf("unable to upgrade chart after %d attempts: %s", h.retries, removeMsg) + return nil, "", installErr } // return any collected connect strings for zarf connect. diff --git a/src/internal/packager/helm/repo.go b/src/internal/packager/helm/repo.go index 24f3a7f4b0..378b12c9cf 100644 --- a/src/internal/packager/helm/repo.go +++ b/src/internal/packager/helm/repo.go @@ -13,23 +13,22 @@ import ( "strings" "github.com/defenseunicorns/pkg/helpers/v2" - "github.com/zarf-dev/zarf/src/config" - "github.com/zarf-dev/zarf/src/config/lang" - "github.com/zarf-dev/zarf/src/internal/git" - "github.com/zarf-dev/zarf/src/pkg/message" - "github.com/zarf-dev/zarf/src/pkg/transform" - "github.com/zarf-dev/zarf/src/pkg/utils" "helm.sh/helm/v3/pkg/action" "helm.sh/helm/v3/pkg/chart" - "helm.sh/helm/v3/pkg/cli" - "helm.sh/helm/v3/pkg/helmpath" - "helm.sh/helm/v3/pkg/registry" - "k8s.io/client-go/util/homedir" - "helm.sh/helm/v3/pkg/chart/loader" + "helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/downloader" "helm.sh/helm/v3/pkg/getter" + "helm.sh/helm/v3/pkg/registry" "helm.sh/helm/v3/pkg/repo" + "k8s.io/client-go/util/homedir" + + "github.com/zarf-dev/zarf/src/config" + "github.com/zarf-dev/zarf/src/config/lang" + "github.com/zarf-dev/zarf/src/internal/git" + "github.com/zarf-dev/zarf/src/pkg/message" + "github.com/zarf-dev/zarf/src/pkg/transform" + "github.com/zarf-dev/zarf/src/pkg/utils" ) // PackageChart creates a chart archive from a path to a chart on the host os and builds chart dependencies @@ -181,10 +180,6 @@ func (h *Helm) DownloadPublishedChart(ctx context.Context, cosignKeyPath string) chartURL, err = repo.FindChartInAuthRepoURL(h.chart.URL, username, password, chartName, h.chart.Version, pull.CertFile, pull.KeyFile, pull.CaFile, getter.All(pull.Settings)) if err != nil { - if strings.Contains(err.Error(), "not found") { - // Intentionally dogsled this error since this is just a nice to have helper - _ = h.listAvailableChartsAndVersions(pull) - } return fmt.Errorf("unable to pull the helm chart: %w", err) } } @@ -337,54 +332,3 @@ func (h *Helm) loadAndValidateChart(location string) (loader.ChartLoader, *chart return cl, chart, nil } - -func (h *Helm) listAvailableChartsAndVersions(pull *action.Pull) error { - c := repo.Entry{ - URL: h.chart.URL, - CertFile: pull.CertFile, - KeyFile: pull.KeyFile, - CAFile: pull.CaFile, - Name: h.chart.Name, - } - - r, err := repo.NewChartRepository(&c, getter.All(pull.Settings)) - if err != nil { - return err - } - idx, err := r.DownloadIndexFile() - if err != nil { - return fmt.Errorf("looks like %q is not a valid chart repository or cannot be reached: %w", h.chart.URL, err) - } - defer func() { - os.RemoveAll(filepath.Join(r.CachePath, helmpath.CacheChartsFile(r.Config.Name))) - os.RemoveAll(filepath.Join(r.CachePath, helmpath.CacheIndexFile(r.Config.Name))) - }() - - // Read the index file for the repository to get chart information and return chart URL - repoIndex, err := repo.LoadIndexFile(idx) - if err != nil { - return err - } - - chartData := [][]string{} - for name, entries := range repoIndex.Entries { - versions := "" - for idx, entry := range entries { - separator := "" - if idx < len(entries)-1 { - separator = ", " - } - versions += entry.Version + separator - } - - versions = helpers.Truncate(versions, 75, false) - chartData = append(chartData, []string{name, versions}) - } - - message.Notef("Available charts and versions from %q:", h.chart.URL) - - // Print out the table for the user - header := []string{"Chart", "Versions"} - message.Table(header, chartData) - return nil -} diff --git a/src/pkg/cluster/secrets.go b/src/pkg/cluster/secrets.go index 43c3402b64..aa693c6a44 100644 --- a/src/pkg/cluster/secrets.go +++ b/src/pkg/cluster/secrets.go @@ -13,6 +13,7 @@ import ( "maps" corev1 "k8s.io/api/core/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/zarf-dev/zarf/src/config" @@ -112,78 +113,79 @@ func (c *Cluster) GenerateGitPullCreds(namespace, name string, gitServerInfo typ } // UpdateZarfManagedImageSecrets updates all Zarf-managed image secrets in all namespaces based on state -// TODO: Refactor to return errors properly. -func (c *Cluster) UpdateZarfManagedImageSecrets(ctx context.Context, state *types.ZarfState) { +func (c *Cluster) UpdateZarfManagedImageSecrets(ctx context.Context, state *types.ZarfState) error { spinner := message.NewProgressSpinner("Updating existing Zarf-managed image secrets") defer spinner.Stop() namespaceList, err := c.Clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) if err != nil { - spinner.Errorf(err, "Unable to get k8s namespaces") - } else { - // Update all image pull secrets - for _, namespace := range namespaceList.Items { - currentRegistrySecret, err := c.Clientset.CoreV1().Secrets(namespace.Name).Get(ctx, config.ZarfImagePullSecretName, metav1.GetOptions{}) - if err != nil { - continue - } - - // Check if this is a Zarf managed secret or is in a namespace the Zarf agent will take action in - if currentRegistrySecret.Labels[ZarfManagedByLabel] == "zarf" || - (namespace.Labels[AgentLabel] != "skip" && namespace.Labels[AgentLabel] != "ignore") { - spinner.Updatef("Updating existing Zarf-managed image secret for namespace: '%s'", namespace.Name) - - newRegistrySecret, err := c.GenerateRegistryPullCreds(ctx, namespace.Name, config.ZarfImagePullSecretName, state.RegistryInfo) - if err != nil { - message.WarnErrf(err, "Unable to generate registry creds") - continue - } - if !maps.EqualFunc(currentRegistrySecret.Data, newRegistrySecret.Data, func(v1, v2 []byte) bool { return bytes.Equal(v1, v2) }) { - _, err := c.Clientset.CoreV1().Secrets(newRegistrySecret.Namespace).Update(ctx, newRegistrySecret, metav1.UpdateOptions{}) - if err != nil { - message.WarnErrf(err, "Problem creating registry secret for the %s namespace", namespace.Name) - } - } - } + return err + } + // Update all image pull secrets + for _, namespace := range namespaceList.Items { + currentRegistrySecret, err := c.Clientset.CoreV1().Secrets(namespace.Name).Get(ctx, config.ZarfImagePullSecretName, metav1.GetOptions{}) + if kerrors.IsNotFound(err) { + continue + } + if err != nil { + return err + } + // Skip if namespace is skipped and secret is not managed by Zarf. + if currentRegistrySecret.Labels[ZarfManagedByLabel] != "zarf" && (namespace.Labels[AgentLabel] == "skip" || namespace.Labels[AgentLabel] == "ignore") { + continue + } + newRegistrySecret, err := c.GenerateRegistryPullCreds(ctx, namespace.Name, config.ZarfImagePullSecretName, state.RegistryInfo) + if err != nil { + return err + } + if maps.EqualFunc(currentRegistrySecret.Data, newRegistrySecret.Data, func(v1, v2 []byte) bool { return bytes.Equal(v1, v2) }) { + continue + } + spinner.Updatef("Updating existing Zarf-managed image secret for namespace: '%s'", namespace.Name) + _, err = c.Clientset.CoreV1().Secrets(newRegistrySecret.Namespace).Update(ctx, newRegistrySecret, metav1.UpdateOptions{}) + if err != nil { + return err } - spinner.Success() } + + spinner.Success() + return nil } // UpdateZarfManagedGitSecrets updates all Zarf-managed git secrets in all namespaces based on state -// TODO: Refactor to return errors properly. -func (c *Cluster) UpdateZarfManagedGitSecrets(ctx context.Context, state *types.ZarfState) { +func (c *Cluster) UpdateZarfManagedGitSecrets(ctx context.Context, state *types.ZarfState) error { spinner := message.NewProgressSpinner("Updating existing Zarf-managed git secrets") defer spinner.Stop() namespaceList, err := c.Clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) if err != nil { - spinner.Errorf(err, "Unable to get k8s namespaces") - } else { - // Update all git pull secrets - for _, namespace := range namespaceList.Items { - currentGitSecret, err := c.Clientset.CoreV1().Secrets(namespace.Name).Get(ctx, config.ZarfGitServerSecretName, metav1.GetOptions{}) - if err != nil { - continue - } - - // Check if this is a Zarf managed secret or is in a namespace the Zarf agent will take action in - if currentGitSecret.Labels[ZarfManagedByLabel] == "zarf" || - (namespace.Labels[AgentLabel] != "skip" && namespace.Labels[AgentLabel] != "ignore") { - spinner.Updatef("Updating existing Zarf-managed git secret for namespace: '%s'", namespace.Name) - - // Create the secret - newGitSecret := c.GenerateGitPullCreds(namespace.Name, config.ZarfGitServerSecretName, state.GitServer) - if !maps.Equal(currentGitSecret.StringData, newGitSecret.StringData) { - _, err := c.Clientset.CoreV1().Secrets(newGitSecret.Namespace).Update(ctx, newGitSecret, metav1.UpdateOptions{}) - if err != nil { - message.WarnErrf(err, "Problem creating git server secret for the %s namespace", namespace.Name) - } - } - } + return err + } + for _, namespace := range namespaceList.Items { + currentGitSecret, err := c.Clientset.CoreV1().Secrets(namespace.Name).Get(ctx, config.ZarfGitServerSecretName, metav1.GetOptions{}) + if kerrors.IsNotFound(err) { + continue + } + if err != nil { + continue + } + // Skip if namespace is skipped and secret is not managed by Zarf. + if currentGitSecret.Labels[ZarfManagedByLabel] != "zarf" && (namespace.Labels[AgentLabel] == "skip" || namespace.Labels[AgentLabel] == "ignore") { + continue + } + newGitSecret := c.GenerateGitPullCreds(namespace.Name, config.ZarfGitServerSecretName, state.GitServer) + if maps.Equal(currentGitSecret.StringData, newGitSecret.StringData) { + continue + } + spinner.Updatef("Updating existing Zarf-managed git secret for namespace: %s", namespace.Name) + _, err = c.Clientset.CoreV1().Secrets(newGitSecret.Namespace).Update(ctx, newGitSecret, metav1.UpdateOptions{}) + if err != nil { + return err } - spinner.Success() } + + spinner.Success() + return nil } // GetServiceInfoFromRegistryAddress gets the service info for a registry address if it is a NodePort diff --git a/src/pkg/cluster/secrets_test.go b/src/pkg/cluster/secrets_test.go index 80cd33b933..0ee731dfe9 100644 --- a/src/pkg/cluster/secrets_test.go +++ b/src/pkg/cluster/secrets_test.go @@ -4,7 +4,6 @@ package cluster import ( - "context" "testing" "github.com/stretchr/testify/require" @@ -12,114 +11,197 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes/fake" + "github.com/zarf-dev/zarf/src/config" + "github.com/zarf-dev/zarf/src/test/testutil" "github.com/zarf-dev/zarf/src/types" ) -func TestGenerateRegistryPullCredsWithOutSvc(t *testing.T) { - c := &Cluster{Clientset: fake.NewSimpleClientset()} - ctx := context.Background() - ri := types.RegistryInfo{ - PullUsername: "pull-user", - PullPassword: "pull-password", - Address: "example.com", - } - secret, err := c.GenerateRegistryPullCreds(ctx, "foo", "bar", ri) - require.NoError(t, err) - expectedSecret := corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "bar", - Namespace: "foo", - Labels: map[string]string{ - ZarfManagedByLabel: "zarf", - }, - }, - Type: corev1.SecretTypeDockerConfigJson, - Data: map[string][]byte{ - ".dockerconfigjson": []byte(`{"auths":{"example.com":{"auth":"cHVsbC11c2VyOnB1bGwtcGFzc3dvcmQ="}}}`), - }, - } - require.Equal(t, expectedSecret, *secret) -} +func TestUpdateZarfManagedSecrets(t *testing.T) { + ctx := testutil.TestContext(t) -func TestGenerateRegistryPullCredsWithSvc(t *testing.T) { - c := &Cluster{Clientset: fake.NewSimpleClientset()} - ctx := context.Background() - svc := &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: "good-service", - Namespace: "whatever", + tests := []struct { + name string + namespaceLabels map[string]string + secretLabels map[string]string + updatedImageSecret bool + updatedGitSecret bool + }{ + { + name: "modify", + updatedImageSecret: true, + updatedGitSecret: true, }, - Spec: corev1.ServiceSpec{ - Type: corev1.ServiceTypeNodePort, - Ports: []corev1.ServicePort{ - { - NodePort: 30001, - Port: 3333, - }, + { + name: "skip namespace", + namespaceLabels: map[string]string{ + AgentLabel: "skip", }, - ClusterIP: "10.11.12.13", }, - } - - _, err := c.Clientset.CoreV1().Services("whatever").Create(ctx, svc, metav1.CreateOptions{}) - require.NoError(t, err) - - ri := types.RegistryInfo{ - PullUsername: "pull-user", - PullPassword: "pull-password", - Address: "127.0.0.1:30001", - } - secret, err := c.GenerateRegistryPullCreds(ctx, "foo", "bar", ri) - require.NoError(t, err) - expectedSecret := corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Secret", + { + name: "ignore namespace", + namespaceLabels: map[string]string{ + AgentLabel: "ignore", + }, }, - ObjectMeta: metav1.ObjectMeta{ - Name: "bar", - Namespace: "foo", - Labels: map[string]string{ + { + name: "skip namespace managed secret", + namespaceLabels: map[string]string{ + AgentLabel: "skip", + }, + secretLabels: map[string]string{ ZarfManagedByLabel: "zarf", }, + updatedImageSecret: true, + updatedGitSecret: true, }, - Type: corev1.SecretTypeDockerConfigJson, - Data: map[string][]byte{ - ".dockerconfigjson": []byte(`{"auths":{"10.11.12.13:3333":{"auth":"cHVsbC11c2VyOnB1bGwtcGFzc3dvcmQ="},"127.0.0.1:30001":{"auth":"cHVsbC11c2VyOnB1bGwtcGFzc3dvcmQ="}}}`), - }, - } - require.Equal(t, expectedSecret, *secret) -} - -func TestGenerateGitPullCreds(t *testing.T) { - c := &Cluster{} - gi := types.GitServerInfo{ - PullUsername: "pull-user", - PullPassword: "pull-password", - } - secret := c.GenerateGitPullCreds("foo", "bar", gi) - expectedSecret := corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "bar", - Namespace: "foo", - Labels: map[string]string{ + { + name: "ignore namespace managed secret", + namespaceLabels: map[string]string{ + AgentLabel: "ignore", + }, + secretLabels: map[string]string{ ZarfManagedByLabel: "zarf", }, + updatedImageSecret: true, + updatedGitSecret: true, }, - Type: corev1.SecretTypeOpaque, - Data: map[string][]byte{}, - StringData: map[string]string{ - "username": "pull-user", - "password": "pull-password", - }, } - require.Equal(t, expectedSecret, *secret) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Cluster{ + Clientset: fake.NewSimpleClientset(), + } + + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Labels: tt.namespaceLabels, + }, + } + _, err := c.Clientset.CoreV1().Namespaces().Create(ctx, namespace, metav1.CreateOptions{}) + require.NoError(t, err) + svc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "good-service", + Namespace: namespace.ObjectMeta.Name, + }, + Spec: corev1.ServiceSpec{ + Type: corev1.ServiceTypeNodePort, + Ports: []corev1.ServicePort{ + { + NodePort: 30001, + Port: 3333, + }, + }, + ClusterIP: "10.11.12.13", + }, + } + _, err = c.Clientset.CoreV1().Services(namespace.ObjectMeta.Name).Create(ctx, svc, metav1.CreateOptions{}) + require.NoError(t, err) + imageSecret := &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: config.ZarfImagePullSecretName, + Namespace: namespace.ObjectMeta.Name, + Labels: tt.secretLabels, + }, + } + _, err = c.Clientset.CoreV1().Secrets(imageSecret.ObjectMeta.Namespace).Create(ctx, imageSecret, metav1.CreateOptions{}) + require.NoError(t, err) + gitSecret := &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: config.ZarfGitServerSecretName, + Namespace: namespace.ObjectMeta.Name, + Labels: tt.secretLabels, + }, + } + _, err = c.Clientset.CoreV1().Secrets(gitSecret.ObjectMeta.Namespace).Create(ctx, gitSecret, metav1.CreateOptions{}) + require.NoError(t, err) + + state := &types.ZarfState{ + GitServer: types.GitServerInfo{ + PullUsername: "pull-user", + PullPassword: "pull-password", + }, + RegistryInfo: types.RegistryInfo{ + PullUsername: "pull-user", + PullPassword: "pull-password", + Address: "127.0.0.1:30001", + }, + } + err = c.UpdateZarfManagedImageSecrets(ctx, state) + require.NoError(t, err) + err = c.UpdateZarfManagedGitSecrets(ctx, state) + require.NoError(t, err) + + // Make sure no new namespaces or secrets have been created. + namespaceList, err := c.Clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{}) + require.NoError(t, err) + require.Len(t, namespaceList.Items, 1) + for _, ns := range namespaceList.Items { + secretList, err := c.Clientset.CoreV1().Secrets(ns.ObjectMeta.Name).List(ctx, metav1.ListOptions{}) + require.NoError(t, err) + require.Len(t, secretList.Items, 2) + } + + // Check image registry secret + updatedImageSecret, err := c.Clientset.CoreV1().Secrets(namespace.ObjectMeta.Name).Get(ctx, config.ZarfImagePullSecretName, metav1.GetOptions{}) + require.NoError(t, err) + expectedImageSecret := corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: config.ZarfImagePullSecretName, + Namespace: namespace.ObjectMeta.Name, + Labels: map[string]string{ + ZarfManagedByLabel: "zarf", + }, + }, + Type: corev1.SecretTypeDockerConfigJson, + Data: map[string][]byte{ + ".dockerconfigjson": []byte(`{"auths":{"10.11.12.13:3333":{"auth":"cHVsbC11c2VyOnB1bGwtcGFzc3dvcmQ="},"127.0.0.1:30001":{"auth":"cHVsbC11c2VyOnB1bGwtcGFzc3dvcmQ="}}}`), + }, + } + if !tt.updatedImageSecret { + expectedImageSecret = *imageSecret + } + require.Equal(t, expectedImageSecret, *updatedImageSecret) + + // Check git secret + updatedGitSecret, err := c.Clientset.CoreV1().Secrets(namespace.ObjectMeta.Name).Get(ctx, config.ZarfGitServerSecretName, metav1.GetOptions{}) + require.NoError(t, err) + expectedGitSecret := corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: config.ZarfGitServerSecretName, + Namespace: namespace.ObjectMeta.Name, + Labels: map[string]string{ + ZarfManagedByLabel: "zarf", + }, + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{}, + StringData: map[string]string{ + "username": state.GitServer.PullUsername, + "password": state.GitServer.PullPassword, + }, + } + if !tt.updatedGitSecret { + expectedGitSecret = *gitSecret + } + require.Equal(t, expectedGitSecret, *updatedGitSecret) + }) + } } diff --git a/src/pkg/cluster/zarf.go b/src/pkg/cluster/zarf.go index 7145349fea..3557544e14 100644 --- a/src/pkg/cluster/zarf.go +++ b/src/pkg/cluster/zarf.go @@ -20,6 +20,7 @@ import ( "github.com/avast/retry-go/v4" "github.com/zarf-dev/zarf/src/api/v1alpha1" "github.com/zarf-dev/zarf/src/config" + "github.com/zarf-dev/zarf/src/internal/gitea" "github.com/zarf-dev/zarf/src/pkg/message" "github.com/zarf-dev/zarf/src/types" ) @@ -133,8 +134,8 @@ func (c *Cluster) PackageSecretNeedsWait(deployedPackage *types.DeployedPackage, } // RecordPackageDeploymentAndWait records the deployment of a package to the cluster and waits for any webhooks to complete. -func (c *Cluster) RecordPackageDeploymentAndWait(ctx context.Context, pkg v1alpha1.ZarfPackage, components []types.DeployedComponent, connectStrings types.ConnectStrings, generation int, component v1alpha1.ZarfComponent, skipWebhooks bool) (*types.DeployedPackage, error) { - deployedPackage, err := c.RecordPackageDeployment(ctx, pkg, components, connectStrings, generation) +func (c *Cluster) RecordPackageDeploymentAndWait(ctx context.Context, pkg v1alpha1.ZarfPackage, components []types.DeployedComponent, generation int, component v1alpha1.ZarfComponent, skipWebhooks bool) (*types.DeployedPackage, error) { + deployedPackage, err := c.RecordPackageDeployment(ctx, pkg, components, generation) if err != nil { return nil, err } @@ -173,7 +174,7 @@ func (c *Cluster) RecordPackageDeploymentAndWait(ctx context.Context, pkg v1alph } // RecordPackageDeployment saves metadata about a package that has been deployed to the cluster. -func (c *Cluster) RecordPackageDeployment(ctx context.Context, pkg v1alpha1.ZarfPackage, components []types.DeployedComponent, connectStrings types.ConnectStrings, generation int) (deployedPackage *types.DeployedPackage, err error) { +func (c *Cluster) RecordPackageDeployment(ctx context.Context, pkg v1alpha1.ZarfPackage, components []types.DeployedComponent, generation int) (deployedPackage *types.DeployedPackage, err error) { packageName := pkg.Metadata.Name // Attempt to load information about webhooks for the package @@ -186,6 +187,16 @@ func (c *Cluster) RecordPackageDeployment(ctx context.Context, pkg v1alpha1.Zarf componentWebhooks = existingPackageSecret.ComponentWebhooks } + // TODO: This is done for backwards compartibility and could be removed in the future. + connectStrings := types.ConnectStrings{} + for _, comp := range components { + for _, chart := range comp.InstalledCharts { + for k, v := range chart.ConnectStrings { + connectStrings[k] = v + } + } + } + deployedPackage = &types.DeployedPackage{ Name: packageName, CLIVersion: config.CLIVersion, @@ -288,3 +299,72 @@ func (c *Cluster) GetInstalledChartsForComponent(ctx context.Context, packageNam return installedCharts, nil } + +// UpdateInternalArtifactServerToken updates the the artifact server token on the internal gitea server and returns it +func (c *Cluster) UpdateInternalArtifactServerToken(ctx context.Context, oldGitServer types.GitServerInfo) (string, error) { + tunnel, err := c.NewTunnel(ZarfNamespaceName, SvcResource, ZarfGitServerName, "", 0, ZarfGitServerPort) + if err != nil { + return "", err + } + _, err = tunnel.Connect(ctx) + if err != nil { + return "", err + } + defer tunnel.Close() + tunnelURL := tunnel.HTTPEndpoint() + giteaClient, err := gitea.NewClient(tunnelURL, oldGitServer.PushUsername, oldGitServer.PushPassword) + if err != nil { + return "", err + } + var newToken string + err = tunnel.Wrap(func() error { + newToken, err = giteaClient.CreatePackageRegistryToken(ctx) + if err != nil { + return err + } + return nil + }) + return newToken, err +} + +// UpdateInternalGitServerSecret updates the internal gitea server secrets with the new git server info +func (c *Cluster) UpdateInternalGitServerSecret(ctx context.Context, oldGitServer types.GitServerInfo, newGitServer types.GitServerInfo) error { + tunnel, err := c.NewTunnel(ZarfNamespaceName, SvcResource, ZarfGitServerName, "", 0, ZarfGitServerPort) + if err != nil { + return err + } + _, err = tunnel.Connect(ctx) + if err != nil { + return err + } + defer tunnel.Close() + tunnelURL := tunnel.HTTPEndpoint() + giteaClient, err := gitea.NewClient(tunnelURL, oldGitServer.PushUsername, oldGitServer.PushPassword) + if err != nil { + return err + } + err = tunnel.Wrap(func() error { + err := giteaClient.UpdateGitUser(ctx, newGitServer.PullUsername, newGitServer.PullPassword) + if err != nil { + return err + } + err = giteaClient.UpdateGitUser(ctx, newGitServer.PushUsername, newGitServer.PushPassword) + if err != nil { + return err + } + return nil + }) + if err != nil { + return err + } + return nil +} + +// InternalGitServerExists checks if the Zarf internal git server exists in the cluster. +func (c *Cluster) InternalGitServerExists(ctx context.Context) (bool, error) { + _, err := c.Clientset.CoreV1().Services(ZarfNamespaceName).Get(ctx, ZarfGitServerName, metav1.GetOptions{}) + if err != nil && !kerrors.IsNotFound(err) { + return false, err + } + return !kerrors.IsNotFound(err), nil +} diff --git a/src/pkg/cluster/zarf_test.go b/src/pkg/cluster/zarf_test.go index fe8b91d188..d2f3aefcde 100644 --- a/src/pkg/cluster/zarf_test.go +++ b/src/pkg/cluster/zarf_test.go @@ -285,3 +285,41 @@ func TestRegistryHPA(t *testing.T) { require.NoError(t, err) require.Equal(t, autoscalingv2.DisabledPolicySelect, *disableHpa.Spec.Behavior.ScaleDown.SelectPolicy) } + +func TestInternalGitServerExists(t *testing.T) { + tests := []struct { + name string + svc *corev1.Service + expectedExist bool + expectedErr error + }{ + { + name: "Git server exists", + svc: &corev1.Service{ObjectMeta: metav1.ObjectMeta{Name: ZarfGitServerName, Namespace: ZarfNamespaceName}}, + expectedExist: true, + expectedErr: nil, + }, + { + name: "Git server does not exist", + svc: nil, + expectedExist: false, + expectedErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cs := fake.NewSimpleClientset() + cluster := &Cluster{Clientset: cs} + ctx := context.Background() + if tt.svc != nil { + _, err := cs.CoreV1().Services(tt.svc.Namespace).Create(ctx, tt.svc, metav1.CreateOptions{}) + require.NoError(t, err) + } + + exists, err := cluster.InternalGitServerExists(ctx) + require.Equal(t, tt.expectedExist, exists) + require.Equal(t, tt.expectedErr, err) + }) + } +} diff --git a/src/pkg/lint/lint.go b/src/pkg/lint/lint.go index 9c0132f2e8..3c4be87eaf 100644 --- a/src/pkg/lint/lint.go +++ b/src/pkg/lint/lint.go @@ -36,7 +36,7 @@ func Validate(ctx context.Context, createOpts types.ZarfCreateOptions) error { return err } findings = append(findings, compFindings...) - schemaFindings, err := ValidatePackageSchema() + schemaFindings, err := ValidatePackageSchema(createOpts.SetVariables) if err != nil { return err } @@ -71,7 +71,7 @@ func lintComponents(ctx context.Context, pkg v1alpha1.ZarfPackage, createOpts ty node := chain.Head() for node != nil { component := node.ZarfComponent - compFindings, err := fillComponentTemplate(&component, createOpts) + compFindings, err := templateZarfObj(&component, createOpts.SetVariables) if err != nil { return nil, err } @@ -87,12 +87,12 @@ func lintComponents(ctx context.Context, pkg v1alpha1.ZarfPackage, createOpts ty return findings, nil } -func fillComponentTemplate(c *v1alpha1.ZarfComponent, createOpts types.ZarfCreateOptions) ([]PackageFinding, error) { +func templateZarfObj(zarfObj any, setVariables map[string]string) ([]PackageFinding, error) { var findings []PackageFinding templateMap := map[string]string{} setVarsAndWarn := func(templatePrefix string, deprecated bool) error { - yamlTemplates, err := utils.FindYamlTemplates(c, templatePrefix, "###") + yamlTemplates, err := utils.FindYamlTemplates(zarfObj, templatePrefix, "###") if err != nil { return err } @@ -105,7 +105,7 @@ func fillComponentTemplate(c *v1alpha1.ZarfComponent, createOpts types.ZarfCreat Severity: SevWarn, }) } - if _, present := createOpts.SetVariables[key]; !present { + if _, present := setVariables[key]; !present { unSetTemplates = true } } @@ -115,7 +115,7 @@ func fillComponentTemplate(c *v1alpha1.ZarfComponent, createOpts types.ZarfCreat Severity: SevWarn, }) } - for key, value := range createOpts.SetVariables { + for key, value := range setVariables { templateMap[fmt.Sprintf("%s%s###", templatePrefix, key)] = value } return nil @@ -130,7 +130,7 @@ func fillComponentTemplate(c *v1alpha1.ZarfComponent, createOpts types.ZarfCreat return nil, err } - if err := utils.ReloadYamlTemplate(c, templateMap); err != nil { + if err := utils.ReloadYamlTemplate(zarfObj, templateMap); err != nil { return nil, err } return findings, nil diff --git a/src/pkg/lint/lint_test.go b/src/pkg/lint/lint_test.go index d6ad24ad82..d499ba6e45 100644 --- a/src/pkg/lint/lint_test.go +++ b/src/pkg/lint/lint_test.go @@ -32,12 +32,10 @@ func TestLintComponents(t *testing.T) { require.Error(t, err) }) } -func TestFillComponentTemplate(t *testing.T) { - createOpts := types.ZarfCreateOptions{ - SetVariables: map[string]string{ - "KEY1": "value1", - "KEY2": "value2", - }, +func TestFillObjTemplate(t *testing.T) { + SetVariables := map[string]string{ + "KEY1": "value1", + "KEY2": "value2", } component := v1alpha1.ZarfComponent{ @@ -48,7 +46,7 @@ func TestFillComponentTemplate(t *testing.T) { }, } - findings, err := fillComponentTemplate(&component, createOpts) + findings, err := templateZarfObj(&component, SetVariables) require.NoError(t, err) expectedFindings := []PackageFinding{ { diff --git a/src/pkg/lint/schema.go b/src/pkg/lint/schema.go index ae3e991863..adf41e935b 100644 --- a/src/pkg/lint/schema.go +++ b/src/pkg/lint/schema.go @@ -18,7 +18,7 @@ import ( var ZarfSchema fs.ReadFileFS // ValidatePackageSchema checks the Zarf package in the current directory against the Zarf schema -func ValidatePackageSchema() ([]PackageFinding, error) { +func ValidatePackageSchema(setVariables map[string]string) ([]PackageFinding, error) { var untypedZarfPackage interface{} if err := utils.ReadYaml(layout.ZarfYAML, &untypedZarfPackage); err != nil { return nil, err @@ -29,6 +29,11 @@ func ValidatePackageSchema() ([]PackageFinding, error) { return nil, err } + _, err = templateZarfObj(&untypedZarfPackage, setVariables) + if err != nil { + return nil, err + } + return getSchemaFindings(jsonSchema, untypedZarfPackage) } diff --git a/src/pkg/lint/schema_test.go b/src/pkg/lint/schema_test.go index bd8b576a83..8ebcdc26bf 100644 --- a/src/pkg/lint/schema_test.go +++ b/src/pkg/lint/schema_test.go @@ -7,11 +7,13 @@ package lint import ( "fmt" "os" + "path/filepath" "testing" goyaml "github.com/goccy/go-yaml" "github.com/stretchr/testify/require" "github.com/zarf-dev/zarf/src/api/v1alpha1" + "github.com/zarf-dev/zarf/src/test/testutil" ) func TestZarfSchema(t *testing.T) { @@ -190,6 +192,24 @@ components: }) } +func TestValidatePackageSchema(t *testing.T) { + ZarfSchema = testutil.LoadSchema(t, "../../../zarf.schema.json") + setVariables := map[string]string{ + "PACKAGE_NAME": "test-package", + "MY_COMP_NAME": "test-comp", + } + cwd, err := os.Getwd() + require.NoError(t, err) + err = os.Chdir(filepath.Join("testdata", "package-with-templates")) + require.NoError(t, err) + defer func() { + require.NoError(t, os.Chdir(cwd)) + }() + findings, err := ValidatePackageSchema(setVariables) + require.Empty(t, findings) + require.NoError(t, err) +} + func TestYqCompat(t *testing.T) { t.Parallel() t.Run("Wrap standalone numbers in bracket", func(t *testing.T) { diff --git a/src/pkg/lint/testdata/package-with-templates/zarf.yaml b/src/pkg/lint/testdata/package-with-templates/zarf.yaml new file mode 100644 index 0000000000..f32ec32bab --- /dev/null +++ b/src/pkg/lint/testdata/package-with-templates/zarf.yaml @@ -0,0 +1,5 @@ +kind: ZarfPackageConfig +metadata: + name: "###ZARF_PKG_VAR_PACKAGE_NAME###" +components: + - name: "###ZARF_PKG_TMPL_MY_COMP_NAME###" diff --git a/src/pkg/packager/common.go b/src/pkg/packager/common.go index 6a3ec004e7..1325a70b46 100644 --- a/src/pkg/packager/common.go +++ b/src/pkg/packager/common.go @@ -36,7 +36,6 @@ type Packager struct { cluster *cluster.Cluster layout *layout.PackagePaths hpaModified bool - connectStrings types.ConnectStrings source sources.PackageSource } @@ -130,15 +129,16 @@ func (p *Packager) GetVariableConfig() *variables.VariableConfig { } // connectToCluster attempts to connect to a cluster if a connection is not already established -func (p *Packager) connectToCluster(ctx context.Context) (err error) { +func (p *Packager) connectToCluster(ctx context.Context) error { if p.isConnectedToCluster() { return nil } - p.cluster, err = cluster.NewClusterWithWait(ctx) + cluster, err := cluster.NewClusterWithWait(ctx) if err != nil { return err } + p.cluster = cluster return p.attemptClusterChecks(ctx) } @@ -150,7 +150,7 @@ func (p *Packager) isConnectedToCluster() bool { // attemptClusterChecks attempts to connect to the cluster and check for useful metadata and config mismatches. // NOTE: attemptClusterChecks should only return an error if there is a problem significant enough to halt a deployment, otherwise it should return nil and print a warning message. -func (p *Packager) attemptClusterChecks(ctx context.Context) (err error) { +func (p *Packager) attemptClusterChecks(ctx context.Context) error { spinner := message.NewProgressSpinner("Gathering additional cluster information (if available)") defer spinner.Stop() diff --git a/src/pkg/packager/creator/normal.go b/src/pkg/packager/creator/normal.go index 79c58250c0..847a22003e 100644 --- a/src/pkg/packager/creator/normal.go +++ b/src/pkg/packager/creator/normal.go @@ -119,7 +119,7 @@ func (pc *PackageCreator) LoadPackageDefinition(ctx context.Context, src *layout } } - if err := Validate(pkg, pc.createOpts.BaseDir); err != nil { + if err := Validate(pkg, pc.createOpts.BaseDir, pc.createOpts.SetVariables); err != nil { return v1alpha1.ZarfPackage{}, nil, err } diff --git a/src/pkg/packager/creator/skeleton.go b/src/pkg/packager/creator/skeleton.go index 218830b75f..648fb562b3 100644 --- a/src/pkg/packager/creator/skeleton.go +++ b/src/pkg/packager/creator/skeleton.go @@ -71,7 +71,7 @@ func (sc *SkeletonCreator) LoadPackageDefinition(ctx context.Context, src *layou message.Warn(warning) } - if err := Validate(pkg, sc.createOpts.BaseDir); err != nil { + if err := Validate(pkg, sc.createOpts.BaseDir, sc.createOpts.SetVariables); err != nil { return v1alpha1.ZarfPackage{}, nil, err } diff --git a/src/pkg/packager/creator/utils.go b/src/pkg/packager/creator/utils.go index b138ce7af2..1bb88750e0 100644 --- a/src/pkg/packager/creator/utils.go +++ b/src/pkg/packager/creator/utils.go @@ -19,12 +19,12 @@ import ( // Validate errors if a package violates the schema or any runtime validations // This must be run while in the parent directory of the zarf.yaml being validated -func Validate(pkg v1alpha1.ZarfPackage, baseDir string) error { +func Validate(pkg v1alpha1.ZarfPackage, baseDir string, setVariables map[string]string) error { if err := lint.ValidatePackage(pkg); err != nil { return fmt.Errorf("package validation failed: %w", err) } - findings, err := lint.ValidatePackageSchema() + findings, err := lint.ValidatePackageSchema(setVariables) if err != nil { return fmt.Errorf("unable to check schema: %w", err) } diff --git a/src/pkg/packager/deploy.go b/src/pkg/packager/deploy.go index 2c5f2e5f2d..517bff7d60 100644 --- a/src/pkg/packager/deploy.go +++ b/src/pkg/packager/deploy.go @@ -113,7 +113,6 @@ func (p *Packager) Deploy(ctx context.Context) error { } p.hpaModified = false - p.connectStrings = make(types.ConnectStrings) // Reset registry HPA scale down whether an error occurs or not defer p.resetRegistryHPA(ctx) @@ -138,7 +137,9 @@ func (p *Packager) Deploy(ctx context.Context) error { } // deployComponents loops through a list of ZarfComponents and deploys them. -func (p *Packager) deployComponents(ctx context.Context) (deployedComponents []types.DeployedComponent, err error) { +func (p *Packager) deployComponents(ctx context.Context) ([]types.DeployedComponent, error) { + deployedComponents := []types.DeployedComponent{} + // Process all the components we are deploying for _, component := range p.cfg.Pkg.Components { // Connect to cluster if a component requires it. @@ -168,10 +169,11 @@ func (p *Packager) deployComponents(ctx context.Context) (deployedComponents []t // Ensure we don't overwrite any installedCharts data when updating the package secret if p.isConnectedToCluster() { - deployedComponent.InstalledCharts, err = p.cluster.GetInstalledChartsForComponent(ctx, p.cfg.Pkg.Metadata.Name, component) + installedCharts, err := p.cluster.GetInstalledChartsForComponent(ctx, p.cfg.Pkg.Metadata.Name, component) if err != nil { message.Debugf("Unable to fetch installed Helm charts for component '%s': %s", component.Name, err.Error()) } + deployedComponent.InstalledCharts = installedCharts } deployedComponents = append(deployedComponents, deployedComponent) @@ -179,7 +181,7 @@ func (p *Packager) deployComponents(ctx context.Context) (deployedComponents []t // Update the package secret to indicate that we are attempting to deploy this component if p.isConnectedToCluster() { - if _, err := p.cluster.RecordPackageDeploymentAndWait(ctx, p.cfg.Pkg, deployedComponents, p.connectStrings, packageGeneration, component, p.cfg.DeployOpts.SkipWebhooks); err != nil { + if _, err := p.cluster.RecordPackageDeploymentAndWait(ctx, p.cfg.Pkg, deployedComponents, packageGeneration, component, p.cfg.DeployOpts.SkipWebhooks); err != nil { message.Debugf("Unable to record package deployment for component %s: this will affect features like `zarf package remove`: %s", component.Name, err.Error()) } } @@ -190,7 +192,7 @@ func (p *Packager) deployComponents(ctx context.Context) (deployedComponents []t if p.cfg.Pkg.IsInitConfig() { charts, deployErr = p.deployInitComponent(ctx, component) } else { - charts, deployErr = p.deployComponent(ctx, component, false /* keep img checksum */, false /* always push images */) + charts, deployErr = p.deployComponent(ctx, component, false, false) } onDeploy := component.Actions.OnDeploy @@ -207,33 +209,32 @@ func (p *Packager) deployComponents(ctx context.Context) (deployedComponents []t // Update the package secret to indicate that we failed to deploy this component deployedComponents[idx].Status = types.ComponentStatusFailed if p.isConnectedToCluster() { - if _, err := p.cluster.RecordPackageDeploymentAndWait(ctx, p.cfg.Pkg, deployedComponents, p.connectStrings, packageGeneration, component, p.cfg.DeployOpts.SkipWebhooks); err != nil { + if _, err := p.cluster.RecordPackageDeploymentAndWait(ctx, p.cfg.Pkg, deployedComponents, packageGeneration, component, p.cfg.DeployOpts.SkipWebhooks); err != nil { message.Debugf("Unable to record package deployment for component %q: this will affect features like `zarf package remove`: %s", component.Name, err.Error()) } } - - return deployedComponents, fmt.Errorf("unable to deploy component %q: %w", component.Name, deployErr) + return nil, fmt.Errorf("unable to deploy component %q: %w", component.Name, deployErr) } // Update the package secret to indicate that we successfully deployed this component deployedComponents[idx].InstalledCharts = charts deployedComponents[idx].Status = types.ComponentStatusSucceeded if p.isConnectedToCluster() { - if _, err := p.cluster.RecordPackageDeploymentAndWait(ctx, p.cfg.Pkg, deployedComponents, p.connectStrings, packageGeneration, component, p.cfg.DeployOpts.SkipWebhooks); err != nil { + if _, err := p.cluster.RecordPackageDeploymentAndWait(ctx, p.cfg.Pkg, deployedComponents, packageGeneration, component, p.cfg.DeployOpts.SkipWebhooks); err != nil { message.Debugf("Unable to record package deployment for component %q: this will affect features like `zarf package remove`: %s", component.Name, err.Error()) } } if err := actions.Run(ctx, onDeploy.Defaults, onDeploy.OnSuccess, p.variableConfig); err != nil { onFailure() - return deployedComponents, fmt.Errorf("unable to run component success action: %w", err) + return nil, fmt.Errorf("unable to run component success action: %w", err) } } return deployedComponents, nil } -func (p *Packager) deployInitComponent(ctx context.Context, component v1alpha1.ZarfComponent) (charts []types.InstalledChart, err error) { +func (p *Packager) deployInitComponent(ctx context.Context, component v1alpha1.ZarfComponent) ([]types.InstalledChart, error) { hasExternalRegistry := p.cfg.InitOpts.RegistryInfo.Address != "" isSeedRegistry := component.Name == "zarf-seed-registry" isRegistry := component.Name == "zarf-registry" @@ -247,7 +248,7 @@ func (p *Packager) deployInitComponent(ctx context.Context, component v1alpha1.Z // Always init the state before the first component that requires the cluster (on most deployments, the zarf-seed-registry) if component.RequiresCluster() && p.state == nil { - err = p.cluster.InitZarfState(ctx, p.cfg.InitOpts) + err := p.cluster.InitZarfState(ctx, p.cfg.InitOpts) if err != nil { return nil, fmt.Errorf("unable to initialize Zarf state: %w", err) } @@ -271,7 +272,9 @@ func (p *Packager) deployInitComponent(ctx context.Context, component v1alpha1.Z } } - charts, err = p.deployComponent(ctx, component, isAgent /* skip img checksum if isAgent */, isSeedRegistry /* skip image push if isSeedRegistry */) + // Skip image checksum if component is agent. + // Skip image push if component is seed registry. + charts, err := p.deployComponent(ctx, component, isAgent, isSeedRegistry) if err != nil { return nil, err } @@ -287,7 +290,7 @@ func (p *Packager) deployInitComponent(ctx context.Context, component v1alpha1.Z } // Deploy a Zarf Component. -func (p *Packager) deployComponent(ctx context.Context, component v1alpha1.ZarfComponent, noImgChecksum bool, noImgPush bool) (charts []types.InstalledChart, err error) { +func (p *Packager) deployComponent(ctx context.Context, component v1alpha1.ZarfComponent, noImgChecksum bool, noImgPush bool) ([]types.InstalledChart, error) { // Toggles for general deploy operations componentPath := p.layout.Components.Dirs[component.Name] @@ -305,9 +308,9 @@ func (p *Packager) deployComponent(ctx context.Context, component v1alpha1.ZarfC if component.RequiresCluster() { // Setup the state in the config if p.state == nil { - err = p.setupState(ctx) + err := p.setupState(ctx) if err != nil { - return charts, err + return nil, err } } @@ -321,30 +324,30 @@ func (p *Packager) deployComponent(ctx context.Context, component v1alpha1.ZarfC } } - err = p.populateComponentAndStateTemplates(component.Name) + err := p.populateComponentAndStateTemplates(component.Name) if err != nil { - return charts, err + return nil, err } if err = actions.Run(ctx, onDeploy.Defaults, onDeploy.Before, p.variableConfig); err != nil { - return charts, fmt.Errorf("unable to run component before action: %w", err) + return nil, fmt.Errorf("unable to run component before action: %w", err) } if hasFiles { if err := p.processComponentFiles(component, componentPath.Files); err != nil { - return charts, fmt.Errorf("unable to process the component files: %w", err) + return nil, fmt.Errorf("unable to process the component files: %w", err) } } if hasImages { if err := p.pushImagesToRegistry(ctx, component.Images, noImgChecksum); err != nil { - return charts, fmt.Errorf("unable to push images to the registry: %w", err) + return nil, fmt.Errorf("unable to push images to the registry: %w", err) } } if hasRepos { if err = p.pushReposToRepository(ctx, componentPath.Repos, component.Repos); err != nil { - return charts, fmt.Errorf("unable to push the repos to the repository: %w", err) + return nil, fmt.Errorf("unable to push the repos to the repository: %w", err) } } @@ -355,14 +358,16 @@ func (p *Packager) deployComponent(ctx context.Context, component v1alpha1.ZarfC }) } + charts := []types.InstalledChart{} if hasCharts || hasManifests { - if charts, err = p.installChartAndManifests(ctx, componentPath, component); err != nil { - return charts, err + charts, err = p.installChartAndManifests(ctx, componentPath, component) + if err != nil { + return nil, err } } if err = actions.Run(ctx, onDeploy.Defaults, onDeploy.After, p.variableConfig); err != nil { - return charts, fmt.Errorf("unable to run component after action: %w", err) + return nil, fmt.Errorf("unable to run component after action: %w", err) } err = g.Wait() @@ -452,7 +457,7 @@ func (p *Packager) processComponentFiles(component v1alpha1.ZarfComponent, pkgLo } // setupState fetches the current ZarfState from the k8s cluster and sets the packager to use it -func (p *Packager) setupState(ctx context.Context) (err error) { +func (p *Packager) setupState(ctx context.Context) error { // If we are touching K8s, make sure we can talk to it once per deployment spinner := message.NewProgressSpinner("Loading the Zarf State from the Kubernetes cluster") defer spinner.Stop() @@ -637,7 +642,9 @@ func (p *Packager) generateValuesOverrides(chart v1alpha1.ZarfChart, componentNa } // Install all Helm charts and raw k8s manifests into the k8s cluster. -func (p *Packager) installChartAndManifests(ctx context.Context, componentPaths *layout.ComponentPaths, component v1alpha1.ZarfComponent) (installedCharts []types.InstalledChart, err error) { +func (p *Packager) installChartAndManifests(ctx context.Context, componentPaths *layout.ComponentPaths, component v1alpha1.ZarfComponent) ([]types.InstalledChart, error) { + installedCharts := []types.InstalledChart{} + for _, chart := range component.Charts { // Do not wait for the chart to be ready if data injections are present. if len(component.DataInjections) > 0 { @@ -648,7 +655,7 @@ func (p *Packager) installChartAndManifests(ctx context.Context, componentPaths for idx := range chart.ValuesFiles { valueFilePath := helm.StandardValuesName(componentPaths.Values, chart, idx) if err := p.variableConfig.ReplaceTextTemplate(valueFilePath); err != nil { - return installedCharts, err + return nil, err } } @@ -656,7 +663,7 @@ func (p *Packager) installChartAndManifests(ctx context.Context, componentPaths // Values overrides are to be applied in order of Helm Chart Defaults -> Zarf `valuesFiles` -> Zarf `variables` -> DeployOpts overrides valuesOverrides, err := p.generateValuesOverrides(chart, component.Name) if err != nil { - return installedCharts, err + return nil, err } helmCfg := helm.New( @@ -673,16 +680,11 @@ func (p *Packager) installChartAndManifests(ctx context.Context, componentPaths p.cfg.PkgOpts.Retries), ) - addedConnectStrings, installedChartName, err := helmCfg.InstallOrUpgradeChart(ctx) + connectStrings, installedChartName, err := helmCfg.InstallOrUpgradeChart(ctx) if err != nil { - return installedCharts, err - } - installedCharts = append(installedCharts, types.InstalledChart{Namespace: chart.Namespace, ChartName: installedChartName}) - - // Iterate over any connectStrings and add to the main map - for name, description := range addedConnectStrings { - p.connectStrings[name] = description + return nil, err } + installedCharts = append(installedCharts, types.InstalledChart{Namespace: chart.Namespace, ChartName: installedChartName, ConnectStrings: connectStrings}) } for _, manifest := range component.Manifests { @@ -691,7 +693,7 @@ func (p *Packager) installChartAndManifests(ctx context.Context, componentPaths // The path is likely invalid because of how we compose OCI components, add an index suffix to the filename manifest.Files[idx] = fmt.Sprintf("%s-%d.yaml", manifest.Name, idx) if helpers.InvalidPath(filepath.Join(componentPaths.Manifests, manifest.Files[idx])) { - return installedCharts, fmt.Errorf("unable to find manifest file %s", manifest.Files[idx]) + return nil, fmt.Errorf("unable to find manifest file %s", manifest.Files[idx]) } } } @@ -722,21 +724,15 @@ func (p *Packager) installChartAndManifests(ctx context.Context, componentPaths p.cfg.PkgOpts.Retries), ) if err != nil { - return installedCharts, err + return nil, err } // Install the chart. - addedConnectStrings, installedChartName, err := helmCfg.InstallOrUpgradeChart(ctx) + connectStrings, installedChartName, err := helmCfg.InstallOrUpgradeChart(ctx) if err != nil { - return installedCharts, err - } - - installedCharts = append(installedCharts, types.InstalledChart{Namespace: manifest.Namespace, ChartName: installedChartName}) - - // Iterate over any connectStrings and add to the main map - for name, description := range addedConnectStrings { - p.connectStrings[name] = description + return nil, err } + installedCharts = append(installedCharts, types.InstalledChart{Namespace: manifest.Namespace, ChartName: installedChartName, ConnectStrings: connectStrings}) } return installedCharts, nil @@ -745,7 +741,15 @@ func (p *Packager) installChartAndManifests(ctx context.Context, componentPaths func (p *Packager) printTablesForDeployment(ctx context.Context, componentsToDeploy []types.DeployedComponent) error { // If not init config, print the application connection table if !p.cfg.Pkg.IsInitConfig() { - message.PrintConnectStringTable(p.connectStrings) + connectStrings := types.ConnectStrings{} + for _, comp := range componentsToDeploy { + for _, chart := range comp.InstalledCharts { + for k, v := range chart.ConnectStrings { + connectStrings[k] = v + } + } + } + message.PrintConnectStringTable(connectStrings) return nil } // Don't print if cluster is not configured diff --git a/src/pkg/packager/dev.go b/src/pkg/packager/dev.go index 1c1208be14..de7af6f4af 100644 --- a/src/pkg/packager/dev.go +++ b/src/pkg/packager/dev.go @@ -16,7 +16,6 @@ import ( "github.com/zarf-dev/zarf/src/pkg/message" "github.com/zarf-dev/zarf/src/pkg/packager/creator" "github.com/zarf-dev/zarf/src/pkg/packager/filters" - "github.com/zarf-dev/zarf/src/types" ) // DevDeploy creates + deploys a package in one shot @@ -53,7 +52,7 @@ func (p *Packager) DevDeploy(ctx context.Context) error { return err } - if err := creator.Validate(p.cfg.Pkg, p.cfg.CreateOpts.BaseDir); err != nil { + if err := creator.Validate(p.cfg.Pkg, p.cfg.CreateOpts.BaseDir, p.cfg.CreateOpts.SetVariables); err != nil { return fmt.Errorf("package validation failed: %w", err) } @@ -75,8 +74,6 @@ func (p *Packager) DevDeploy(ctx context.Context) error { message.HeaderInfof("📦 PACKAGE DEPLOY %s", p.cfg.Pkg.Metadata.Name) - p.connectStrings = make(types.ConnectStrings) - if !p.cfg.CreateOpts.NoYOLO { p.cfg.Pkg.Metadata.YOLO = true } else { diff --git a/src/pkg/packager/generate.go b/src/pkg/packager/generate.go index 068808b171..5d92d56b8d 100644 --- a/src/pkg/packager/generate.go +++ b/src/pkg/packager/generate.go @@ -21,7 +21,7 @@ import ( ) // Generate generates a Zarf package definition. -func (p *Packager) Generate(ctx context.Context) (err error) { +func (p *Packager) Generate(ctx context.Context) error { generatedZarfYAMLPath := filepath.Join(p.cfg.GenerateOpts.Output, layout.ZarfYAML) spinner := message.NewProgressSpinner("Generating package for %q at %s", p.cfg.GenerateOpts.Name, generatedZarfYAMLPath) diff --git a/src/pkg/packager/inspect.go b/src/pkg/packager/inspect.go index 88b1de88d8..ae850498b8 100644 --- a/src/pkg/packager/inspect.go +++ b/src/pkg/packager/inspect.go @@ -15,13 +15,14 @@ import ( ) // Inspect list the contents of a package. -func (p *Packager) Inspect(ctx context.Context) (err error) { +func (p *Packager) Inspect(ctx context.Context) error { wantSBOM := p.cfg.InspectOpts.ViewSBOM || p.cfg.InspectOpts.SBOMOutputDir != "" - p.cfg.Pkg, _, err = p.source.LoadPackageMetadata(ctx, p.layout, wantSBOM, true) + pkg, _, err := p.source.LoadPackageMetadata(ctx, p.layout, wantSBOM, true) if err != nil { return err } + p.cfg.Pkg = pkg if p.cfg.InspectOpts.ListImages { imageList := []string{} diff --git a/src/pkg/packager/interactive.go b/src/pkg/packager/interactive.go index 3a09595eab..bd44342e7f 100644 --- a/src/pkg/packager/interactive.go +++ b/src/pkg/packager/interactive.go @@ -18,7 +18,7 @@ import ( "github.com/zarf-dev/zarf/src/pkg/utils" ) -func (p *Packager) confirmAction(stage string, warnings []string, sbomViewFiles []string) (confirm bool) { +func (p *Packager) confirmAction(stage string, warnings []string, sbomViewFiles []string) bool { pterm.Println() message.HeaderInfof("📦 PACKAGE DEFINITION") utils.ColorPrintYAML(p.cfg.Pkg, p.getPackageYAMLHints(stage), true) @@ -77,6 +77,7 @@ func (p *Packager) confirmAction(stage string, warnings []string, sbomViewFiles pterm.Println() // Prompt the user for confirmation, on abort return false + var confirm bool if err := survey.AskOne(prompt, &confirm); err != nil || !confirm { // User aborted or declined, cancel the action return false diff --git a/src/pkg/packager/pull.go b/src/pkg/packager/pull.go index 7c00d36c9c..a80f36497c 100644 --- a/src/pkg/packager/pull.go +++ b/src/pkg/packager/pull.go @@ -10,12 +10,12 @@ import ( ) // Pull pulls a Zarf package and saves it as a compressed tarball. -func (p *Packager) Pull(ctx context.Context) (err error) { +func (p *Packager) Pull(ctx context.Context) error { if p.cfg.PkgOpts.OptionalComponents != "" { return fmt.Errorf("pull does not support optional components") } - _, err = p.source.Collect(ctx, p.cfg.PullOpts.OutputDirectory) + _, err := p.source.Collect(ctx, p.cfg.PullOpts.OutputDirectory) if err != nil { return err } diff --git a/src/pkg/packager/remove.go b/src/pkg/packager/remove.go index 4edcdd5b05..322db5570f 100644 --- a/src/pkg/packager/remove.go +++ b/src/pkg/packager/remove.go @@ -30,7 +30,7 @@ import ( ) // Remove removes a package that was already deployed onto a cluster, uninstalling all installed helm charts. -func (p *Packager) Remove(ctx context.Context) (err error) { +func (p *Packager) Remove(ctx context.Context) error { _, isClusterSource := p.source.(*sources.ClusterSource) if isClusterSource { p.cluster = p.source.(*sources.ClusterSource).Cluster @@ -40,10 +40,11 @@ func (p *Packager) Remove(ctx context.Context) (err error) { // we do not want to allow removal of signed packages without a signature if there are remove actions // as this is arbitrary code execution from an untrusted source - p.cfg.Pkg, _, err = p.source.LoadPackageMetadata(ctx, p.layout, false, false) + pkg, _, err := p.source.LoadPackageMetadata(ctx, p.layout, false, false) if err != nil { return err } + p.cfg.Pkg = pkg packageName := p.cfg.Pkg.Metadata.Name // Build a list of components to remove and determine if we need a cluster connection diff --git a/src/pkg/utils/cosign.go b/src/pkg/utils/cosign.go index eb16d6159f..21bfc282bd 100644 --- a/src/pkg/utils/cosign.go +++ b/src/pkg/utils/cosign.go @@ -6,20 +6,15 @@ package utils import ( "context" + "errors" "fmt" "io" - "os" "strings" "github.com/defenseunicorns/pkg/helpers/v2" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" "github.com/google/go-containerregistry/pkg/v1/remote" - "github.com/pkg/errors" - "github.com/zarf-dev/zarf/src/config" - "github.com/zarf-dev/zarf/src/config/lang" - "github.com/zarf-dev/zarf/src/pkg/message" - "github.com/sigstore/cosign/v2/cmd/cosign/cli/fulcio" "github.com/sigstore/cosign/v2/cmd/cosign/cli/options" "github.com/sigstore/cosign/v2/cmd/cosign/cli/sign" @@ -33,6 +28,9 @@ import ( _ "github.com/sigstore/sigstore/pkg/signature/kms/azure" _ "github.com/sigstore/sigstore/pkg/signature/kms/gcp" _ "github.com/sigstore/sigstore/pkg/signature/kms/hashivault" + + "github.com/zarf-dev/zarf/src/config/lang" + "github.com/zarf-dev/zarf/src/pkg/message" ) // Sget performs a cosign signature verification on a given image using the specified public key. @@ -41,12 +39,6 @@ import ( func Sget(ctx context.Context, image, key string, out io.Writer) error { message.Warnf(lang.WarnSGetDeprecation) - // If this is a DefenseUnicorns package, use an internal sget public key - if strings.HasPrefix(image, fmt.Sprintf("%s://defenseunicorns", helpers.SGETURLScheme)) { - os.Setenv("DU_SGET_KEY", config.CosignPublicKey) - key = "env://DU_SGET_KEY" - } - // Remove the custom protocol header from the url image = strings.TrimPrefix(image, helpers.SGETURLPrefix) diff --git a/src/test/e2e/00_use_cli_test.go b/src/test/e2e/00_use_cli_test.go index b663d58454..de0c5a7a51 100644 --- a/src/test/e2e/00_use_cli_test.go +++ b/src/test/e2e/00_use_cli_test.go @@ -43,9 +43,9 @@ func TestUseCLI(t *testing.T) { t.Run("zarf prepare sha256sum ", func(t *testing.T) { t.Parallel() // Test `zarf prepare sha256sum` for a remote asset - expectedShasum := "c3cdea0573ba5a058ec090b5d2683bf398e8b1614c37ec81136ed03b78167617\n" + expectedShasum := "b905e647e0d7876cfd5b665632cfc43ad919dc60408f7236c5b541c53277b503\n" - stdOut, stdErr, err := e2e.Zarf(t, "prepare", "sha256sum", "https://zarf-public.s3-us-gov-west-1.amazonaws.com/pipelines/zarf-prepare-shasum-remote-test-file.txt") + stdOut, stdErr, err := e2e.Zarf(t, "prepare", "sha256sum", "https://zarf-init.s3.us-east-2.amazonaws.com/injector/2024-07-22/zarf-injector-arm64") require.NoError(t, err, stdOut, stdErr) require.Contains(t, stdOut, expectedShasum, "The expected SHASUM should equal the actual SHASUM") }) diff --git a/src/test/e2e/05_tarball_test.go b/src/test/e2e/05_tarball_test.go index a9af583002..d1646899cf 100644 --- a/src/test/e2e/05_tarball_test.go +++ b/src/test/e2e/05_tarball_test.go @@ -83,7 +83,7 @@ func TestReproducibleTarballs(t *testing.T) { var ( createPath = filepath.Join("examples", "dos-games") tmp = t.TempDir() - tb = filepath.Join(tmp, fmt.Sprintf("zarf-package-dos-games-%s-1.0.0.tar.zst", e2e.Arch)) + tb = filepath.Join(tmp, fmt.Sprintf("zarf-package-dos-games-%s-1.1.0.tar.zst", e2e.Arch)) unpack1 = filepath.Join(tmp, "unpack1") unpack2 = filepath.Join(tmp, "unpack2") ) diff --git a/src/test/e2e/06_create_sbom_test.go b/src/test/e2e/06_create_sbom_test.go index 26890ccaf9..a3ee3b4118 100644 --- a/src/test/e2e/06_create_sbom_test.go +++ b/src/test/e2e/06_create_sbom_test.go @@ -18,7 +18,7 @@ func TestCreateSBOM(t *testing.T) { tmpdir := t.TempDir() sbomPath := filepath.Join(tmpdir, ".sbom-location") - pkgName := fmt.Sprintf("zarf-package-dos-games-%s-1.0.0.tar.zst", e2e.Arch) + pkgName := fmt.Sprintf("zarf-package-dos-games-%s-1.1.0.tar.zst", e2e.Arch) stdOut, stdErr, err := e2e.Zarf(t, "package", "create", "examples/dos-games", "--sbom-out", sbomPath, "--confirm") require.NoError(t, err, stdOut, stdErr) diff --git a/src/test/e2e/09_component_compose_test.go b/src/test/e2e/09_component_compose_test.go index 37a905456a..8c369feab9 100644 --- a/src/test/e2e/09_component_compose_test.go +++ b/src/test/e2e/09_component_compose_test.go @@ -61,7 +61,7 @@ func (suite *CompositionSuite) Test_0_ComposabilityExample() { // Ensure that the action was appended suite.Contains(stdErr, ` - - defenseunicorns/zarf-game:multi-tile-dark + - ghcr.io/zarf-dev/doom-game:0.0.1 actions: onDeploy: before: @@ -185,7 +185,7 @@ func (suite *CompositionSuite) Test_2_ComposabilityBadLocalOS() { _, stdErr, err := e2e.Zarf(suite.T(), "package", "create", composeTestBadLocalOS, "-o", "build", "--no-color", "--confirm") suite.Error(err) - suite.Contains(stdErr, "\"only.localOS\" \"linux\" cannot be redefined as \"windows\" during compose") + suite.Contains(e2e.StripMessageFormatting(stdErr), "\"only.localOS\" \"linux\" cannot be redefined as \"windows\" during compose") } func TestCompositionSuite(t *testing.T) { diff --git a/src/test/e2e/11_oci_pull_inspect_test.go b/src/test/e2e/11_oci_pull_inspect_test.go index a992a50f55..cd045ae0a6 100644 --- a/src/test/e2e/11_oci_pull_inspect_test.go +++ b/src/test/e2e/11_oci_pull_inspect_test.go @@ -55,7 +55,7 @@ func (suite *PullInspectTestSuite) Test_0_Pull() { // Verify the package was pulled correctly. suite.FileExists(out) - stdOut, stdErr, err = e2e.Zarf(suite.T(), "package", "inspect", out, "--key", "https://zarf.dev/cosign.pub", "--sbom-out", sbomTmp) + stdOut, stdErr, err = e2e.Zarf(suite.T(), "package", "inspect", out, "--key", "https://raw.githubusercontent.com/zarf-dev/zarf/v0.38.2/cosign.pub", "--sbom-out", sbomTmp) suite.NoError(err, stdOut, stdErr) suite.Contains(stdErr, "Validating SBOM checksums") suite.Contains(stdErr, "Package signature validated!") diff --git a/src/test/e2e/12_lint_test.go b/src/test/e2e/12_lint_test.go index 58e19536ce..dea06dd678 100644 --- a/src/test/e2e/12_lint_test.go +++ b/src/test/e2e/12_lint_test.go @@ -46,14 +46,14 @@ func TestLint(t *testing.T) { // Testing import / compose + variables are working require.Contains(t, strippedStderr, ".components.[2].images.[3] | Image not pinned with digest - busybox:latest") // Testing OCI imports get linted - require.Contains(t, strippedStderr, ".components.[0].images.[0] | Image not pinned with digest - defenseunicorns/zarf-game:multi-tile-dark") + require.Contains(t, strippedStderr, ".components.[0].images.[0] | Image not pinned with digest - ghcr.io/zarf-dev/doom-game:0.0.1") // Check flavors require.NotContains(t, strippedStderr, "image-in-bad-flavor-component:unpinned") require.Contains(t, strippedStderr, "image-in-good-flavor-component:unpinned") // Check reported filepaths - require.Contains(t, strippedStderr, "Linting package \"dos-games\" at oci://🦄/dos-games:1.0.0") + require.Contains(t, strippedStderr, "Linting package \"dos-games\" at oci://ghcr.io/zarf-dev/packages/dos-games:1.1.0") require.Contains(t, strippedStderr, fmt.Sprintf("Linting package \"lint\" at %s", testPackagePath)) }) } diff --git a/src/test/e2e/20_zarf_init_test.go b/src/test/e2e/20_zarf_init_test.go index 6f788ef3b1..c94bfa4226 100644 --- a/src/test/e2e/20_zarf_init_test.go +++ b/src/test/e2e/20_zarf_init_test.go @@ -44,7 +44,7 @@ func TestZarfInit(t *testing.T) { // We need to use the --architecture flag here to force zarf to find the package. _, stdErr, err = e2e.Zarf(t, "init", "--architecture", mismatchedArch, "--components=k3s", "--confirm") require.Error(t, err, stdErr) - require.Contains(t, stdErr, expectedErrorMessage) + require.Contains(t, e2e.StripMessageFormatting(stdErr), expectedErrorMessage) } if !e2e.ApplianceMode { diff --git a/src/test/e2e/21_connect_creds_test.go b/src/test/e2e/21_connect_creds_test.go index e794de3f3e..c58244a736 100644 --- a/src/test/e2e/21_connect_creds_test.go +++ b/src/test/e2e/21_connect_creds_test.go @@ -23,11 +23,16 @@ type RegistryResponse struct { func TestConnectAndCreds(t *testing.T) { t.Log("E2E: Connect") + ctx := context.Background() prevAgentSecretData, _, err := e2e.Kubectl(t, "get", "secret", "agent-hook-tls", "-n", "zarf", "-o", "jsonpath={.data}") require.NoError(t, err) - ctx := context.Background() + c, err := cluster.NewCluster() + require.NoError(t, err) + // Init the state variable + oldState, err := c.LoadZarfState(ctx) + require.NoError(t, err) connectToZarfServices(ctx, t) @@ -36,7 +41,11 @@ func TestConnectAndCreds(t *testing.T) { newAgentSecretData, _, err := e2e.Kubectl(t, "get", "secret", "agent-hook-tls", "-n", "zarf", "-o", "jsonpath={.data}") require.NoError(t, err) - require.NotEqual(t, prevAgentSecretData, newAgentSecretData, "agent secrets should not be the same") + newState, err := c.LoadZarfState(ctx) + require.NoError(t, err) + require.NotEqual(t, prevAgentSecretData, newAgentSecretData) + require.NotEqual(t, oldState.ArtifactServer.PushToken, newState.ArtifactServer.PushToken) + require.NotEqual(t, oldState.GitServer.PushPassword, newState.GitServer.PushPassword) connectToZarfServices(ctx, t) } diff --git a/src/test/e2e/25_helm_test.go b/src/test/e2e/25_helm_test.go index 322d3c89dd..e1814d4b1e 100644 --- a/src/test/e2e/25_helm_test.go +++ b/src/test/e2e/25_helm_test.go @@ -52,7 +52,6 @@ func testHelmChartsExample(t *testing.T) { stdOut, stdErr, err = e2e.Zarf(t, "package", "create", evilChartLookupPath, "--tmpdir", tmpdir, "--confirm") require.Error(t, err, stdOut, stdErr) require.Contains(t, e2e.StripMessageFormatting(stdErr), "chart \"asdf\" version \"6.4.0\" not found") - require.Contains(t, e2e.StripMessageFormatting(stdErr), "Available charts and versions from \"https://stefanprodan.github.io/podinfo\":") // Create a test package (with a registry override (host+subpath to host+subpath) to test that as well) stdOut, stdErr, err = e2e.Zarf(t, "package", "create", "examples/helm-charts", "-o", "build", "--registry-override", "ghcr.io/stefanprodan=docker.io/stefanprodan", "--tmpdir", tmpdir, "--confirm") @@ -115,7 +114,7 @@ func testHelmEscaping(t *testing.T) { func testHelmUninstallRollback(t *testing.T) { t.Log("E2E: Helm Uninstall and Rollback") - goodPath := fmt.Sprintf("build/zarf-package-dos-games-%s-1.0.0.tar.zst", e2e.Arch) + goodPath := fmt.Sprintf("build/zarf-package-dos-games-%s-1.1.0.tar.zst", e2e.Arch) evilPath := fmt.Sprintf("zarf-package-dos-games-%s.tar.zst", e2e.Arch) // Create the evil package (with the bad service). @@ -172,7 +171,7 @@ func testHelmUninstallRollback(t *testing.T) { func testHelmAdoption(t *testing.T) { t.Log("E2E: Helm Adopt a Deployment") - packagePath := fmt.Sprintf("build/zarf-package-dos-games-%s-1.0.0.tar.zst", e2e.Arch) + packagePath := fmt.Sprintf("build/zarf-package-dos-games-%s-1.1.0.tar.zst", e2e.Arch) deploymentManifest := "src/test/packages/25-manifest-adoption/deployment.yaml" // Deploy dos-games manually into the cluster without Zarf diff --git a/src/test/e2e/26_simple_packages_test.go b/src/test/e2e/26_simple_packages_test.go index 6e4c20bd8a..08df1d709b 100644 --- a/src/test/e2e/26_simple_packages_test.go +++ b/src/test/e2e/26_simple_packages_test.go @@ -18,7 +18,7 @@ import ( func TestDosGames(t *testing.T) { t.Log("E2E: Dos games") - path := filepath.Join("build", fmt.Sprintf("zarf-package-dos-games-%s-1.0.0.tar.zst", e2e.Arch)) + path := filepath.Join("build", fmt.Sprintf("zarf-package-dos-games-%s-1.1.0.tar.zst", e2e.Arch)) // Deploy the game stdOut, stdErr, err := e2e.Zarf(t, "package", "deploy", path, "--confirm") diff --git a/src/test/e2e/27_deploy_regression_test.go b/src/test/e2e/27_deploy_regression_test.go index f16c3d243a..a4ba8e014f 100644 --- a/src/test/e2e/27_deploy_regression_test.go +++ b/src/test/e2e/27_deploy_regression_test.go @@ -9,7 +9,6 @@ import ( "testing" "github.com/stretchr/testify/require" - "github.com/zarf-dev/zarf/src/pkg/utils/exec" ) func TestGHCRDeploy(t *testing.T) { @@ -19,26 +18,13 @@ func TestGHCRDeploy(t *testing.T) { // shas for package published 2023-08-08T22:13:51Z switch e2e.Arch { case "arm64": - sha = "ac7d7684ca9b4edb061a7732aefc17cfb7b7c983fec23e1fe319cf535618a8b6" + sha = "844dff9aa60345c67b597d5315db5e263cbda01b50643a8d0b7f5ec721f8a16f" case "amd64": - sha = "aca4d4cf24532d69a8941a446067fc3d8474581507236b37bb7188836d93bf89" + sha = "a44d17160cd6ce7b7b6d4687e7d3f75dad4fedba6670c79665af2e8665a7868e" } // Test with command from https://docs.zarf.dev/getting-started/install/ - stdOut, stdErr, err := e2e.Zarf(t, "package", "deploy", fmt.Sprintf("oci://🦄/dos-games:1.0.0-%s@sha256:%s", e2e.Arch, sha), "--key=https://zarf.dev/cosign.pub", "--confirm") - require.NoError(t, err, stdOut, stdErr) - - stdOut, stdErr, err = e2e.Zarf(t, "package", "remove", "dos-games", "--confirm") - require.NoError(t, err, stdOut, stdErr) -} - -func TestCosignDeploy(t *testing.T) { - t.Log("E2E: Cosign deploy") - - // Test with command from https://docs.zarf.dev/getting-started/install/ - command := fmt.Sprintf("%s package deploy sget://defenseunicorns/zarf-hello-world:$(uname -m) --confirm", e2e.ZarfBinPath) - - stdOut, stdErr, err := exec.CmdWithTesting(t, exec.PrintCfg(), "sh", "-c", command) + stdOut, stdErr, err := e2e.Zarf(t, "package", "deploy", fmt.Sprintf("oci://ghcr.io/zarf-dev/packages/dos-games:1.1.0@sha256:%s", sha), "--key=https://zarf.dev/cosign.pub", "--confirm") require.NoError(t, err, stdOut, stdErr) stdOut, stdErr, err = e2e.Zarf(t, "package", "remove", "dos-games", "--confirm") diff --git a/src/test/e2e/31_checksum_and_signature_test.go b/src/test/e2e/31_checksum_and_signature_test.go index e8aed4c1cb..c83888fe00 100644 --- a/src/test/e2e/31_checksum_and_signature_test.go +++ b/src/test/e2e/31_checksum_and_signature_test.go @@ -15,7 +15,7 @@ func TestChecksumAndSignature(t *testing.T) { t.Log("E2E: Checksum and Signature") testPackageDirPath := "examples/dos-games" - pkgName := fmt.Sprintf("zarf-package-dos-games-%s-1.0.0.tar.zst", e2e.Arch) + pkgName := fmt.Sprintf("zarf-package-dos-games-%s-1.1.0.tar.zst", e2e.Arch) privateKeyFlag := "--signing-key=src/test/packages/zarf-test.prv-key" publicKeyFlag := "--key=src/test/packages/zarf-test.pub" @@ -37,7 +37,7 @@ func TestChecksumAndSignature(t *testing.T) { // Test that we get an error when trying to deploy a package without providing the public key stdOut, stdErr, err = e2e.Zarf(t, "package", "deploy", pkgName, "--confirm") require.Error(t, err, stdOut, stdErr) - require.Contains(t, stdErr, "failed to deploy package: unable to load the package: package is signed but no key was provided - add a key with the --key flag or use the --insecure flag and run the command again") + require.Contains(t, e2e.StripMessageFormatting(stdErr), "failed to deploy package: unable to load the package: package is signed but no key was provided - add a key with the --key flag or use the --insecure flag and run the command again") // Test that we don't get an error when we remember to provide the public key stdOut, stdErr, err = e2e.Zarf(t, "package", "deploy", pkgName, publicKeyFlag, "--confirm") diff --git a/src/test/e2e/32_component_webhooks_test.go b/src/test/e2e/32_component_webhooks_test.go index 7d6d761835..18c9ed33cc 100644 --- a/src/test/e2e/32_component_webhooks_test.go +++ b/src/test/e2e/32_component_webhooks_test.go @@ -23,7 +23,7 @@ func TestComponentWebhooks(t *testing.T) { defer e2e.CleanFiles(webhookPath) // Ensure package deployments wait for webhooks to complete. - gamesPath := fmt.Sprintf("build/zarf-package-dos-games-%s-1.0.0.tar.zst", e2e.Arch) + gamesPath := fmt.Sprintf("build/zarf-package-dos-games-%s-1.1.0.tar.zst", e2e.Arch) stdOut, stdErr, err = e2e.Zarf(t, "package", "deploy", gamesPath, "--confirm") require.NoError(t, err, stdOut, stdErr) require.Contains(t, stdErr, "Waiting for webhook \"test-webhook\" to complete for component \"baseline\"") diff --git a/src/test/e2e/34_custom_init_package_test.go b/src/test/e2e/34_custom_init_package_test.go index c61cf91e2d..e4d3307fc4 100644 --- a/src/test/e2e/34_custom_init_package_test.go +++ b/src/test/e2e/34_custom_init_package_test.go @@ -38,7 +38,7 @@ func TestCustomInit(t *testing.T) { // Test that we get an error when trying to deploy a package without providing the public key stdOut, stdErr, err = e2e.Zarf(t, "init", "--confirm") require.Error(t, err, stdOut, stdErr) - require.Contains(t, stdErr, "unable to load the package: package is signed but no key was provided - add a key with the --key flag or use the --insecure flag and run the command again") + require.Contains(t, e2e.StripMessageFormatting(stdErr), "unable to load the package: package is signed but no key was provided - add a key with the --key flag or use the --insecure flag and run the command again") /* Test operations during package deploy */ // Test that we can deploy the package with the public key diff --git a/src/test/packages/12-lint/linted-import/zarf.yaml b/src/test/packages/12-lint/linted-import/zarf.yaml index 04fdcd47f2..d4a190f9e6 100644 --- a/src/test/packages/12-lint/linted-import/zarf.yaml +++ b/src/test/packages/12-lint/linted-import/zarf.yaml @@ -19,5 +19,5 @@ components: - name: oci-games-url import: - url: oci://🦄/dos-games:1.0.0 + url: oci://ghcr.io/zarf-dev/packages/dos-games:1.1.0 name: baseline diff --git a/src/test/packages/12-lint/zarf.yaml b/src/test/packages/12-lint/zarf.yaml index c4c80ba6ea..8d4d9cd1d1 100644 --- a/src/test/packages/12-lint/zarf.yaml +++ b/src/test/packages/12-lint/zarf.yaml @@ -32,7 +32,7 @@ components: - name: oci-games-url import: - url: oci://🦄/dos-games:1.0.0 + url: oci://ghcr.io/zarf-dev/packages/dos-games:1.1.0 name: baseline - name: oci-games-url diff --git a/src/types/k8s.go b/src/types/k8s.go index 84ad8d7994..696be20b79 100644 --- a/src/types/k8s.go +++ b/src/types/k8s.go @@ -119,8 +119,9 @@ type Webhook struct { // InstalledChart contains information about a Helm Chart that has been deployed to a cluster. type InstalledChart struct { - Namespace string `json:"namespace"` - ChartName string `json:"chartName"` + Namespace string `json:"namespace"` + ChartName string `json:"chartName"` + ConnectStrings ConnectStrings `json:"connectStrings,omitempty"` } // GitServerInfo contains information Zarf uses to communicate with a git repository to push/pull repositories to. diff --git a/zarf-config.toml b/zarf-config.toml index 09afb3eed3..70f2ac615a 100644 --- a/zarf-config.toml +++ b/zarf-config.toml @@ -5,9 +5,9 @@ agent_image = 'zarf-dev/zarf/agent' agent_image_tag = 'local' # Tag for the zarf injector binary to use -injector_version = '2024-05-15' -injector_amd64_shasum = '1b34519ac30daf0e5a4a2f0a0766dbcd0852c0b5364b35576eea4ac9e22d9e82' -injector_arm64_shasum = 'ca20f427f9cf91ff42646a785c4772be5892a6752fa14924c5085b2d0109b008' +injector_version = '2024-07-22' +injector_amd64_shasum = '8463bfd66930a4b26c665b51f25e8a32ed5948068bae49987013c89173394478' +injector_arm64_shasum = 'b905e647e0d7876cfd5b665632cfc43ad919dc60408f7236c5b541c53277b503' # The image reference to use for the registry that Zarf deploys into the cluster