Compare commits

...

58 Commits

Author SHA1 Message Date
Stefan Prodan 0c36aa0904
Merge pull request #1508 from fluxcd/external-artifact
[RFC-0012] Add support for `ExternalArtifact` source type
2025-09-06 09:46:08 +03:00
Stefan Prodan 5329f56881
Add ExternalArtifact feature gate
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-09-05 23:45:26 +03:00
Stefan Prodan 049a805992
Implement ExternalArtifact reconciliation
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-09-05 00:02:43 +03:00
Stefan Prodan 3d6179cc99
Import ExternalArtifact CRD
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-09-04 21:31:21 +03:00
Stefan Prodan 2e1ef03389
Merge pull request #1507 from fluxcd/ignore-components
Add `.spec.ignoreMissingComponents` field to Kustomization API
2025-09-04 20:55:59 +03:00
Stefan Prodan afdc58cd48
Add `.spec.ignoreMissingComponents` field to Kustomization API
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-09-04 20:12:13 +03:00
Stefan Prodan 9c41b0649d
Merge pull request #1505 from fluxcd/otel-v1.38.0
Update otel packages to v1.38.0
2025-09-03 20:00:35 +03:00
Stefan Prodan 493fbfee62
Update otel packages to v1.38.0
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-09-03 18:41:50 +03:00
Stefan Prodan f83bc52441
Merge pull request #1492 from fluxcd/dependabot/go_modules/github.com/docker/docker-28.3.3incompatible
Bump github.com/docker/docker from 28.2.2+incompatible to 28.3.3+incompatible
2025-09-02 19:15:17 +03:00
dependabot[bot] 4dc270e06c
Bump github.com/docker/docker
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 28.2.2+incompatible to 28.3.3+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v28.2.2...v28.3.3)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-version: 28.3.3+incompatible
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-02 15:58:33 +00:00
Stefan Prodan ca604f3389
Merge pull request #1502 from fluxcd/status-history
Track reconciliation attempts over time in `.status.history`
2025-09-01 10:46:49 +03:00
Stefan Prodan cf3e7b7204
Add `.status.history` to the API docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-09-01 10:35:42 +03:00
Stefan Prodan e722bbbf50
Implement reconciliation history tracking
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-09-01 10:35:39 +03:00
Stefan Prodan f133b50872
api: Add reconciliation history to Kustomization status
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-09-01 10:35:38 +03:00
Stefan Prodan 6b2e5e8729
Merge pull request #1504 from fluxcd/dependabot/github_actions/ci-1dcc4b39f8
Bump the ci group across 1 directory with 11 updates
2025-09-01 09:31:03 +03:00
dependabot[bot] 0f1d6864e9
Bump the ci group across 1 directory with 11 updates
Bumps the ci group with 11 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [actions/checkout](https://github.com/actions/checkout) | `4.2.2` | `5.0.0` |
| [korthout/backport-action](https://github.com/korthout/backport-action) | `3.2.0` | `3.3.0` |
| [docker/setup-buildx-action](https://github.com/docker/setup-buildx-action) | `3.10.0` | `3.11.1` |
| [actions/cache](https://github.com/actions/cache) | `4.2.3` | `4.2.4` |
| [docker/build-push-action](https://github.com/docker/build-push-action) | `6.17.0` | `6.18.0` |
| [docker/login-action](https://github.com/docker/login-action) | `3.4.0` | `3.5.0` |
| [docker/metadata-action](https://github.com/docker/metadata-action) | `5.7.0` | `5.8.0` |
| [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) | `3.8.2` | `3.9.2` |
| [anchore/sbom-action](https://github.com/anchore/sbom-action) | `0.20.0` | `0.20.5` |
| [goreleaser/goreleaser-action](https://github.com/goreleaser/goreleaser-action) | `6.3.0` | `6.4.0` |
| [github/codeql-action](https://github.com/github/codeql-action) | `3.28.18` | `3.29.11` |



Updates `actions/checkout` from 4.2.2 to 5.0.0
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](11bd71901b...08c6903cd8)

Updates `korthout/backport-action` from 3.2.0 to 3.3.0
- [Release notes](https://github.com/korthout/backport-action/releases)
- [Commits](436145e922...ca4972adce)

Updates `docker/setup-buildx-action` from 3.10.0 to 3.11.1
- [Release notes](https://github.com/docker/setup-buildx-action/releases)
- [Commits](b5ca514318...e468171a9d)

Updates `actions/cache` from 4.2.3 to 4.2.4
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](5a3ec84eff...0400d5f644)

Updates `docker/build-push-action` from 6.17.0 to 6.18.0
- [Release notes](https://github.com/docker/build-push-action/releases)
- [Commits](1dc7386353...263435318d)

Updates `docker/login-action` from 3.4.0 to 3.5.0
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](74a5d14239...184bdaa072)

Updates `docker/metadata-action` from 5.7.0 to 5.8.0
- [Release notes](https://github.com/docker/metadata-action/releases)
- [Commits](902fa8ec7d...c1e51972af)

Updates `sigstore/cosign-installer` from 3.8.2 to 3.9.2
- [Release notes](https://github.com/sigstore/cosign-installer/releases)
- [Commits](3454372f43...d58896d6a1)

Updates `anchore/sbom-action` from 0.20.0 to 0.20.5
- [Release notes](https://github.com/anchore/sbom-action/releases)
- [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md)
- [Commits](e11c554f70...da167eac91)

Updates `goreleaser/goreleaser-action` from 6.3.0 to 6.4.0
- [Release notes](https://github.com/goreleaser/goreleaser-action/releases)
- [Commits](9c156ee8a1...e435ccd777)

Updates `github/codeql-action` from 3.28.18 to 3.29.11
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](ff0a06e83c...3c3833e0f8)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: ci
- dependency-name: korthout/backport-action
  dependency-version: 3.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: ci
- dependency-name: docker/setup-buildx-action
  dependency-version: 3.11.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: ci
- dependency-name: actions/cache
  dependency-version: 4.2.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: ci
- dependency-name: docker/build-push-action
  dependency-version: 6.18.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: ci
- dependency-name: docker/login-action
  dependency-version: 3.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: ci
- dependency-name: docker/metadata-action
  dependency-version: 5.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: ci
- dependency-name: sigstore/cosign-installer
  dependency-version: 3.9.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: ci
- dependency-name: anchore/sbom-action
  dependency-version: 0.20.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: ci
- dependency-name: goreleaser/goreleaser-action
  dependency-version: 6.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: ci
- dependency-name: github/codeql-action
  dependency-version: 3.29.11
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: ci
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-01 06:14:24 +00:00
Stefan Prodan 2e9eb7c9fd
Merge pull request #1501 from fluxcd/k8s-1.34
Update to Kubernetes v1.34.0 and Go 1.25.0
2025-08-30 16:24:32 +03:00
Stefan Prodan 7a399d5ec7
Build with Go 1.25
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-08-30 15:52:33 +03:00
Stefan Prodan 177f19c460
Regenerate CRDs with controller-gen v0.19
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-08-30 15:49:51 +03:00
Stefan Prodan 57eaa2ea00
Update dependencies to Kubernetes v1.34.0
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-08-30 15:46:49 +03:00
Matheus Pimenta 44cddcb72d
Merge pull request #1497 from cappyzawa/feat/object-level-config-validation
Add object-level configuration validation
2025-08-17 17:13:32 +01:00
cappyzawa eacc2a05a8
Add object-level configuration validation
Validates that ObjectLevelWorkloadIdentity feature gate is enabled
when default service account flags are set. This prevents
misconfiguration where lockdown flags are used without enabling
the required feature gate.

Signed-off-by: cappyzawa <cappyzawa@gmail.com>
2025-08-18 00:44:51 +09:00
Matheus Pimenta e7aaaf2a3e
Merge pull request #1495 from cappyzawa/rfc-0010-multi-tenancy-lockdown
[RFC-0010] Add multi-tenancy lockdown for decryption and kubeconfig
2025-08-17 09:29:22 +01:00
cappyzawa c5f0efdced
[RFC-0010] Add multi-tenancy lockdown for decryption and kubeconfig
Adds two new controller flags to enforce ServiceAccount usage in
multi-tenant clusters where administrators need to lock down workload
identity access:

- --default-decryption-service-account
- --default-kubeconfig-service-account

These flags complement the existing --default-service-account flag to
provide complete multi-tenancy lockdown coverage for all three classes
of ServiceAccount fields in the Kustomization API.

Signed-off-by: cappyzawa <cappyzawa@gmail.com>
2025-08-17 17:11:50 +09:00
Stefan Prodan aaad1e033e
Merge pull request #1494 from fluxcd/remove-v1beta1-api
Remove deprecated APIs in group `kustomize.toolkit.fluxcd.io/v1beta1`
2025-08-11 17:54:18 +03:00
Stefan Prodan c8b9d82b71
Remove deprecated APIs in group `kustomize.toolkit.fluxcd.io/v1beta1`
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-08-08 14:43:29 +03:00
Stefan Prodan 60b8f86f49
Merge pull request #1491 from fluxcd/feat-dependon-cel
Extend the readiness evaluation of dependencies with CEL expressions
2025-07-29 08:28:26 +03:00
Stefan Prodan fd63b520d5
controller: Move manager to a dedicated file
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-07-22 20:43:14 +03:00
Stefan Prodan d17e5d2514
docs: Add dependency ready expression to API docs
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-07-22 20:24:03 +03:00
Stefan Prodan e0e6e22272
controller: Implement CEL evaluation for dependency checks
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-07-22 20:23:58 +03:00
Stefan Prodan c2754dd5de
controller: Add `AdditiveCELDependencyCheck` feature gate
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-07-22 12:57:52 +03:00
Stefan Prodan 5edcf5b394
api: Add the `readyExpr` field to `dependsOn`
Extend the readiness evaluation of dependencies with CEL expressions

Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-07-22 12:57:08 +03:00
Matheus Pimenta 8ec3ec388d
Merge pull request #1488 from fluxcd/restconfig-azure-cloud
[RFC-0010] Support all Azure clouds for remote clusters at the controller level
2025-07-18 08:25:47 +01:00
Matheus Pimenta 0c48a16ede
[RFC-0010] Support all Azure clouds for remote clusters at the controller level
Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
2025-07-18 08:10:19 +01:00
Matheus Pimenta c48ffbef1c
Merge pull request #1486 from fluxcd/watch-label
Introduce label selector for watching ConfigMaps and Secrets
2025-07-17 10:40:05 +01:00
Matheus Pimenta 2dcec193bb
Introduce label selector for watching ConfigMaps and Secrets
Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
2025-07-17 10:29:26 +01:00
Stefan Prodan b4d23e756f
Merge pull request #1487 from fluxcd/fix-substitute-doc
docs: Clarify `substituteFrom` overwrite order
2025-07-17 09:34:20 +03:00
Stefan Prodan 2fb1fa3890
docs: Clarify `substituteFrom` overwrite order
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-07-17 09:00:56 +03:00
Matheus Pimenta ba3c16aa45
Merge pull request #1476 from fluxcd/restconfig
[RFC-0010] Introduce workload identity auth for remote clusters
2025-07-14 18:45:25 +01:00
Matheus Pimenta 2fb1482f71
[RFC-0010] Introduce workload identity auth for remote clusters
Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
2025-07-14 16:47:26 +01:00
Stefan Prodan 52170876d5
Merge pull request #1484 from fluxcd/ssa-staged
Refactor reconciler to use `ssa.ApplyAllStaged`
2025-07-08 20:25:06 +03:00
Stefan Prodan 1476042b1e
Refactor reconciler to use `ssa.ApplyAllStaged`
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-07-08 19:42:12 +03:00
Matheus Pimenta f479c3e1f6
Merge pull request #1483 from fluxcd/pick-changelog-v1.6.1
Add changelog entry for v1.6.1
2025-07-08 10:14:48 +01:00
Matheus Pimenta bc4756a38f
Add changelog entry for v1.6.1
Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
(cherry picked from commit 2333a7413c)
2025-07-08 10:02:19 +01:00
Matheus Pimenta 5703d47395
Merge pull request #1481 from fluxcd/global-secret-based-decryption
Introduce global decryption for SOPS age keys
2025-07-07 11:25:31 +01:00
Matheus Pimenta 30275f2615
Introduce global decryption for SOPS age keys
Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
2025-07-06 19:24:22 +01:00
Matheus Pimenta a342d00602
Merge pull request #1478 from fluxcd/fix-1477
Fix regression in STS endpoint for SOPS decryption with AWS KMS in US Gov partition
2025-07-03 17:53:50 +01:00
Matheus Pimenta 4623a38989
Fix regression in STS endpoint for SOPS decryption with AWS KMS in US Gov partition
Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
2025-07-03 17:39:11 +01:00
Matheus Pimenta 124402b53a
Merge pull request #1472 from dgunzy/bump-ssa-v0.49.0
Bump pkg/ssa to v0.49.0 for CABundle validation fix
2025-06-29 16:56:18 +01:00
Daniel Guns 8e90ab8008
Bump pkg/ssa to v0.49.0 for CABundle validation fix
Includes fix for #800: Remove CABundle from CRDs if cert is invalid

Signed-off-by: Daniel Guns <danbguns@gmail.com>
2025-06-29 12:39:09 -03:00
Matheus Pimenta 8c1d87ba6a
Merge pull request #1470 from cappyzawa/update-kustomize-v5.7.0
Update kustomize to v5.7.0 and add regression test for multiple patch delete
2025-06-29 16:01:53 +01:00
cappyzawa 8479377cd7
Add regression test for multiple patch delete in strategic merge patches
This test ensures that the controller properly handles multiple
$patch: delete directives in strategic merge patches, which was
causing panic errors in kustomize v5.6.0 but is now fixed in v5.7.0.

The test includes two scenarios:
- Multiple delete directives in a single patch (the main fix)
- Multiple delete directives in separate patches (previously a workaround)

Signed-off-by: cappyzawa <cappyzawa@gmail.com>
2025-06-29 15:38:51 +09:00
cappyzawa ebee7880e7
Update kustomize dependency from v5.6.0 to v5.7.0
The v5.7.0 release fixes panic issues that occur when using multiple
$patch: delete directives in a single strategic merge patch. This was
causing kustomize build to fail with nil pointer dereference errors.

This change updates:
- sigs.k8s.io/kustomize/api from v0.19.0 to v0.20.0
- sigs.k8s.io/kustomize/kyaml from v0.19.0 to v0.20.0
- sigs.k8s.io/yaml from v1.4.0 to v1.5.0 (dependency update)

Signed-off-by: cappyzawa <cappyzawa@gmail.com>
2025-06-29 15:14:05 +09:00
Stefan Prodan 9f784c5e9f
Merge pull request #1463 from fluxcd/dependabot-up
Update dependabot config
2025-05-28 16:46:19 +03:00
Stefan Prodan 07837f603c
Update dependabot config
Signed-off-by: Stefan Prodan <stefan.prodan@gmail.com>
2025-05-28 16:35:40 +03:00
Matheus Pimenta ee8981f35d
Merge pull request #1462 from fluxcd/update-labels
Add 1.6.x release label
2025-05-28 14:32:06 +01:00
Matheus Pimenta 40a75725dd
Add 1.6.x release label
Signed-off-by: Matheus Pimenta <matheuscscp@gmail.com>
2025-05-28 14:22:47 +01:00
Matheus Pimenta f1cfd9f0c8
Merge pull request #1461 from fluxcd/release/v1.6.x
Release/v1.6.x
2025-05-28 14:20:21 +01:00
68 changed files with 2851 additions and 1534 deletions

View File

@ -5,7 +5,7 @@ updates:
directory: "/" directory: "/"
labels: ["dependencies"] labels: ["dependencies"]
schedule: schedule:
interval: "daily" interval: "monthly"
groups: groups:
go-deps: go-deps:
patterns: patterns:
@ -31,4 +31,4 @@ updates:
patterns: patterns:
- "*" - "*"
schedule: schedule:
interval: "daily" interval: "monthly"

3
.github/labels.yaml vendored
View File

@ -35,3 +35,6 @@
- name: backport:release/v1.5.x - name: backport:release/v1.5.x
description: To be backported to release/v1.5.x description: To be backported to release/v1.5.x
color: '#ffd700' color: '#ffd700'
- name: backport:release/v1.6.x
description: To be backported to release/v1.6.x
color: '#ffd700'

View File

@ -16,11 +16,11 @@ jobs:
if: github.event.pull_request.state == 'closed' && github.event.pull_request.merged && (github.event_name != 'labeled' || startsWith('backport:', github.event.label.name)) if: github.event.pull_request.state == 'closed' && github.event.pull_request.merged && (github.event_name != 'labeled' || startsWith('backport:', github.event.label.name))
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with: with:
ref: ${{ github.event.pull_request.head.sha }} ref: ${{ github.event.pull_request.head.sha }}
- name: Create backport PRs - name: Create backport PRs
uses: korthout/backport-action@436145e922f9561fc5ea157ff406f21af2d6b363 # v3.2.0 uses: korthout/backport-action@ca4972adce8039ff995e618f5fc02d1b7961f27a # v3.3.0
# xref: https://github.com/korthout/backport-action#inputs # xref: https://github.com/korthout/backport-action#inputs
with: with:
# Use token to allow workflows to be triggered for the created PR # Use token to allow workflows to be triggered for the created PR

View File

@ -12,11 +12,11 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup Go - name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with: with:
go-version: 1.24.x go-version: 1.25.x
cache-dependency-path: | cache-dependency-path: |
**/go.sum **/go.sum
**/go.mod **/go.mod

View File

@ -15,14 +15,14 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup QEMU - name: Setup QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Setup Docker Buildx - name: Setup Docker Buildx
id: buildx id: buildx
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Cache Docker layers - name: Cache Docker layers
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
id: cache id: cache
with: with:
path: /tmp/.buildx-cache path: /tmp/.buildx-cache
@ -32,7 +32,7 @@ jobs:
- name: Setup Go - name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with: with:
go-version: 1.24.x go-version: 1.25.x
cache-dependency-path: | cache-dependency-path: |
**/go.sum **/go.sum
**/go.mod **/go.mod

View File

@ -15,16 +15,16 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup QEMU - name: Setup QEMU
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Setup Docker Buildx - name: Setup Docker Buildx
id: buildx id: buildx
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
with: with:
buildkitd-flags: "--debug" buildkitd-flags: "--debug"
- name: Build multi-arch container image - name: Build multi-arch container image
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0 uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with: with:
push: false push: false
builder: ${{ steps.buildx.outputs.name }} builder: ${{ steps.buildx.outputs.name }}

View File

@ -29,7 +29,7 @@ jobs:
packages: write # for pushing and signing container images. packages: write # for pushing and signing container images.
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup Kustomize - name: Setup Kustomize
uses: fluxcd/pkg/actions/kustomize@main uses: fluxcd/pkg/actions/kustomize@main
- name: Prepare - name: Prepare
@ -45,21 +45,21 @@ jobs:
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0 uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
- name: Setup Docker Buildx - name: Setup Docker Buildx
id: buildx id: buildx
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with: with:
registry: ghcr.io registry: ghcr.io
username: fluxcdbot username: fluxcdbot
password: ${{ secrets.GHCR_TOKEN }} password: ${{ secrets.GHCR_TOKEN }}
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 # v3.5.0
with: with:
username: fluxcdbot username: fluxcdbot
password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }} password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }}
- name: Generate images meta - name: Generate images meta
id: meta id: meta
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0 uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
with: with:
images: | images: |
fluxcd/${{ env.CONTROLLER }} fluxcd/${{ env.CONTROLLER }}
@ -68,7 +68,7 @@ jobs:
type=raw,value=${{ steps.prep.outputs.VERSION }} type=raw,value=${{ steps.prep.outputs.VERSION }}
- name: Publish images - name: Publish images
id: build-push id: build-push
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0 uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with: with:
sbom: true sbom: true
provenance: true provenance: true
@ -79,7 +79,7 @@ jobs:
platforms: linux/amd64,linux/arm/v7,linux/arm64 platforms: linux/amd64,linux/arm/v7,linux/arm64
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
- uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2 - uses: sigstore/cosign-installer@d58896d6a1865668819e1d91763c7751a165e159 # v3.9.2
- name: Sign images - name: Sign images
env: env:
COSIGN_EXPERIMENTAL: 1 COSIGN_EXPERIMENTAL: 1
@ -92,11 +92,11 @@ jobs:
mkdir -p config/release mkdir -p config/release
kustomize build ./config/crd > ./config/release/${{ env.CONTROLLER }}.crds.yaml kustomize build ./config/crd > ./config/release/${{ env.CONTROLLER }}.crds.yaml
kustomize build ./config/manager > ./config/release/${{ env.CONTROLLER }}.deployment.yaml kustomize build ./config/manager > ./config/release/${{ env.CONTROLLER }}.deployment.yaml
- uses: anchore/sbom-action/download-syft@e11c554f704a0b820cbf8c51673f6945e0731532 # v0.20.0 - uses: anchore/sbom-action/download-syft@da167eac915b4e86f08b264dbdbc867b61be6f0c # v0.20.5
- name: Create release and SBOM - name: Create release and SBOM
id: run-goreleaser id: run-goreleaser
if: startsWith(github.ref, 'refs/tags/v') if: startsWith(github.ref, 'refs/tags/v')
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0 uses: goreleaser/goreleaser-action@e435ccd777264be153ace6237001ef4d979d3a7a # v6.4.0
with: with:
version: latest version: latest
args: release --clean --skip=validate args: release --clean --skip=validate

View File

@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Run FOSSA scan and upload build data - name: Run FOSSA scan and upload build data
uses: fossa-contrib/fossa-action@3d2ef181b1820d6dcd1972f86a767d18167fa19b # v3.0.1 uses: fossa-contrib/fossa-action@3d2ef181b1820d6dcd1972f86a767d18167fa19b # v3.0.1
with: with:
@ -31,22 +31,22 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- name: Setup Go - name: Setup Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
with: with:
go-version: 1.24.x go-version: 1.25.x
cache-dependency-path: | cache-dependency-path: |
**/go.sum **/go.sum
**/go.mod **/go.mod
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 uses: github/codeql-action/init@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
with: with:
languages: go languages: go
# xref: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # xref: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
# xref: https://codeql.github.com/codeql-query-help/go/ # xref: https://codeql.github.com/codeql-query-help/go/
queries: security-and-quality queries: security-and-quality
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 uses: github/codeql-action/autobuild@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 uses: github/codeql-action/analyze@3c3833e0f8c1c83d449a7478aa59c036a9165498 # v3.29.11

View File

@ -17,7 +17,7 @@ jobs:
permissions: permissions:
issues: write issues: write
steps: steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: EndBug/label-sync@52074158190acb45f3077f9099fea818aa43f97a # v2.3.3 - uses: EndBug/label-sync@52074158190acb45f3077f9099fea818aa43f97a # v2.3.3
with: with:
# Configuration file # Configuration file

1
.gitignore vendored
View File

@ -19,6 +19,7 @@ config/release/
config/crd/bases/ocirepositories.yaml config/crd/bases/ocirepositories.yaml
config/crd/bases/gitrepositories.yaml config/crd/bases/gitrepositories.yaml
config/crd/bases/buckets.yaml config/crd/bases/buckets.yaml
config/crd/bases/externalartifacts.yaml
build/ build/

View File

@ -2,6 +2,24 @@
All notable changes to this project are documented in this file. All notable changes to this project are documented in this file.
## 1.6.1
**Release date:** 2025-07-08
This patch release fixes a bug introduced in v1.6.0
that causes SOPS decryption with US Government KMS
keys to fail with the error:
```
STS: AssumeRoleWithWebIdentity, https response error\n StatusCode: 0, RequestID: ,
request send failed, Post\n \"https://sts.arn.amazonaws.com/\": dial tcp:
lookupts.arn.amazonaws.com on 10.100.0.10:53: no such host
```
Fixes:
- Fix regression in STS endpoint for SOPS decryption with AWS KMS in US Gov partition
[#1478](https://github.com/fluxcd/kustomize-controller/pull/1478)
## 1.6.0 ## 1.6.0
**Release date:** 2025-05-28 **Release date:** 2025-05-28

View File

@ -16,7 +16,7 @@ There are a number of dependencies required to be able to run the controller and
## How to run the test suite ## How to run the test suite
Prerequisites: Prerequisites:
* Go >= 1.24 * Go >= 1.25
You can run the test suite by simply doing You can run the test suite by simply doing

View File

@ -1,4 +1,4 @@
ARG GO_VERSION=1.24 ARG GO_VERSION=1.25
ARG XX_VERSION=1.6.1 ARG XX_VERSION=1.6.1
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
@ -30,7 +30,7 @@ COPY internal/ internal/
ENV CGO_ENABLED=0 ENV CGO_ENABLED=0
RUN xx-go build -trimpath -a -o kustomize-controller main.go RUN xx-go build -trimpath -a -o kustomize-controller main.go
FROM alpine:3.21 FROM alpine:3.22
ARG TARGETPLATFORM ARG TARGETPLATFORM

View File

@ -38,13 +38,14 @@ ENVTEST_ARCH ?= amd64
GITREPO_CRD ?= config/crd/bases/gitrepositories.yaml GITREPO_CRD ?= config/crd/bases/gitrepositories.yaml
BUCKET_CRD ?= config/crd/bases/buckets.yaml BUCKET_CRD ?= config/crd/bases/buckets.yaml
OCIREPO_CRD ?= config/crd/bases/ocirepositories.yaml OCIREPO_CRD ?= config/crd/bases/ocirepositories.yaml
EA_CRD ?= config/crd/bases/externalartifacts.yaml
# Keep a record of the version of the downloaded source CRDs. It is used to # Keep a record of the version of the downloaded source CRDs. It is used to
# detect and download new CRDs when the SOURCE_VER changes. # detect and download new CRDs when the SOURCE_VER changes.
SOURCE_CRD_VER=$(BUILD_DIR)/.src-crd-$(SOURCE_VER) SOURCE_CRD_VER=$(BUILD_DIR)/.src-crd-$(SOURCE_VER)
# API (doc) generation utilities # API (doc) generation utilities
CONTROLLER_GEN_VERSION ?= v0.16.1 CONTROLLER_GEN_VERSION ?= v0.19.0
GEN_API_REF_DOCS_VERSION ?= e327d0730470cbd61b06300f81c5fcf91c23c113 GEN_API_REF_DOCS_VERSION ?= e327d0730470cbd61b06300f81c5fcf91c23c113
all: manager all: manager
@ -90,12 +91,15 @@ $(BUCKET_CRD):
$(OCIREPO_CRD): $(OCIREPO_CRD):
curl -s https://raw.githubusercontent.com/fluxcd/source-controller/${SOURCE_VER}/config/crd/bases/source.toolkit.fluxcd.io_ocirepositories.yaml -o $(OCIREPO_CRD) curl -s https://raw.githubusercontent.com/fluxcd/source-controller/${SOURCE_VER}/config/crd/bases/source.toolkit.fluxcd.io_ocirepositories.yaml -o $(OCIREPO_CRD)
$(EA_CRD):
curl -s https://raw.githubusercontent.com/fluxcd/source-controller/${SOURCE_VER}/config/crd/bases/source.toolkit.fluxcd.io_externalartifacts.yaml -o $(EA_CRD)
# Download the CRDs the controller depends on # Download the CRDs the controller depends on
download-crd-deps: $(SOURCE_CRD_VER) $(GITREPO_CRD) $(BUCKET_CRD) $(OCIREPO_CRD) download-crd-deps: $(SOURCE_CRD_VER) $(GITREPO_CRD) $(BUCKET_CRD) $(OCIREPO_CRD) $(EA_CRD)
# Delete the downloaded CRD dependencies. # Delete the downloaded CRD dependencies.
cleanup-crd-deps: cleanup-crd-deps:
rm -f $(GITREPO_CRD) $(BUCKET_CRD) $(OCIREPO_CRD) rm -f $(GITREPO_CRD) $(BUCKET_CRD) $(OCIREPO_CRD) $(EA_CRD)
# Install CRDs into a cluster # Install CRDs into a cluster
install: manifests install: manifests
@ -135,8 +139,8 @@ api-docs: gen-crd-api-reference-docs
# Run go mod tidy # Run go mod tidy
tidy: tidy:
cd api; rm -f go.sum; go mod tidy -compat=1.24 cd api; rm -f go.sum; go mod tidy -compat=1.25
rm -f go.sum; go mod tidy -compat=1.24 rm -f go.sum; go mod tidy -compat=1.25
# Run go fmt against code # Run go fmt against code
fmt: fmt:

View File

@ -1,36 +1,32 @@
module github.com/fluxcd/kustomize-controller/api module github.com/fluxcd/kustomize-controller/api
go 1.24.0 go 1.25.0
require ( require (
github.com/fluxcd/pkg/apis/kustomize v1.10.0 github.com/fluxcd/pkg/apis/kustomize v1.12.0
github.com/fluxcd/pkg/apis/meta v1.12.0 github.com/fluxcd/pkg/apis/meta v1.20.0
k8s.io/apiextensions-apiserver v0.33.0 k8s.io/apiextensions-apiserver v0.34.0
k8s.io/apimachinery v0.33.0 k8s.io/apimachinery v0.34.0
sigs.k8s.io/controller-runtime v0.21.0 sigs.k8s.io/controller-runtime v0.22.0
) )
// Fix CVE-2022-28948
replace gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1
require ( require (
github.com/fxamacker/cbor/v2 v2.8.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/logr v1.4.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/kr/pretty v0.3.1 // indirect github.com/kr/pretty v0.3.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/x448/float16 v0.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect
golang.org/x/net v0.40.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/text v0.25.0 // indirect golang.org/x/net v0.43.0 // indirect
golang.org/x/tools v0.33.0 // indirect golang.org/x/text v0.28.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e // indirect k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect sigs.k8s.io/yaml v1.6.0 // indirect
) )

View File

@ -2,19 +2,18 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fluxcd/pkg/apis/kustomize v1.10.0 h1:47EeSzkQvlQZdH92vHMe2lK2iR8aOSEJq95avw5idts= github.com/fluxcd/pkg/apis/kustomize v1.12.0 h1:KvZN6xwgP/dNSeckL4a/Uv715XqiN1C3xS+jGcPejtE=
github.com/fluxcd/pkg/apis/kustomize v1.10.0/go.mod h1:UsqMV4sqNa1Yg0pmTsdkHRJr7bafBOENIJoAN+3ezaQ= github.com/fluxcd/pkg/apis/kustomize v1.12.0/go.mod h1:OojLxIdKm1JAAdh3sL4j4F+vfrLKb7kq1vr8bpyEKgg=
github.com/fluxcd/pkg/apis/meta v1.12.0 h1:XW15TKZieC2b7MN8VS85stqZJOx+/b8jATQ/xTUhVYg= github.com/fluxcd/pkg/apis/meta v1.20.0 h1:l9h0kWoDZTcYV0WJkFMgDXq6Q4tSojrJ+bHpFJSsaW0=
github.com/fluxcd/pkg/apis/meta v1.12.0/go.mod h1:+son1Va60x2eiDcTwd7lcctbI6C+K3gM7R+ULmEq1SI= github.com/fluxcd/pkg/apis/meta v1.20.0/go.mod h1:XUAEUgT4gkWDAEN79E141tmL+v4SV50tVZ/Ojpc/ueg=
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@ -31,8 +30,9 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
@ -53,6 +53,10 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@ -62,26 +66,26 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -93,24 +97,23 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU= k8s.io/api v0.34.0 h1:L+JtP2wDbEYPUeNGbeSa/5GwFtIA662EmT2YSLOkAVE=
k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM= k8s.io/api v0.34.0/go.mod h1:YzgkIzOOlhl9uwWCZNqpw6RJy9L2FK4dlJeayUoydug=
k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs= k8s.io/apiextensions-apiserver v0.34.0 h1:B3hiB32jV7BcyKcMU5fDaDxk882YrJ1KU+ZSkA9Qxoc=
k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc= k8s.io/apiextensions-apiserver v0.34.0/go.mod h1:hLI4GxE1BDBy9adJKxUxCEHBGZtGfIg98Q+JmTD7+g0=
k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ= k8s.io/apimachinery v0.34.0 h1:eR1WO5fo0HyoQZt1wdISpFDffnWOvFLOOeJ7MgIv4z0=
k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= k8s.io/apimachinery v0.34.0/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e h1:KqK5c/ghOm8xkHYhlodbp6i6+r+ChV2vuAuVRdFbLro= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= sigs.k8s.io/controller-runtime v0.22.0 h1:mTOfibb8Hxwpx3xEkR56i7xSjB+nH4hZG37SrlCY5e0=
sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= sigs.k8s.io/controller-runtime v0.22.0/go.mod h1:FwiwRjkRPbiN+zp2QRp7wlTCzbUXxZ/D4OzuQUDwBHY=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@ -49,11 +49,11 @@ type KustomizationSpec struct {
// +optional // +optional
CommonMetadata *CommonMetadata `json:"commonMetadata,omitempty"` CommonMetadata *CommonMetadata `json:"commonMetadata,omitempty"`
// DependsOn may contain a meta.NamespacedObjectReference slice // DependsOn may contain a DependencyReference slice
// with references to Kustomization resources that must be ready before this // with references to Kustomization resources that must be ready before this
// Kustomization can be reconciled. // Kustomization can be reconciled.
// +optional // +optional
DependsOn []meta.NamespacedObjectReference `json:"dependsOn,omitempty"` DependsOn []DependencyReference `json:"dependsOn,omitempty"`
// Decrypt Kubernetes secrets before applying them on the cluster. // Decrypt Kubernetes secrets before applying them on the cluster.
// +optional // +optional
@ -177,10 +177,16 @@ type KustomizationSpec struct {
// +optional // +optional
Wait bool `json:"wait,omitempty"` Wait bool `json:"wait,omitempty"`
// Components specifies relative paths to specifications of other Components. // Components specifies relative paths to kustomize Components.
// +optional // +optional
Components []string `json:"components,omitempty"` Components []string `json:"components,omitempty"`
// IgnoreMissingComponents instructs the controller to ignore Components paths
// not found in source by removing them from the generated kustomization.yaml
// before running kustomize build.
// +optional
IgnoreMissingComponents bool `json:"ignoreMissingComponents,omitempty"`
// HealthCheckExprs is a list of healthcheck expressions for evaluating the // HealthCheckExprs is a list of healthcheck expressions for evaluating the
// health of custom resources using Common Expression Language (CEL). // health of custom resources using Common Expression Language (CEL).
// The expressions are evaluated only when Wait or HealthChecks are specified. // The expressions are evaluated only when Wait or HealthChecks are specified.
@ -297,6 +303,11 @@ type KustomizationStatus struct {
// have been successfully applied. // have been successfully applied.
// +optional // +optional
Inventory *ResourceInventory `json:"inventory,omitempty"` Inventory *ResourceInventory `json:"inventory,omitempty"`
// History contains a set of snapshots of the last reconciliation attempts
// tracking the revision, the state and the duration of each attempt.
// +optional
History meta.History `json:"history,omitempty"`
} }
// GetTimeout returns the timeout with default. // GetTimeout returns the timeout with default.
@ -333,9 +344,19 @@ func (in Kustomization) GetDeletionPolicy() string {
return in.Spec.DeletionPolicy return in.Spec.DeletionPolicy
} }
// GetDependsOn returns the list of dependencies across-namespaces. // GetDependsOn returns the dependencies as a list of meta.NamespacedObjectReference.
//
// This function makes the Kustomization type conformant with the meta.ObjectWithDependencies interface
// and allows the controller-runtime to index Kustomizations by their dependencies.
func (in Kustomization) GetDependsOn() []meta.NamespacedObjectReference { func (in Kustomization) GetDependsOn() []meta.NamespacedObjectReference {
return in.Spec.DependsOn deps := make([]meta.NamespacedObjectReference, len(in.Spec.DependsOn))
for i := range in.Spec.DependsOn {
deps[i] = meta.NamespacedObjectReference{
Name: in.Spec.DependsOn[i].Name,
Namespace: in.Spec.DependsOn[i].Namespace,
}
}
return deps
} }
// GetConditions returns the status conditions of the object. // GetConditions returns the status conditions of the object.

View File

@ -16,7 +16,9 @@ limitations under the License.
package v1 package v1
import "fmt" import (
"fmt"
)
// CrossNamespaceSourceReference contains enough information to let you locate the // CrossNamespaceSourceReference contains enough information to let you locate the
// typed Kubernetes resource object at cluster level. // typed Kubernetes resource object at cluster level.
@ -26,7 +28,7 @@ type CrossNamespaceSourceReference struct {
APIVersion string `json:"apiVersion,omitempty"` APIVersion string `json:"apiVersion,omitempty"`
// Kind of the referent. // Kind of the referent.
// +kubebuilder:validation:Enum=OCIRepository;GitRepository;Bucket // +kubebuilder:validation:Enum=OCIRepository;GitRepository;Bucket;ExternalArtifact
// +required // +required
Kind string `json:"kind"` Kind string `json:"kind"`
@ -40,9 +42,31 @@ type CrossNamespaceSourceReference struct {
Namespace string `json:"namespace,omitempty"` Namespace string `json:"namespace,omitempty"`
} }
// String returns a string representation of the CrossNamespaceSourceReference
// in the format "Kind/Name" or "Kind/Namespace/Name" if Namespace is set.
func (s *CrossNamespaceSourceReference) String() string { func (s *CrossNamespaceSourceReference) String() string {
if s.Namespace != "" { if s.Namespace != "" {
return fmt.Sprintf("%s/%s/%s", s.Kind, s.Namespace, s.Name) return fmt.Sprintf("%s/%s/%s", s.Kind, s.Namespace, s.Name)
} }
return fmt.Sprintf("%s/%s", s.Kind, s.Name) return fmt.Sprintf("%s/%s", s.Kind, s.Name)
} }
// DependencyReference defines a Kustomization dependency on another Kustomization resource.
type DependencyReference struct {
// Name of the referent.
// +required
Name string `json:"name"`
// Namespace of the referent, defaults to the namespace of the Kustomization
// resource object that contains the reference.
// +optional
Namespace string `json:"namespace,omitempty"`
// ReadyExpr is a CEL expression that can be used to assess the readiness
// of a dependency. When specified, the built-in readiness check
// is replaced by the logic defined in the CEL expression.
// To make the CEL expression additive to the built-in readiness check,
// the feature gate `AdditiveCELDependencyCheck` must be set to `true`.
// +optional
ReadyExpr string `json:"readyExpr,omitempty"`
}

View File

@ -1,7 +1,7 @@
//go:build !ignore_autogenerated //go:build !ignore_autogenerated
/* /*
Copyright 2023 The Flux authors Copyright 2025 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -91,6 +91,21 @@ func (in *Decryption) DeepCopy() *Decryption {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DependencyReference) DeepCopyInto(out *DependencyReference) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DependencyReference.
func (in *DependencyReference) DeepCopy() *DependencyReference {
if in == nil {
return nil
}
out := new(DependencyReference)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Kustomization) DeepCopyInto(out *Kustomization) { func (in *Kustomization) DeepCopyInto(out *Kustomization) {
*out = *in *out = *in
@ -160,7 +175,7 @@ func (in *KustomizationSpec) DeepCopyInto(out *KustomizationSpec) {
} }
if in.DependsOn != nil { if in.DependsOn != nil {
in, out := &in.DependsOn, &out.DependsOn in, out := &in.DependsOn, &out.DependsOn
*out = make([]meta.NamespacedObjectReference, len(*in)) *out = make([]DependencyReference, len(*in))
copy(*out, *in) copy(*out, *in)
} }
if in.Decryption != nil { if in.Decryption != nil {
@ -177,7 +192,7 @@ func (in *KustomizationSpec) DeepCopyInto(out *KustomizationSpec) {
if in.KubeConfig != nil { if in.KubeConfig != nil {
in, out := &in.KubeConfig, &out.KubeConfig in, out := &in.KubeConfig, &out.KubeConfig
*out = new(meta.KubeConfigReference) *out = new(meta.KubeConfigReference)
**out = **in (*in).DeepCopyInto(*out)
} }
if in.PostBuild != nil { if in.PostBuild != nil {
in, out := &in.PostBuild, &out.PostBuild in, out := &in.PostBuild, &out.PostBuild
@ -245,6 +260,13 @@ func (in *KustomizationStatus) DeepCopyInto(out *KustomizationStatus) {
*out = new(ResourceInventory) *out = new(ResourceInventory)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
if in.History != nil {
in, out := &in.History, &out.History
*out = make(meta.History, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
} }
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KustomizationStatus. // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KustomizationStatus.

View File

@ -15,6 +15,9 @@ limitations under the License.
*/ */
// Package v1beta1 contains API Schema definitions for the kustomize v1beta1 API group // Package v1beta1 contains API Schema definitions for the kustomize v1beta1 API group
//
// Deprecated: v1beta1 is no longer supported, use v1 instead.
//
// +kubebuilder:object:generate=true // +kubebuilder:object:generate=true
// +groupName=kustomize.toolkit.fluxcd.io // +groupName=kustomize.toolkit.fluxcd.io
package v1beta1 package v1beta1

View File

@ -272,12 +272,7 @@ const (
// +genclient // +genclient
// +kubebuilder:object:root=true // +kubebuilder:object:root=true
// +kubebuilder:resource:shortName=ks // +kubebuilder:skipversion
// +kubebuilder:subresource:status
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].message",description=""
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
// +kubebuilder:deprecatedversion:warning="v1beta1 Kustomization is deprecated, upgrade to v1"
// Kustomization is the Schema for the kustomizations API. // Kustomization is the Schema for the kustomizations API.
type Kustomization struct { type Kustomization struct {

View File

@ -1,7 +1,7 @@
//go:build !ignore_autogenerated //go:build !ignore_autogenerated
/* /*
Copyright 2023 The Flux authors Copyright 2025 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -1,7 +1,7 @@
//go:build !ignore_autogenerated //go:build !ignore_autogenerated
/* /*
Copyright 2023 The Flux authors Copyright 2025 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -178,7 +178,7 @@ func (in *KustomizationSpec) DeepCopyInto(out *KustomizationSpec) {
if in.KubeConfig != nil { if in.KubeConfig != nil {
in, out := &in.KubeConfig, &out.KubeConfig in, out := &in.KubeConfig, &out.KubeConfig
*out = new(meta.KubeConfigReference) *out = new(meta.KubeConfigReference)
**out = **in (*in).DeepCopyInto(*out)
} }
if in.PostBuild != nil { if in.PostBuild != nil {
in, out := &in.PostBuild, &out.PostBuild in, out := &in.PostBuild, &out.PostBuild

View File

@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition kind: CustomResourceDefinition
metadata: metadata:
annotations: annotations:
controller-gen.kubebuilder.io/version: v0.16.1 controller-gen.kubebuilder.io/version: v0.19.0
name: kustomizations.kustomize.toolkit.fluxcd.io name: kustomizations.kustomize.toolkit.fluxcd.io
spec: spec:
group: kustomize.toolkit.fluxcd.io group: kustomize.toolkit.fluxcd.io
@ -71,8 +71,7 @@ spec:
type: object type: object
type: object type: object
components: components:
description: Components specifies relative paths to specifications description: Components specifies relative paths to kustomize Components.
of other Components.
items: items:
type: string type: string
type: array type: array
@ -123,20 +122,28 @@ spec:
type: string type: string
dependsOn: dependsOn:
description: |- description: |-
DependsOn may contain a meta.NamespacedObjectReference slice DependsOn may contain a DependencyReference slice
with references to Kustomization resources that must be ready before this with references to Kustomization resources that must be ready before this
Kustomization can be reconciled. Kustomization can be reconciled.
items: items:
description: |- description: DependencyReference defines a Kustomization dependency
NamespacedObjectReference contains enough information to locate the referenced Kubernetes resource object in any on another Kustomization resource.
namespace.
properties: properties:
name: name:
description: Name of the referent. description: Name of the referent.
type: string type: string
namespace: namespace:
description: Namespace of the referent, when not specified it description: |-
acts as LocalObjectReference. Namespace of the referent, defaults to the namespace of the Kustomization
resource object that contains the reference.
type: string
readyExpr:
description: |-
ReadyExpr is a CEL expression that can be used to assess the readiness
of a dependency. When specified, the built-in readiness check
is replaced by the logic defined in the CEL expression.
To make the CEL expression additive to the built-in readiness check,
the feature gate `AdditiveCELDependencyCheck` must be set to `true`.
type: string type: string
required: required:
- name - name
@ -210,6 +217,12 @@ spec:
- name - name
type: object type: object
type: array type: array
ignoreMissingComponents:
description: |-
IgnoreMissingComponents instructs the controller to ignore Components paths
not found in source by removing them from the generated kustomization.yaml
before running kustomize build.
type: boolean
images: images:
description: |- description: |-
Images is a list of (image name, new name, new tag or digest) Images is a list of (image name, new name, new tag or digest)
@ -256,16 +269,54 @@ spec:
a controller level fallback for when KustomizationSpec.ServiceAccountName a controller level fallback for when KustomizationSpec.ServiceAccountName
is empty. is empty.
properties: properties:
configMapRef:
description: |-
ConfigMapRef holds an optional name of a ConfigMap that contains
the following keys:
- `provider`: the provider to use. One of `aws`, `azure`, `gcp`, or
`generic`. Required.
- `cluster`: the fully qualified resource name of the Kubernetes
cluster in the cloud provider API. Not used by the `generic`
provider. Required when one of `address` or `ca.crt` is not set.
- `address`: the address of the Kubernetes API server. Required
for `generic`. For the other providers, if not specified, the
first address in the cluster resource will be used, and if
specified, it must match one of the addresses in the cluster
resource.
If audiences is not set, will be used as the audience for the
`generic` provider.
- `ca.crt`: the optional PEM-encoded CA certificate for the
Kubernetes API server. If not set, the controller will use the
CA certificate from the cluster resource.
- `audiences`: the optional audiences as a list of
line-break-separated strings for the Kubernetes ServiceAccount
token. Defaults to the `address` for the `generic` provider, or
to specific values for the other providers depending on the
provider.
- `serviceAccountName`: the optional name of the Kubernetes
ServiceAccount in the same namespace that should be used
for authentication. If not specified, the controller
ServiceAccount will be used.
Mutually exclusive with SecretRef.
properties:
name:
description: Name of the referent.
type: string
required:
- name
type: object
secretRef: secretRef:
description: |- description: |-
SecretRef holds the name of a secret that contains a key with SecretRef holds an optional name of a secret that contains a key with
the kubeconfig file as the value. If no key is set, the key will default the kubeconfig file as the value. If no key is set, the key will default
to 'value'. to 'value'. Mutually exclusive with ConfigMapRef.
It is recommended that the kubeconfig is self-contained, and the secret It is recommended that the kubeconfig is self-contained, and the secret
is regularly updated if credentials such as a cloud-access-token expire. is regularly updated if credentials such as a cloud-access-token expire.
Cloud specific `cmd-path` auth helpers will not function without adding Cloud specific `cmd-path` auth helpers will not function without adding
binaries and credentials to the Pod that is responsible for reconciling binaries and credentials to the Pod that is responsible for reconciling
Kubernetes resources. Kubernetes resources. Supported only for the generic provider.
properties: properties:
key: key:
description: Key in the Secret, when not specified an implementation-specific description: Key in the Secret, when not specified an implementation-specific
@ -277,9 +328,14 @@ spec:
required: required:
- name - name
type: object type: object
required:
- secretRef
type: object type: object
x-kubernetes-validations:
- message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef
must be specified
rule: has(self.configMapRef) || has(self.secretRef)
- message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef
must be specified
rule: '!has(self.configMapRef) || !has(self.secretRef)'
namePrefix: namePrefix:
description: NamePrefix will prefix the names of all managed resources. description: NamePrefix will prefix the names of all managed resources.
maxLength: 200 maxLength: 200
@ -438,6 +494,7 @@ spec:
- OCIRepository - OCIRepository
- GitRepository - GitRepository
- Bucket - Bucket
- ExternalArtifact
type: string type: string
name: name:
description: Name of the referent. description: Name of the referent.
@ -540,6 +597,57 @@ spec:
- type - type
type: object type: object
type: array type: array
history:
description: |-
History contains a set of snapshots of the last reconciliation attempts
tracking the revision, the state and the duration of each attempt.
items:
description: |-
Snapshot represents a point-in-time record of a group of resources reconciliation,
including timing information, status, and a unique digest identifier.
properties:
digest:
description: Digest is the checksum in the format `<algo>:<hex>`
of the resources in this snapshot.
type: string
firstReconciled:
description: FirstReconciled is the time when this revision
was first reconciled to the cluster.
format: date-time
type: string
lastReconciled:
description: LastReconciled is the time when this revision was
last reconciled to the cluster.
format: date-time
type: string
lastReconciledDuration:
description: LastReconciledDuration is time it took to reconcile
the resources in this revision.
type: string
lastReconciledStatus:
description: LastReconciledStatus is the status of the last
reconciliation.
type: string
metadata:
additionalProperties:
type: string
description: Metadata contains additional information about
the snapshot.
type: object
totalReconciliations:
description: TotalReconciliations is the total number of reconciliations
that have occurred for this snapshot.
format: int64
type: integer
required:
- digest
- firstReconciled
- lastReconciled
- lastReconciledDuration
- lastReconciledStatus
- totalReconciliations
type: object
type: array
inventory: inventory:
description: |- description: |-
Inventory contains the list of Kubernetes resource object references that Inventory contains the list of Kubernetes resource object references that
@ -601,574 +709,6 @@ spec:
storage: true storage: true
subresources: subresources:
status: {} status: {}
- additionalPrinterColumns:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
- jsonPath: .status.conditions[?(@.type=="Ready")].message
name: Status
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
deprecated: true
deprecationWarning: v1beta1 Kustomization is deprecated, upgrade to v1
name: v1beta1
schema:
openAPIV3Schema:
description: Kustomization is the Schema for the kustomizations API.
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: KustomizationSpec defines the desired state of a kustomization.
properties:
decryption:
description: Decrypt Kubernetes secrets before applying them on the
cluster.
properties:
provider:
description: Provider is the name of the decryption engine.
enum:
- sops
type: string
secretRef:
description: The secret name containing the private OpenPGP keys
used for decryption.
properties:
name:
description: Name of the referent.
type: string
required:
- name
type: object
required:
- provider
type: object
dependsOn:
description: |-
DependsOn may contain a meta.NamespacedObjectReference slice
with references to Kustomization resources that must be ready before this
Kustomization can be reconciled.
items:
description: |-
NamespacedObjectReference contains enough information to locate the referenced Kubernetes resource object in any
namespace.
properties:
name:
description: Name of the referent.
type: string
namespace:
description: Namespace of the referent, when not specified it
acts as LocalObjectReference.
type: string
required:
- name
type: object
type: array
force:
default: false
description: |-
Force instructs the controller to recreate resources
when patching fails due to an immutable field change.
type: boolean
healthChecks:
description: A list of resources to be included in the health assessment.
items:
description: |-
NamespacedObjectKindReference contains enough information to locate the typed referenced Kubernetes resource object
in any namespace.
properties:
apiVersion:
description: API version of the referent, if not specified the
Kubernetes preferred version will be used.
type: string
kind:
description: Kind of the referent.
type: string
name:
description: Name of the referent.
type: string
namespace:
description: Namespace of the referent, when not specified it
acts as LocalObjectReference.
type: string
required:
- kind
- name
type: object
type: array
images:
description: |-
Images is a list of (image name, new name, new tag or digest)
for changing image names, tags or digests. This can also be achieved with a
patch, but this operator is simpler to specify.
items:
description: Image contains an image name, a new name, a new tag
or digest, which will replace the original name and tag.
properties:
digest:
description: |-
Digest is the value used to replace the original image tag.
If digest is present NewTag value is ignored.
type: string
name:
description: Name is a tag-less image name.
type: string
newName:
description: NewName is the value used to replace the original
name.
type: string
newTag:
description: NewTag is the value used to replace the original
tag.
type: string
required:
- name
type: object
type: array
interval:
description: The interval at which to reconcile the Kustomization.
type: string
kubeConfig:
description: |-
The KubeConfig for reconciling the Kustomization on a remote cluster.
When specified, KubeConfig takes precedence over ServiceAccountName.
properties:
secretRef:
description: |-
SecretRef holds the name to a secret that contains a 'value' key with
the kubeconfig file as the value. It must be in the same namespace as
the Kustomization.
It is recommended that the kubeconfig is self-contained, and the secret
is regularly updated if credentials such as a cloud-access-token expire.
Cloud specific `cmd-path` auth helpers will not function without adding
binaries and credentials to the Pod that is responsible for reconciling
the Kustomization.
properties:
name:
description: Name of the referent.
type: string
required:
- name
type: object
required:
- secretRef
type: object
patches:
description: |-
Strategic merge and JSON patches, defined as inline YAML objects,
capable of targeting objects based on kind, label and annotation selectors.
items:
description: |-
Patch contains an inline StrategicMerge or JSON6902 patch, and the target the patch should
be applied to.
properties:
patch:
description: |-
Patch contains an inline StrategicMerge patch or an inline JSON6902 patch with
an array of operation objects.
type: string
target:
description: Target points to the resources that the patch document
should be applied to.
properties:
annotationSelector:
description: |-
AnnotationSelector is a string that follows the label selection expression
https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api
It matches with the resource annotations.
type: string
group:
description: |-
Group is the API group to select resources from.
Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources.
https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type: string
kind:
description: |-
Kind of the API Group to select resources from.
Together with Group and Version it is capable of unambiguously
identifying and/or selecting resources.
https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type: string
labelSelector:
description: |-
LabelSelector is a string that follows the label selection expression
https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api
It matches with the resource labels.
type: string
name:
description: Name to match resources with.
type: string
namespace:
description: Namespace to select resources from.
type: string
version:
description: |-
Version of the API Group to select resources from.
Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources.
https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type: string
type: object
required:
- patch
type: object
type: array
patchesJson6902:
description: JSON 6902 patches, defined as inline YAML objects.
items:
description: JSON6902Patch contains a JSON6902 patch and the target
the patch should be applied to.
properties:
patch:
description: Patch contains the JSON6902 patch document with
an array of operation objects.
items:
description: |-
JSON6902 is a JSON6902 operation object.
https://datatracker.ietf.org/doc/html/rfc6902#section-4
properties:
from:
description: |-
From contains a JSON-pointer value that references a location within the target document where the operation is
performed. The meaning of the value depends on the value of Op, and is NOT taken into account by all operations.
type: string
op:
description: |-
Op indicates the operation to perform. Its value MUST be one of "add", "remove", "replace", "move", "copy", or
"test".
https://datatracker.ietf.org/doc/html/rfc6902#section-4
enum:
- test
- remove
- add
- replace
- move
- copy
type: string
path:
description: |-
Path contains the JSON-pointer value that references a location within the target document where the operation
is performed. The meaning of the value depends on the value of Op.
type: string
value:
description: |-
Value contains a valid JSON structure. The meaning of the value depends on the value of Op, and is NOT taken into
account by all operations.
x-kubernetes-preserve-unknown-fields: true
required:
- op
- path
type: object
type: array
target:
description: Target points to the resources that the patch document
should be applied to.
properties:
annotationSelector:
description: |-
AnnotationSelector is a string that follows the label selection expression
https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api
It matches with the resource annotations.
type: string
group:
description: |-
Group is the API group to select resources from.
Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources.
https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type: string
kind:
description: |-
Kind of the API Group to select resources from.
Together with Group and Version it is capable of unambiguously
identifying and/or selecting resources.
https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type: string
labelSelector:
description: |-
LabelSelector is a string that follows the label selection expression
https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api
It matches with the resource labels.
type: string
name:
description: Name to match resources with.
type: string
namespace:
description: Namespace to select resources from.
type: string
version:
description: |-
Version of the API Group to select resources from.
Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources.
https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md
type: string
type: object
required:
- patch
- target
type: object
type: array
patchesStrategicMerge:
description: Strategic merge patches, defined as inline YAML objects.
items:
x-kubernetes-preserve-unknown-fields: true
type: array
path:
description: |-
Path to the directory containing the kustomization.yaml file, or the
set of plain YAMLs a kustomization.yaml should be generated for.
Defaults to 'None', which translates to the root path of the SourceRef.
type: string
postBuild:
description: |-
PostBuild describes which actions to perform on the YAML manifest
generated by building the kustomize overlay.
properties:
substitute:
additionalProperties:
type: string
description: |-
Substitute holds a map of key/value pairs.
The variables defined in your YAML manifests
that match any of the keys defined in the map
will be substituted with the set value.
Includes support for bash string replacement functions
e.g. ${var:=default}, ${var:position} and ${var/substring/replacement}.
type: object
substituteFrom:
description: |-
SubstituteFrom holds references to ConfigMaps and Secrets containing
the variables and their values to be substituted in the YAML manifests.
The ConfigMap and the Secret data keys represent the var names and they
must match the vars declared in the manifests for the substitution to happen.
items:
description: |-
SubstituteReference contains a reference to a resource containing
the variables name and value.
properties:
kind:
description: Kind of the values referent, valid values are
('Secret', 'ConfigMap').
enum:
- Secret
- ConfigMap
type: string
name:
description: |-
Name of the values referent. Should reside in the same namespace as the
referring resource.
maxLength: 253
minLength: 1
type: string
required:
- kind
- name
type: object
type: array
type: object
prune:
description: Prune enables garbage collection.
type: boolean
retryInterval:
description: |-
The interval at which to retry a previously failed reconciliation.
When not specified, the controller uses the KustomizationSpec.Interval
value to retry failures.
type: string
serviceAccountName:
description: |-
The name of the Kubernetes service account to impersonate
when reconciling this Kustomization.
type: string
sourceRef:
description: Reference of the source where the kustomization file
is.
properties:
apiVersion:
description: API version of the referent
type: string
kind:
description: Kind of the referent
enum:
- GitRepository
- Bucket
type: string
name:
description: Name of the referent
type: string
namespace:
description: Namespace of the referent, defaults to the Kustomization
namespace
type: string
required:
- kind
- name
type: object
suspend:
description: |-
This flag tells the controller to suspend subsequent kustomize executions,
it does not apply to already started executions. Defaults to false.
type: boolean
targetNamespace:
description: |-
TargetNamespace sets or overrides the namespace in the
kustomization.yaml file.
maxLength: 63
minLength: 1
type: string
timeout:
description: |-
Timeout for validation, apply and health checking operations.
Defaults to 'Interval' duration.
type: string
validation:
description: |-
Validate the Kubernetes objects before applying them on the cluster.
The validation strategy can be 'client' (local dry-run), 'server'
(APIServer dry-run) or 'none'.
When 'Force' is 'true', validation will fallback to 'client' if set to
'server' because server-side validation is not supported in this scenario.
enum:
- none
- client
- server
type: string
required:
- interval
- prune
- sourceRef
type: object
status:
default:
observedGeneration: -1
description: KustomizationStatus defines the observed state of a kustomization.
properties:
conditions:
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
lastAppliedRevision:
description: |-
The last successfully applied revision.
The revision format for Git sources is <branch|tag>/<commit-sha>.
type: string
lastAttemptedRevision:
description: LastAttemptedRevision is the revision of the last reconciliation
attempt.
type: string
lastHandledReconcileAt:
description: |-
LastHandledReconcileAt holds the value of the most recent
reconcile request value, so a change of the annotation value
can be detected.
type: string
observedGeneration:
description: ObservedGeneration is the last reconciled generation.
format: int64
type: integer
snapshot:
description: The last successfully applied revision metadata.
properties:
checksum:
description: The manifests sha1 checksum.
type: string
entries:
description: A list of Kubernetes kinds grouped by namespace.
items:
description: |-
Snapshot holds the metadata of namespaced
Kubernetes objects
properties:
kinds:
additionalProperties:
type: string
description: The list of Kubernetes kinds.
type: object
namespace:
description: The namespace of this entry.
type: string
required:
- kinds
type: object
type: array
required:
- checksum
- entries
type: object
type: object
type: object
served: true
storage: false
subresources:
status: {}
- additionalPrinterColumns: - additionalPrinterColumns:
- jsonPath: .metadata.creationTimestamp - jsonPath: .metadata.creationTimestamp
name: Age name: Age
@ -1347,16 +887,54 @@ spec:
a controller level fallback for when KustomizationSpec.ServiceAccountName a controller level fallback for when KustomizationSpec.ServiceAccountName
is empty. is empty.
properties: properties:
configMapRef:
description: |-
ConfigMapRef holds an optional name of a ConfigMap that contains
the following keys:
- `provider`: the provider to use. One of `aws`, `azure`, `gcp`, or
`generic`. Required.
- `cluster`: the fully qualified resource name of the Kubernetes
cluster in the cloud provider API. Not used by the `generic`
provider. Required when one of `address` or `ca.crt` is not set.
- `address`: the address of the Kubernetes API server. Required
for `generic`. For the other providers, if not specified, the
first address in the cluster resource will be used, and if
specified, it must match one of the addresses in the cluster
resource.
If audiences is not set, will be used as the audience for the
`generic` provider.
- `ca.crt`: the optional PEM-encoded CA certificate for the
Kubernetes API server. If not set, the controller will use the
CA certificate from the cluster resource.
- `audiences`: the optional audiences as a list of
line-break-separated strings for the Kubernetes ServiceAccount
token. Defaults to the `address` for the `generic` provider, or
to specific values for the other providers depending on the
provider.
- `serviceAccountName`: the optional name of the Kubernetes
ServiceAccount in the same namespace that should be used
for authentication. If not specified, the controller
ServiceAccount will be used.
Mutually exclusive with SecretRef.
properties:
name:
description: Name of the referent.
type: string
required:
- name
type: object
secretRef: secretRef:
description: |- description: |-
SecretRef holds the name of a secret that contains a key with SecretRef holds an optional name of a secret that contains a key with
the kubeconfig file as the value. If no key is set, the key will default the kubeconfig file as the value. If no key is set, the key will default
to 'value'. to 'value'. Mutually exclusive with ConfigMapRef.
It is recommended that the kubeconfig is self-contained, and the secret It is recommended that the kubeconfig is self-contained, and the secret
is regularly updated if credentials such as a cloud-access-token expire. is regularly updated if credentials such as a cloud-access-token expire.
Cloud specific `cmd-path` auth helpers will not function without adding Cloud specific `cmd-path` auth helpers will not function without adding
binaries and credentials to the Pod that is responsible for reconciling binaries and credentials to the Pod that is responsible for reconciling
Kubernetes resources. Kubernetes resources. Supported only for the generic provider.
properties: properties:
key: key:
description: Key in the Secret, when not specified an implementation-specific description: Key in the Secret, when not specified an implementation-specific
@ -1368,9 +946,14 @@ spec:
required: required:
- name - name
type: object type: object
required:
- secretRef
type: object type: object
x-kubernetes-validations:
- message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef
must be specified
rule: has(self.configMapRef) || has(self.secretRef)
- message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef
must be specified
rule: '!has(self.configMapRef) || !has(self.secretRef)'
patches: patches:
description: |- description: |-
Strategic merge and JSON patches, defined as inline YAML objects, Strategic merge and JSON patches, defined as inline YAML objects,

View File

@ -2,8 +2,8 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization kind: Kustomization
namespace: kustomize-system namespace: kustomize-system
resources: resources:
- https://github.com/fluxcd/source-controller/releases/download/v1.6.0/source-controller.crds.yaml - https://github.com/fluxcd/source-controller/releases/download/v1.7.0-rc.3/source-controller.crds.yaml
- https://github.com/fluxcd/source-controller/releases/download/v1.6.0/source-controller.deployment.yaml - https://github.com/fluxcd/source-controller/releases/download/v1.7.0-rc.3/source-controller.deployment.yaml
- ../crd - ../crd
- ../rbac - ../rbac
- ../manager - ../manager

View File

@ -89,14 +89,14 @@ overridden if its key matches a common one.</p>
<td> <td>
<code>dependsOn</code><br> <code>dependsOn</code><br>
<em> <em>
<a href="https://godoc.org/github.com/fluxcd/pkg/apis/meta#NamespacedObjectReference"> <a href="#kustomize.toolkit.fluxcd.io/v1.DependencyReference">
[]github.com/fluxcd/pkg/apis/meta.NamespacedObjectReference []DependencyReference
</a> </a>
</em> </em>
</td> </td>
<td> <td>
<em>(Optional)</em> <em>(Optional)</em>
<p>DependsOn may contain a meta.NamespacedObjectReference slice <p>DependsOn may contain a DependencyReference slice
with references to Kustomization resources that must be ready before this with references to Kustomization resources that must be ready before this
Kustomization can be reconciled.</p> Kustomization can be reconciled.</p>
</td> </td>
@ -392,7 +392,21 @@ resources. When enabled, the HealthChecks are ignored. Defaults to false.</p>
</td> </td>
<td> <td>
<em>(Optional)</em> <em>(Optional)</em>
<p>Components specifies relative paths to specifications of other Components.</p> <p>Components specifies relative paths to kustomize Components.</p>
</td>
</tr>
<tr>
<td>
<code>ignoreMissingComponents</code><br>
<em>
bool
</em>
</td>
<td>
<em>(Optional)</em>
<p>IgnoreMissingComponents instructs the controller to ignore Components paths
not found in source by removing them from the generated kustomization.yaml
before running kustomize build.</p>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -609,6 +623,67 @@ field.</p>
</table> </table>
</div> </div>
</div> </div>
<h3 id="kustomize.toolkit.fluxcd.io/v1.DependencyReference">DependencyReference
</h3>
<p>
(<em>Appears on:</em>
<a href="#kustomize.toolkit.fluxcd.io/v1.KustomizationSpec">KustomizationSpec</a>)
</p>
<p>DependencyReference defines a Kustomization dependency on another Kustomization resource.</p>
<div class="md-typeset__scrollwrap">
<div class="md-typeset__table">
<table>
<thead>
<tr>
<th>Field</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>name</code><br>
<em>
string
</em>
</td>
<td>
<p>Name of the referent.</p>
</td>
</tr>
<tr>
<td>
<code>namespace</code><br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>Namespace of the referent, defaults to the namespace of the Kustomization
resource object that contains the reference.</p>
</td>
</tr>
<tr>
<td>
<code>readyExpr</code><br>
<em>
string
</em>
</td>
<td>
<em>(Optional)</em>
<p>ReadyExpr is a CEL expression that can be used to assess the readiness
of a dependency. When specified, the built-in readiness check
is replaced by the logic defined in the CEL expression.
To make the CEL expression additive to the built-in readiness check,
the feature gate <code>AdditiveCELDependencyCheck</code> must be set to <code>true</code>.</p>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<h3 id="kustomize.toolkit.fluxcd.io/v1.KustomizationSpec">KustomizationSpec <h3 id="kustomize.toolkit.fluxcd.io/v1.KustomizationSpec">KustomizationSpec
</h3> </h3>
<p> <p>
@ -647,14 +722,14 @@ overridden if its key matches a common one.</p>
<td> <td>
<code>dependsOn</code><br> <code>dependsOn</code><br>
<em> <em>
<a href="https://godoc.org/github.com/fluxcd/pkg/apis/meta#NamespacedObjectReference"> <a href="#kustomize.toolkit.fluxcd.io/v1.DependencyReference">
[]github.com/fluxcd/pkg/apis/meta.NamespacedObjectReference []DependencyReference
</a> </a>
</em> </em>
</td> </td>
<td> <td>
<em>(Optional)</em> <em>(Optional)</em>
<p>DependsOn may contain a meta.NamespacedObjectReference slice <p>DependsOn may contain a DependencyReference slice
with references to Kustomization resources that must be ready before this with references to Kustomization resources that must be ready before this
Kustomization can be reconciled.</p> Kustomization can be reconciled.</p>
</td> </td>
@ -950,7 +1025,21 @@ resources. When enabled, the HealthChecks are ignored. Defaults to false.</p>
</td> </td>
<td> <td>
<em>(Optional)</em> <em>(Optional)</em>
<p>Components specifies relative paths to specifications of other Components.</p> <p>Components specifies relative paths to kustomize Components.</p>
</td>
</tr>
<tr>
<td>
<code>ignoreMissingComponents</code><br>
<em>
bool
</em>
</td>
<td>
<em>(Optional)</em>
<p>IgnoreMissingComponents instructs the controller to ignore Components paths
not found in source by removing them from the generated kustomization.yaml
before running kustomize build.</p>
</td> </td>
</tr> </tr>
<tr> <tr>
@ -1086,6 +1175,21 @@ ResourceInventory
have been successfully applied.</p> have been successfully applied.</p>
</td> </td>
</tr> </tr>
<tr>
<td>
<code>history</code><br>
<em>
<a href="https://godoc.org/github.com/fluxcd/pkg/apis/meta#History">
github.com/fluxcd/pkg/apis/meta.History
</a>
</em>
</td>
<td>
<em>(Optional)</em>
<p>History contains a set of snapshots of the last reconciliation attempts
tracking the revision, the state and the duration of each attempt.</p>
</td>
</tr>
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -117,6 +117,7 @@ Artifact containing the YAML manifests. It has two required fields:
+ [GitRepository](https://github.com/fluxcd/source-controller/blob/main/docs/spec/v1/gitrepositories.md) + [GitRepository](https://github.com/fluxcd/source-controller/blob/main/docs/spec/v1/gitrepositories.md)
+ [OCIRepository](https://github.com/fluxcd/source-controller/blob/main/docs/spec/v1/ocirepositories.md) + [OCIRepository](https://github.com/fluxcd/source-controller/blob/main/docs/spec/v1/ocirepositories.md)
+ [Bucket](https://github.com/fluxcd/source-controller/blob/main/docs/spec/v1/buckets.md) + [Bucket](https://github.com/fluxcd/source-controller/blob/main/docs/spec/v1/buckets.md)
+ [ExternalArtifact](https://github.com/fluxcd/source-controller/blob/main/docs/spec/v1/externalartifacts.md) (requires `--feature-gates=ExternalArtifact=true` flag)
- `name`: The Name of the referred Source object. - `name`: The Name of the referred Source object.
#### Cross-namespace references #### Cross-namespace references
@ -487,6 +488,51 @@ is running before deploying applications inside the mesh.
**Note:** Circular dependencies between Kustomizations must be avoided, **Note:** Circular dependencies between Kustomizations must be avoided,
otherwise the interdependent Kustomizations will never be applied on the cluster. otherwise the interdependent Kustomizations will never be applied on the cluster.
#### Dependency Ready Expression
`.spec.dependsOn[].readyExpr` is an optional field that can be used to define a CEL expression
to determine the readiness of a Kustomization dependency.
This is helpful for when custom logic is needed to determine if a dependency is ready.
For example, when performing a lockstep upgrade, the `readyExpr` can be used to
verify that a dependency has a matching version label before proceeding with the
reconciliation of the dependent Kustomization.
```yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: app-backend
namespace: apps
labels:
app/version: v1.2.3
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: app-frontend
namespace: apps
labels:
app/version: v1.2.3
spec:
dependsOn:
- name: app-backend
readyExpr: >
dep.metadata.labels['app/version'] == self.metadata.labels['app/version'] &&
dep.status.conditions.filter(e, e.type == 'Ready').all(e, e.status == 'True') &&
dep.metadata.generation == dep.status.observedGeneration
```
The CEL expression contains the following variables:
- `dep`: The dependency Kustomization object being evaluated.
- `self`: The Kustomization object being reconciled.
**Note:** When `readyExpr` is specified, the built-in readiness check is replaced by the logic
defined in the CEL expression. You can configure the controller to run both the CEL expression
evaluation and the built-in readiness check, with the `AdditiveCELDependencyCheck`
[feature gate](https://fluxcd.io/flux/components/kustomize/options/#feature-gates).
### Service Account reference ### Service Account reference
`.spec.serviceAccountName` is an optional field used to specify the `.spec.serviceAccountName` is an optional field used to specify the
@ -618,6 +664,7 @@ metadata:
namespace: flux-system namespace: flux-system
spec: spec:
# ...omitted for brevity # ...omitted for brevity
ignoreMissingComponents: false
components: components:
- ../ingress - ../ingress
- ../tls - ../tls
@ -625,6 +672,11 @@ spec:
**Note:** The components paths must be local and relative to the path specified by `.spec.path`. **Note:** The components paths must be local and relative to the path specified by `.spec.path`.
With `.spec.ignoreMissingComponents` you can specify whether the controller
should ignore the component paths that are missing from the source. By default,
it is set to `false`, meaning that the controller will fail the reconciliation
if any of the specified paths are missing from the source.
**Warning:** Components are an alpha feature in Kustomize and are therefore **Warning:** Components are an alpha feature in Kustomize and are therefore
considered experimental in Flux. No guarantees are provided as the feature may considered experimental in Flux. No guarantees are provided as the feature may
be modified in backwards incompatible ways or removed without warning. be modified in backwards incompatible ways or removed without warning.
@ -639,6 +691,10 @@ With `.spec.postBuild.substituteFrom` you can provide a list of ConfigMaps and
Secrets from which the variables are loaded. The ConfigMap and Secret data keys Secrets from which the variables are loaded. The ConfigMap and Secret data keys
are used as the variable names. are used as the variable names.
To make a Kustomization react immediately to changes in the referenced Secret
or ConfigMap see [this](#reacting-immediately-to-configuration-dependencies)
section.
The `.spec.postBuild.substituteFrom.optional` field indicates how the The `.spec.postBuild.substituteFrom.optional` field indicates how the
controller should handle a referenced ConfigMap or Secret being absent controller should handle a referenced ConfigMap or Secret being absent
at reconciliation time. The controller's default behavior ― with at reconciliation time. The controller's default behavior ― with
@ -699,7 +755,7 @@ spec:
# Fail if this Secret does not exist. # Fail if this Secret does not exist.
``` ```
**Note:** For substituting variables in a secret, `.spec.stringData` field must be used i.e: For substituting variables in a secret, `.spec.stringData` field must be used i.e:
```yaml ```yaml
--- ---
@ -713,11 +769,11 @@ stringData:
token: ${token} token: ${token}
``` ```
**Note:** The var values which are specified in-line with `substitute` The var values which are specified in-line with `substitute`
take precedence over the ones derived from `substituteFrom`. take precedence over the ones derived from `substituteFrom`.
When var values for the same variable keys are derived from multiple When var values for the same variable keys are derived from multiple
`ConfigMaps` or `Secrets` referenced in the `substituteFrom` list, then the `ConfigMaps` or `Secrets` referenced in the `substituteFrom` list,
first take precedence over the later values. the later values overwrite earlier values.
**Note:** If you want to avoid var substitutions in scripts embedded in **Note:** If you want to avoid var substitutions in scripts embedded in
ConfigMaps or container commands, you must use the format `$var` instead of ConfigMaps or container commands, you must use the format `$var` instead of
@ -791,14 +847,44 @@ with:
kustomize.toolkit.fluxcd.io/force: enabled kustomize.toolkit.fluxcd.io/force: enabled
``` ```
### KubeConfig reference ### KubeConfig (Remote clusters)
With the `.spec.kubeConfig` field a Kustomization
can apply and manage resources on a remote cluster.
Two authentication alternatives are available:
- `.spec.kubeConfig.secretRef`: Secret-based authentication using a
static kubeconfig stored in a Kubernetes Secret in the same namespace
as the Kustomization.
- `.spec.kubeConfig.configMapRef` (Recommended): Secret-less authentication
building a kubeconfig dynamically with parameters stored in a Kubernetes
ConfigMap in the same namespace as the Kustomization via workload identity.
To make a Kustomization react immediately to changes in the referenced Secret
or ConfigMap see [this](#reacting-immediately-to-configuration-dependencies)
section.
When both `.spec.kubeConfig` and
[`.spec.serviceAccountName`](#service-account-reference) are specified,
the controller will impersonate the ServiceAccount on the target cluster,
i.e. a ServiceAccount with name `.spec.serviceAccountName` must exist in
the target cluster inside a namespace with the same name as the namespace
of the Kustomization. For example, if the Kustomization is in the namespace
`apps` of the cluster where Flux is running, then the ServiceAccount
must be in the `apps` namespace of the target remote cluster, and have the
name `.spec.serviceAccountName`. In other words, the namespace of the
Kustomization must exist both in the cluster where Flux is running
and in the target remote cluster where Flux will apply resources.
#### Secret-based authentication
`.spec.kubeConfig.secretRef.Name` is an optional field to specify the name of `.spec.kubeConfig.secretRef.Name` is an optional field to specify the name of
the secret containing a KubeConfig. If specified, objects will be applied, the secret containing a KubeConfig. If specified, objects will be applied,
health-checked, pruned, and deleted for the default cluster specified in that health-checked, pruned, and deleted for the default cluster specified in that
KubeConfig instead of using the in-cluster ServiceAccount. KubeConfig instead of using the in-cluster ServiceAccount.
The secret defined in the `kubeConfig.SecretRef` must exist in the same The secret defined in the `.spec.kubeConfig.secretRef` must exist in the same
namespace as the Kustomization. On every reconciliation, the KubeConfig bytes namespace as the Kustomization. On every reconciliation, the KubeConfig bytes
will be loaded from the `.secretRef.key` key (default: `value` or `value.yaml`) will be loaded from the `.secretRef.key` key (default: `value` or `value.yaml`)
of the Secrets data , and the Secret can thus be regularly updated if of the Secrets data , and the Secret can thus be regularly updated if
@ -822,12 +908,88 @@ stringData:
environment, or credential files from the kustomize-controller Pod. environment, or credential files from the kustomize-controller Pod.
This matches the constraints of KubeConfigs from current Cluster API providers. This matches the constraints of KubeConfigs from current Cluster API providers.
KubeConfigs with `cmd-path` in them likely won't work without a custom, KubeConfigs with `cmd-path` in them likely won't work without a custom,
per-provider installation of kustomize-controller. per-provider installation of kustomize-controller. For more information, see
[remote clusters/Cluster-API](#remote-cluster-api-clusters).
When both `.spec.kubeConfig` and `.spec.ServiceAccountName` are specified, #### Secret-less authentication
the controller will impersonate the service account on the target cluster.
For more information, see [remote clusters/Cluster-API](#remote-clusterscluster-api). The field `.spec.kubeConfig.configMapRef.name` can be used to specify the
name of a ConfigMap in the same namespace as the Kustomization containing
parameters for secret-less authentication via workload identity. The
supported keys inside the `.data` field of the ConfigMap are:
- `.data.provider`: The provider to use. One of `aws`, `azure`, `gcp`,
or `generic`. Required. The `aws` provider is used for connecting to
remote EKS clusters, `azure` for AKS, `gcp` for GKE, and `generic`
for Kubernetes OIDC authentication between clusters. For the
`generic` provider, the remote cluster must be configured to trust
the OIDC issuer of the cluster where Flux is running.
- `.data.cluster`: The fully qualified resource name of the Kubernetes
cluster in the cloud provider API. Not used by the `generic`
provider. Required when one of `.data.address` or `.data["ca.crt"]` is
not set, or if the provider is `aws` (required for defining a region).
- `.data.address`: The address of the Kubernetes API server. Required
for `generic`. For the other providers, if not specified, the
first address in the cluster resource will be used, and if
specified, it must match one of the addresses in the cluster
resource.
If `audiences` is not set, will be used as the audience for the
`generic` provider.
- `.data["ca.crt"]`: The optional PEM-encoded CA certificate for the
Kubernetes API server. If not set, the controller will use the
CA certificate from the cluster resource.
- `.data.audiences`: The optional audiences as a list of
line-break-separated strings for the Kubernetes ServiceAccount token.
Defaults to the address for the `generic` provider, or to specific
values for the other providers depending on the provider.
- `.data.serviceAccountName`: The optional name of the Kubernetes
ServiceAccount in the same namespace that should be used
for authentication. If not specified, the controller
ServiceAccount will be used. Not confuse with the ServiceAccount
used for impersonation, which is specified with
[`.spec.serviceAccountName`](#service-account-reference) directly
in the Kustomization spec and must exist in the target remote cluster.
The `.data.cluster` field, when specified, must have the following formats:
- `aws`: `arn:<partition>:eks:<region>:<account-id>:cluster/<cluster-name>`
- `azure`: `/subscriptions/<subscription-id>/resourceGroups/<resource-group>/providers/Microsoft.ContainerService/managedClusters/<cluster-name>`
- `gcp`: `projects/<project-id>/locations/<location>/clusters/<cluster-name>`
For complete guides on workload identity and setting up permissions for
this feature, see the following docs:
- [EKS](/flux/integrations/aws/#for-amazon-elastic-kubernetes-service)
- [AKS](/flux/integrations/azure/#for-azure-kubernetes-service)
- [GKE](/flux/integrations/gcp/#for-google-kubernetes-engine)
- [Generic](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#configuring-the-api-server)
Example for an EKS cluster:
```yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: backend
namespace: apps
spec:
... # other fields omitted for brevity
kubeConfig:
configMapRef:
name: kubeconfig
serviceAccountName: apps-sa # optional. must exist in the target cluster. user for impersonation
---
apiVersion: v1
kind: ConfigMap
metadata:
name: kubeconfig
namespace: apps
data:
kubeConfig:
provider: aws
cluster: arn:aws:eks:eu-central-1:123456789012:cluster/my-cluster
serviceAccountName: apps-iam-role # optional. maps to an AWS IAM Role. used for authentication
```
### Decryption ### Decryption
@ -856,6 +1018,9 @@ The `.spec.decryption` field has the following subfields:
- `.serviceAccountName`: The name of the service account used for - `.serviceAccountName`: The name of the service account used for
secret-less authentication with KMS services from cloud providers. secret-less authentication with KMS services from cloud providers.
To make a Kustomization react immediately to changes in the referenced Secret
see [this](#reacting-immediately-to-configuration-dependencies) section.
For a complete guide on how to set up authentication for KMS services from For a complete guide on how to set up authentication for KMS services from
cloud providers, see the integration [docs](/flux/integrations/). cloud providers, see the integration [docs](/flux/integrations/).
@ -1353,9 +1518,9 @@ When the flag is set, all Kustomizations which don't have [`.spec.serviceAccount
specified will use the service account name provided by specified will use the service account name provided by
`--default-service-account=<SA Name>` in the namespace of the object. `--default-service-account=<SA Name>` in the namespace of the object.
### Remote clusters/Cluster-API ### Remote Cluster API clusters
With the [`.spec.kubeConfig` field](#kubeconfig-reference) a Kustomization can be fully Using a [`.spec.kubeConfig` reference](#kubeconfig-remote-clusters) a Kustomization can be fully
reconciled on a remote cluster. This composes well with Cluster API bootstrap reconciled on a remote cluster. This composes well with Cluster API bootstrap
providers such as CAPBK (kubeadm), CAPA (AWS) and others. providers such as CAPBK (kubeadm), CAPA (AWS) and others.
@ -1430,155 +1595,24 @@ Kustomization object itself, it will fall back to these defaults.
See also the [workload identity](/flux/installation/configuration/workload-identity/) docs. See also the [workload identity](/flux/installation/configuration/workload-identity/) docs.
#### AWS KMS #### Cloud Provider KMS Services
While making use of the [IAM OIDC provider](https://eksctl.io/usage/iamserviceaccounts/) For cloud provider KMS services, please refer to the specific sections in the integration guides:
on your EKS cluster, you can create an IAM Role and Service Account with access
to AWS KMS (using at least `kms:Decrypt` and `kms:DescribeKey`). Once these are
created, you can annotate the kustomize-controller Service Account with the
Role ARN, granting the controller permission to decrypt the Secrets. Please refer
to the [SOPS guide](https://fluxcd.io/flux/guides/mozilla-sops/#aws) for detailed steps.
```sh Service-specific configuration:
kubectl -n flux-system annotate serviceaccount kustomize-controller \
--field-manager=flux-client-side-apply \
eks.amazonaws.com/role-arn='arn:aws:iam::<ACCOUNT_ID>:role/<KMS-ROLE-NAME>'
```
Furthermore, you can also use the usual [environment variables used for specifying AWS - [AWS KMS](https://fluxcd.io/flux/integrations/aws/#for-amazon-key-management-service)
credentials](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html#envvars-list), - [Azure Key Vault](https://fluxcd.io/flux/integrations/azure/#for-azure-key-vault)
by patching the kustomize-controller Deployment: - [GCP KMS](https://fluxcd.io/flux/integrations/gcp/#for-google-cloud-key-management-service)
```yaml Controller-level configuration:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: kustomize-controller
namespace: flux-system
spec:
template:
spec:
containers:
- name: manager
env:
- name: AWS_ACCESS_KEY_ID
valueFrom:
secretKeyRef:
name: aws-creds
key: awsAccessKeyID
- name: AWS_SECRET_ACCESS_KEY
valueFrom:
secretKeyRef:
name: aws-creds
key: awsSecretAccessKey
- name: AWS_SESSION_TOKEN
valueFrom:
secretKeyRef:
name: aws-creds
key: awsSessionToken
```
In addition to this, the - [AWS](https://fluxcd.io/flux/integrations/aws/#at-the-controller-level)
[general SOPS documentation around KMS AWS applies](https://github.com/mozilla/sops#27kms-aws-profiles), - [Azure](https://fluxcd.io/flux/integrations/azure/#at-the-controller-level)
allowing you to specify e.g. a `SOPS_KMS_ARN` environment variable. - [GCP](https://fluxcd.io/flux/integrations/gcp/#at-the-controller-level)
**Note:**: If you are mounting a secret containing the AWS credentials as a These guides provide detailed instructions for setting up authentication,
file in the `kustomize-controller` Pod, you need to specify an environment permissions, and controller configuration for each cloud provider.
variable `$HOME`, since the AWS credentials file is expected to be present at
`~/.aws`. For example:
```yaml
env:
- name: HOME
value: /home/{$USER}
```
#### Azure Key Vault
##### Workload Identity
If you have Workload Identity set up on your AKS cluster, you can establish
a federated identity between the kustomize-controller ServiceAccount and an
identity that has "Decrypt" role on the Azure Key Vault. Once, this is done
you can label and annotate the kustomize-controller ServiceAccount and Pod
with the patch shown below:
```yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- gotk-components.yaml
- gotk-sync.yaml
patches:
- patch: |-
apiVersion: v1
kind: ServiceAccount
metadata:
name: kustomize-controller
namespace: flux-system
annotations:
azure.workload.identity/client-id: <AZURE_CLIENT_ID>
labels:
azure.workload.identity/use: "true"
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: kustomize-controller
namespace: flux-system
labels:
azure.workload.identity/use: "true"
spec:
template:
metadata:
labels:
azure.workload.identity/use: "true"
```
##### Kubelet Identity
If the kubelet managed identity has `Decrypt` permissions on Azure Key Vault,
no additional configuration is required for the kustomize-controller to decrypt
data.
#### GCP KMS
While making use of Google Cloud Platform, the [`GOOGLE_APPLICATION_CREDENTIALS`
environment variable](https://cloud.google.com/docs/authentication/production)
is automatically taken into account.
[Granting permissions](https://cloud.google.com/kms/docs/reference/permissions-and-roles)
to the Service Account attached to this will therefore be sufficient to decrypt
data. When running outside GCP, it is possible to manually patch the
kustomize-controller Deployment with a valid set of (mounted) credentials.
```yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: kustomize-controller
namespace: flux-system
spec:
template:
spec:
containers:
- name: manager
env:
- name: GOOGLE_APPLICATION_CREDENTIALS
value: /var/gcp/credentials.json
volumeMounts:
- name: gcp-credentials
mountPath: /var/gcp/
readOnly: true
volumes:
- name: gcp-credentials
secret:
secretName: mysecret
items:
- key: credentials
path: credentials.json
```
#### Hashicorp Vault #### Hashicorp Vault
@ -1602,6 +1636,48 @@ spec:
value: <token> value: <token>
``` ```
#### SOPS Age Keys
To configure global decryption for SOPS Age keys, use the `--sops-age-secret`
controller flag to specify a Kubernetes Secret containing the Age private keys.
First, create a Secret containing the Age private keys with the `.agekey` suffix:
```yaml
---
apiVersion: v1
kind: Secret
metadata:
name: sops-age-keys
namespace: flux-system
stringData:
identity1.agekey: <identity1 key>
identity2.agekey: <identity1 key>
```
The Secret must be in the same namespace as the kustomize-controller Deployment.
Then, patch the kustomize-controller Deployment to add the `--sops-age-secret` flag:
```yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: kustomize-controller
namespace: flux-system
spec:
template:
spec:
containers:
- name: manager
args:
- --sops-age-secret=sops-age-keys
```
The field `.spec.decryption.secretRef` in the Kustomization will take precedence
in case both the controller flag and the Kustomization field are set.
### Kustomize secretGenerator ### Kustomize secretGenerator
SOPS encrypted data can be stored as a base64 encoded Secret, which enables the SOPS encrypted data can be stored as a base64 encoded Secret, which enables the
@ -1897,6 +1973,29 @@ the controller. The Flux CLI offer commands for filtering the logs for a
specific Kustomization, e.g. specific Kustomization, e.g.
`flux logs --level=error --kind=Kustomization --name=<kustomization-name>`. `flux logs --level=error --kind=Kustomization --name=<kustomization-name>`.
### Reacting immediately to configuration dependencies
To trigger a reconciliation when changes occur in referenced
Secrets or ConfigMaps, you can set the following label on the
Secret or ConfigMap:
```yaml
metadata:
labels:
reconcile.fluxcd.io/watch: Enabled
```
An alternative to labeling every Secret or ConfigMap is
setting the `--watch-configs-label-selector=owner!=helm`
[flag](https://fluxcd.io/flux/components/kustomize/options/#flags)
in kustomize-controller, which allows watching all Secrets and
ConfigMaps except for Helm storage Secrets.
**Note**: A reconciliation will be triggered for an event on a
referenced Secret/ConfigMap even if it's marked as optional in
the `.spec.postBuild.substituteFrom` field, including deletion
events.
## Kustomization Status ## Kustomization Status
### Conditions ### Conditions
@ -1974,6 +2073,37 @@ configuration issue in the Kustomization spec. When a reconciliation fails, the
`Reconciling` Condition `reason` would be `ProgressingWithRetry`. When the `Reconciling` Condition `reason` would be `ProgressingWithRetry`. When the
reconciliation is performed again after the failure, the `reason` is updated to `Progressing`. reconciliation is performed again after the failure, the `reason` is updated to `Progressing`.
### History
The kustomize-controller maintains a history of the last 5 reconciliations
in `.status.history`, including the digest of the applied manifests, the
source and origin revision, the timestamps and the duration of the reconciliations,
the status and the total number of times a specific digest was reconciled.
```yaml
status:
history:
- digest: sha256:43ad78c94b2655429d84f21488f29d7cca9cd45b7f54d2b27e16bbec8eff9228
firstReconciled: "2025-08-15T10:11:00Z"
lastReconciled: "2025-08-15T11:12:00Z"
lastReconciledDuration: 2.818583s
lastReconciledStatus: ReconciliationSucceeded
totalReconciliations: 2
metadata:
revision: "v1.0.1@sha1:450796ddb2ab6724ee1cc32a4be56da032d1cca0"
- digest: sha256:ec8dbfe61777b65001190260cf873ffe454451bd2e464bd6f9a154cffcdcd7e5
firstReconciled: "2025-07-14T13:10:00Z"
lastReconciled: "2025-08-15T10:00:00Z"
lastReconciledDuration: 49.813292s
lastReconciledStatus: HealthCheckFailed
totalReconciliations: 120
metadata:
revision: "v1.0.0@sha1:67e2c98a60dc92283531412a9e604dd4bae005a9"
```
The kustomize-controller deduplicates entries based on the digest and status, with the
most recent reconciliation being the first entry in the list.
### Inventory ### Inventory
In order to perform operations such as drift detection, garbage collection, etc. In order to perform operations such as drift detection, garbage collection, etc.

214
go.mod
View File

@ -1,6 +1,6 @@
module github.com/fluxcd/kustomize-controller module github.com/fluxcd/kustomize-controller
go 1.24.0 go 1.25.0
replace github.com/fluxcd/kustomize-controller/api => ./api replace github.com/fluxcd/kustomize-controller/api => ./api
@ -9,100 +9,105 @@ replace github.com/fluxcd/kustomize-controller/api => ./api
replace github.com/opencontainers/go-digest => github.com/opencontainers/go-digest v1.0.1-0.20220411205349-bde1400a84be replace github.com/opencontainers/go-digest => github.com/opencontainers/go-digest v1.0.1-0.20220411205349-bde1400a84be
require ( require (
cloud.google.com/go/kms v1.21.2 cloud.google.com/go/kms v1.22.0
filippo.io/age v1.2.1 filippo.io/age v1.2.1
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0
github.com/aws/aws-sdk-go-v2 v1.36.3 github.com/aws/aws-sdk-go-v2 v1.38.3
github.com/aws/aws-sdk-go-v2/credentials v1.17.67 github.com/aws/aws-sdk-go-v2/credentials v1.18.10
github.com/cyphar/filepath-securejoin v0.4.1 github.com/cyphar/filepath-securejoin v0.4.1
github.com/dimchansky/utfbom v1.1.1 github.com/dimchansky/utfbom v1.1.1
github.com/fluxcd/cli-utils v0.36.0-flux.13 github.com/fluxcd/cli-utils v0.36.0-flux.15
github.com/fluxcd/kustomize-controller/api v1.6.0 github.com/fluxcd/kustomize-controller/api v1.6.0
github.com/fluxcd/pkg/apis/acl v0.7.0 github.com/fluxcd/pkg/apis/acl v0.9.0
github.com/fluxcd/pkg/apis/event v0.17.0 github.com/fluxcd/pkg/apis/event v0.19.0
github.com/fluxcd/pkg/apis/kustomize v1.10.0 github.com/fluxcd/pkg/apis/kustomize v1.12.0
github.com/fluxcd/pkg/apis/meta v1.12.0 github.com/fluxcd/pkg/apis/meta v1.21.0
github.com/fluxcd/pkg/auth v0.16.0 github.com/fluxcd/pkg/auth v0.30.0
github.com/fluxcd/pkg/cache v0.9.0 github.com/fluxcd/pkg/cache v0.11.0
github.com/fluxcd/pkg/http/fetch v0.16.0 github.com/fluxcd/pkg/http/fetch v0.19.0
github.com/fluxcd/pkg/kustomize v1.18.0 github.com/fluxcd/pkg/kustomize v1.21.0
github.com/fluxcd/pkg/runtime v0.60.0 github.com/fluxcd/pkg/runtime v0.83.0
github.com/fluxcd/pkg/ssa v0.48.0 github.com/fluxcd/pkg/ssa v0.53.0
github.com/fluxcd/pkg/tar v0.12.0 github.com/fluxcd/pkg/tar v0.14.0
github.com/fluxcd/pkg/testserver v0.11.0 github.com/fluxcd/pkg/testserver v0.13.0
github.com/fluxcd/source-controller/api v1.6.0 github.com/fluxcd/source-controller/api v1.7.0-rc.3
github.com/getsops/sops/v3 v3.10.2 github.com/getsops/sops/v3 v3.10.2
github.com/hashicorp/vault/api v1.16.0 github.com/google/cel-go v0.26.1
github.com/onsi/gomega v1.37.0 github.com/hashicorp/vault/api v1.20.0
github.com/onsi/gomega v1.38.2
github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/go-digest v1.0.0
github.com/ory/dockertest/v3 v3.12.0 github.com/ory/dockertest/v3 v3.12.0
github.com/spf13/pflag v1.0.6 github.com/spf13/pflag v1.0.7
golang.org/x/net v0.40.0 golang.org/x/net v0.43.0
golang.org/x/oauth2 v0.30.0 golang.org/x/oauth2 v0.30.0
k8s.io/api v0.33.0 k8s.io/api v0.34.0
k8s.io/apimachinery v0.33.0 k8s.io/apimachinery v0.34.0
k8s.io/client-go v0.33.0 k8s.io/client-go v0.34.0
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
sigs.k8s.io/controller-runtime v0.21.0 sigs.k8s.io/controller-runtime v0.22.0
sigs.k8s.io/kustomize/api v0.19.0 sigs.k8s.io/kustomize/api v0.20.1
sigs.k8s.io/yaml v1.4.0 sigs.k8s.io/yaml v1.6.0
) )
// Pin kustomize to v5.6.0 // Pin kustomize to v5.7.1
replace ( replace (
sigs.k8s.io/kustomize/api => sigs.k8s.io/kustomize/api v0.19.0 sigs.k8s.io/kustomize/api => sigs.k8s.io/kustomize/api v0.20.1
sigs.k8s.io/kustomize/kyaml => sigs.k8s.io/kustomize/kyaml v0.19.0 sigs.k8s.io/kustomize/kyaml => sigs.k8s.io/kustomize/kyaml v0.20.1
) )
// Fix CVE-2022-28948 // Fix CVE-2022-28948
replace gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1 replace gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1
require ( require (
cel.dev/expr v0.22.1 // indirect cel.dev/expr v0.24.0 // indirect
cloud.google.com/go v0.120.1 // indirect cloud.google.com/go v0.121.6 // indirect
cloud.google.com/go/auth v0.16.1 // indirect cloud.google.com/go/auth v0.16.5 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect cloud.google.com/go/compute/metadata v0.8.0 // indirect
cloud.google.com/go/iam v1.5.2 // indirect cloud.google.com/go/iam v1.5.2 // indirect
cloud.google.com/go/longrunning v0.6.7 // indirect cloud.google.com/go/longrunning v0.6.7 // indirect
cloud.google.com/go/monitoring v1.24.2 // indirect cloud.google.com/go/monitoring v1.24.2 // indirect
cloud.google.com/go/storage v1.51.0 // indirect cloud.google.com/go/storage v1.56.0 // indirect
dario.cat/mergo v1.0.1 // indirect dario.cat/mergo v1.0.1 // indirect
filippo.io/edwards25519 v1.1.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.3 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/ProtonMail/go-crypto v1.2.0 // indirect github.com/ProtonMail/go-crypto v1.2.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
github.com/aws/aws-sdk-go-v2/config v1.29.14 // indirect github.com/aws/aws-sdk-go-v2/config v1.31.6 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.6 // indirect
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.72 // indirect github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.72 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.6 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.6 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect
github.com/aws/aws-sdk-go-v2/service/ecr v1.43.3 // indirect github.com/aws/aws-sdk-go-v2/service/ecr v1.50.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.37.2 // indirect
github.com/aws/aws-sdk-go-v2/service/eks v1.73.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.6 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect
github.com/aws/aws-sdk-go-v2/service/kms v1.38.3 // indirect github.com/aws/aws-sdk-go-v2/service/kms v1.38.3 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.79.2 // indirect github.com/aws/aws-sdk-go-v2/service/s3 v1.79.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.29.1 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.38.2 // indirect
github.com/aws/smithy-go v1.22.3 // indirect github.com/aws/smithy-go v1.23.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect github.com/blang/semver v3.5.1+incompatible // indirect
github.com/blang/semver/v4 v4.0.0 // indirect github.com/blang/semver/v4 v4.0.0 // indirect
@ -110,13 +115,13 @@ require (
github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chai2010/gettext-go v1.0.3 // indirect github.com/chai2010/gettext-go v1.0.3 // indirect
github.com/cloudflare/circl v1.6.1 // indirect github.com/cloudflare/circl v1.6.1 // indirect
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect
github.com/containerd/continuity v0.4.5 // indirect github.com/containerd/continuity v0.4.5 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/cli v28.1.1+incompatible // indirect github.com/docker/cli v28.3.3+incompatible // indirect
github.com/docker/docker v28.1.1+incompatible // indirect github.com/docker/docker v28.3.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.2 // indirect github.com/docker/docker-credential-helpers v0.9.3 // indirect
github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect
github.com/emicklei/go-restful/v3 v3.12.2 // indirect github.com/emicklei/go-restful/v3 v3.12.2 // indirect
@ -126,47 +131,46 @@ require (
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
github.com/fatih/color v1.18.0 // indirect github.com/fatih/color v1.18.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fluxcd/pkg/envsubst v1.4.0 // indirect github.com/fluxcd/pkg/envsubst v1.5.0 // indirect
github.com/fluxcd/pkg/sourceignore v0.12.0 // indirect github.com/fluxcd/pkg/sourceignore v0.14.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.8.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/getsops/gopgagent v0.0.0-20241224165529-7044f28e491e // indirect github.com/getsops/gopgagent v0.0.0-20241224165529-7044f28e491e // indirect
github.com/go-errors/errors v1.5.1 // indirect github.com/go-errors/errors v1.5.1 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-git/go-git/v5 v5.16.0 // indirect github.com/go-git/go-git/v5 v5.16.2 // indirect
github.com/go-jose/go-jose/v4 v4.1.0 // indirect github.com/go-jose/go-jose/v4 v4.1.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-logr/zapr v1.3.0 // indirect github.com/go-logr/zapr v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.21.1 // indirect github.com/go-openapi/jsonpointer v0.21.1 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.1 // indirect github.com/go-openapi/swag v0.23.1 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/google/btree v1.1.3 // indirect github.com/google/btree v1.1.3 // indirect
github.com/google/cel-go v0.23.2 // indirect github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect
github.com/google/go-containerregistry v0.20.3 // indirect github.com/google/go-containerregistry v0.20.6 // indirect
github.com/google/s2a-go v0.1.9 // indirect github.com/google/s2a-go v0.1.9 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/googleapis/gax-go/v2 v2.15.0 // indirect
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
github.com/goware/prefixer v0.0.0-20160118172347-395022866408 // indirect github.com/goware/prefixer v0.0.0-20160118172347-395022866408 // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 // indirect github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 // indirect
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
github.com/hashicorp/go-sockaddr v1.0.7 // indirect github.com/hashicorp/go-sockaddr v1.0.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-7 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
@ -186,7 +190,7 @@ require (
github.com/moby/sys/user v0.4.0 // indirect github.com/moby/sys/user v0.4.0 // indirect
github.com/moby/term v0.5.2 // indirect github.com/moby/term v0.5.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
@ -197,10 +201,11 @@ require (
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/prometheus/client_golang v1.22.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.23.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.63.0 // indirect github.com/prometheus/common v0.65.0 // indirect
github.com/prometheus/procfs v0.16.1 // indirect github.com/prometheus/procfs v0.17.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect
@ -216,43 +221,46 @@ require (
github.com/zeebo/blake3 v0.2.4 // indirect github.com/zeebo/blake3 v0.2.4 // indirect
github.com/zeebo/errs v1.4.0 // indirect github.com/zeebo/errs v1.4.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.35.0 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.38.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.35.0 // indirect go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.38.0 // indirect go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
golang.org/x/sync v0.14.0 // indirect golang.org/x/sync v0.16.0 // indirect
golang.org/x/sys v0.33.0 // indirect golang.org/x/sys v0.35.0 // indirect
golang.org/x/term v0.32.0 // indirect golang.org/x/term v0.34.0 // indirect
golang.org/x/text v0.25.0 // indirect golang.org/x/text v0.28.0 // indirect
golang.org/x/time v0.11.0 // indirect golang.org/x/time v0.12.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
google.golang.org/api v0.230.0 // indirect google.golang.org/api v0.248.0 // indirect
google.golang.org/genproto v0.0.0-20250425173222-7b384671a197 // indirect google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250425173222-7b384671a197 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/grpc v1.72.0 // indirect google.golang.org/grpc v1.75.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect google.golang.org/protobuf v1.36.8 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.33.0 // indirect k8s.io/apiextensions-apiserver v0.34.0 // indirect
k8s.io/cli-runtime v0.33.0 // indirect k8s.io/cli-runtime v0.34.0 // indirect
k8s.io/component-base v0.33.0 // indirect k8s.io/component-base v0.34.0 // indirect
k8s.io/klog/v2 v2.130.1 // indirect k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
k8s.io/kubectl v0.33.0 // indirect k8s.io/kubectl v0.34.0 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect sigs.k8s.io/kustomize/kyaml v0.20.1 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
) )

442
go.sum
View File

@ -1,27 +1,27 @@
c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805 h1:u2qwJeEvnypw+OCPUHmoZE3IqwfuN5kgDfo5MLzpNM0= c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805 h1:u2qwJeEvnypw+OCPUHmoZE3IqwfuN5kgDfo5MLzpNM0=
c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805/go.mod h1:FomMrUJ2Lxt5jCLmZkG3FHa72zUprnhd3v/Z18Snm4w= c2sp.org/CCTV/age v0.0.0-20240306222714-3ec4d716e805/go.mod h1:FomMrUJ2Lxt5jCLmZkG3FHa72zUprnhd3v/Z18Snm4w=
cel.dev/expr v0.22.1 h1:xoFEsNh972Yzey8N9TCPx2nDvMN7TMhQEzxLuj/iRrI= cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY=
cel.dev/expr v0.22.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
cloud.google.com/go v0.120.1 h1:Z+5V7yd383+9617XDCyszmK5E4wJRJL+tquMfDj9hLM= cloud.google.com/go v0.121.6 h1:waZiuajrI28iAf40cWgycWNgaXPO06dupuS+sgibK6c=
cloud.google.com/go v0.120.1/go.mod h1:56Vs7sf/i2jYM6ZL9NYlC82r04PThNcPS5YgFmb0rp8= cloud.google.com/go v0.121.6/go.mod h1:coChdst4Ea5vUpiALcYKXEpR1S9ZgXbhEzzMcMR66vI=
cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU= cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI=
cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI= cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ=
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.8.0 h1:HxMRIbao8w17ZX6wBnjhcDkW6lTFpgcaobyVfZWqRLA=
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= cloud.google.com/go/compute/metadata v0.8.0/go.mod h1:sYOGTp851OV9bOFJ9CH7elVvyzopvWQFNNghtDQ/Biw=
cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8=
cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE=
cloud.google.com/go/kms v1.21.2 h1:c/PRUSMNQ8zXrc1sdAUnsenWWaNXN+PzTXfXOcSFdoE= cloud.google.com/go/kms v1.22.0 h1:dBRIj7+GDeeEvatJeTB19oYZNV0aj6wEqSIT/7gLqtk=
cloud.google.com/go/kms v1.21.2/go.mod h1:8wkMtHV/9Z8mLXEXr1GK7xPSBdi6knuLXIhqjuWcI6w= cloud.google.com/go/kms v1.22.0/go.mod h1:U7mf8Sva5jpOb4bxYZdtw/9zsbIjrklYwPcvMk34AL8=
cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc=
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE= cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM= cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM=
cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U= cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U=
cloud.google.com/go/storage v1.51.0 h1:ZVZ11zCiD7b3k+cH5lQs/qcNaoSz3U9I0jgwVzqDlCw= cloud.google.com/go/storage v1.56.0 h1:iixmq2Fse2tqxMbWhLWC9HfBj1qdxqAmiK8/eqtsLxI=
cloud.google.com/go/storage v1.51.0/go.mod h1:YEJfu/Ki3i5oHC/7jyTgsGZwdQ8P9hqMqvpi5kRKGgc= cloud.google.com/go/storage v1.56.0/go.mod h1:Tpuj6t4NweCLzlNbw9Z9iwxEkrSem20AetIeH/shgVU=
cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4= cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4=
cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI= cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
@ -32,14 +32,18 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0 h1:ci6Yd6nysBRLEodoziB6ah1+YOzZbZk+NYneoA6q+6E=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.0/go.mod h1:QyVsSSN64v5TGltphKLQ2sQxe4OBQg0J1eKRcVBnfgE=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 h1:OVoM452qUFBrX+URdH3VpR299ma4kfom0yB0URYky9g= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0 h1:MhRfI58HblXzCtWEZCO0feHs8LweePB3s90r7WaR1KU=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0/go.mod h1:kUjrAo8bgEwLeZ/CmHqNl3Z/kPm7y6FKfxxK0izYUg4= github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0/go.mod h1:okZ+ZURbArNdlJ+ptXoyHNuOETzOl1Oww19rm8I2WLA=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.3 h1:ldKsKtEIblsgsr6mPwrd9yRntoX6uLz/K89wsldwx/k=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA= github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.3/go.mod h1:MAm7bk0oDLmD8yIkvfbxPW04fxzphPyL+7GzwHxOp6Y=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0 h1:figxyQZXzZQIcP3njhC68bYUiTw45J8/SsHaLW8Ax0M=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0/go.mod h1:TmlMW4W5OvXOmOyKNnor8nlMMiO1ctIyzmHme/VHsrA=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZba3YZqeTNJPtvqZoBu1sBN/L4sry+u2U3Y75w= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZba3YZqeTNJPtvqZoBu1sBN/L4sry+u2U3Y75w=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI=
@ -51,16 +55,18 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mo
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 h1:ErKg/3iS1AKcTkf3yixlZ54f9U1rljCkQyEXWUnIUxc= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 h1:UQUsRi8WTzhZntp5313l+CHIAT95ojUI2lpP/ExlZa4=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0/go.mod h1:Cz6ft6Dkn3Et6l2v2a9/RpN7epQ1GtDlO6lj8bEcOvw=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 h1:fYE9p3esPxA/C0rQ0AHhP0drtPXDRhaWiwg1DPqO7IU= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0/go.mod h1:BnBReJLvVYx2CS/UHOgVz2BXKXD9wsQPxZug20nZhd0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0 h1:OqVGm6Ei3x5+yZmSJG1Mh2NwHvpVmZ08CB5qJhT9Nuk= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.51.0/go.mod h1:SZiPHWGOOk3bl8tkevxkoiwPgsIl6CwrWcbwjfHZpdM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0/go.mod h1:jUZ5LYlw40WMd07qxcQJD5M40aUxrfwqQX1g7zxYnrQ=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 h1:6/0iUd0xrnX7qt+mLNRwg5c0PGv8wpE8K90ryANQwMI= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 h1:Ron4zCA/yk6U7WOBXhTJcDpsUBG9npumK6xw2auFltQ=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0/go.mod h1:otE2jQekW/PqXk1Awf5lmfokJx4uwuqcj1ab5SpGeW0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0/go.mod h1:cSgYe11MCNYunTnRXrKiR/tHc0eoKjICUuWpNZoVCOo=
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
@ -71,48 +77,52 @@ github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= github.com/aws/aws-sdk-go-v2 v1.38.3 h1:B6cV4oxnMs45fql4yRH+/Po/YU+597zgWqvDpYMturk=
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= github.com/aws/aws-sdk-go-v2 v1.38.3/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14=
github.com/aws/aws-sdk-go-v2/config v1.29.14 h1:f+eEi/2cKCg9pqKBoAIwRGzVb70MRKqWX4dg1BDcSJM= github.com/aws/aws-sdk-go-v2/config v1.31.6 h1:a1t8fXY4GT4xjyJExz4knbuoxSCacB5hT/WgtfPyLjo=
github.com/aws/aws-sdk-go-v2/config v1.29.14/go.mod h1:wVPHWcIFv3WO89w0rE10gzf17ZYy+UVS1Geq8Iei34g= github.com/aws/aws-sdk-go-v2/config v1.31.6/go.mod h1:5ByscNi7R+ztvOGzeUaIu49vkMk2soq5NaH5PYe33MQ=
github.com/aws/aws-sdk-go-v2/credentials v1.17.67 h1:9KxtdcIA/5xPNQyZRgUSpYOE6j9Bc4+D7nZua0KGYOM= github.com/aws/aws-sdk-go-v2/credentials v1.18.10 h1:xdJnXCouCx8Y0NncgoptztUocIYLKeQxrCgN6x9sdhg=
github.com/aws/aws-sdk-go-v2/credentials v1.17.67/go.mod h1:p3C44m+cfnbv763s52gCqrjaqyPikj9Sg47kUVaNZQQ= github.com/aws/aws-sdk-go-v2/credentials v1.18.10/go.mod h1:7tQk08ntj914F/5i9jC4+2HQTAuJirq7m1vZVIhEkWs=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.6 h1:wbjnrrMnKew78/juW7I2BtKQwa1qlf6EjQgS69uYY14=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.6/go.mod h1:AtiqqNrDioJXuUgz3+3T0mBWN7Hro2n9wll2zRUc0ww=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.72 h1:PcKMOZfp+kNtJTw2HF2op6SjDvwPBYRvz0Y24PQLUR4= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.72 h1:PcKMOZfp+kNtJTw2HF2op6SjDvwPBYRvz0Y24PQLUR4=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.72/go.mod h1:vq7/m7dahFXcdzWVOvvjasDI9RcsD3RsTfHmDundJYg= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.72/go.mod h1:vq7/m7dahFXcdzWVOvvjasDI9RcsD3RsTfHmDundJYg=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.6 h1:uF68eJA6+S9iVr9WgX1NaRGyQ/6MdIyc4JNUo6TN1FA=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.6/go.mod h1:qlPeVZCGPiobx8wb1ft0GHT5l+dc6ldnwInDFaMvC7Y=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.6 h1:pa1DEC6JoI0zduhZePp3zmhWvk/xxm4NB8Hy/Tlsgos=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.6/go.mod h1:gxEjPebnhWGJoaDdtDkA0JX46VRg1wcTHYe63OfX5pE=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcuz+RjeziUtNJackkM= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 h1:ZNTqv4nIdE/DiBfUUfXcLZ/Spcuz+RjeziUtNJackkM=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs= github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34/go.mod h1:zf7Vcd1ViW7cPqYWEHLHJkS50X0JS2IKz9Cgaj6ugrs=
github.com/aws/aws-sdk-go-v2/service/ecr v1.43.3 h1:YyH8Hk73bYzdbvf6S8NF5z/fb/1stpiMnFSfL6jSfRA= github.com/aws/aws-sdk-go-v2/service/ecr v1.50.1 h1:lcwFjRx3C/hBxJzoWkD6DIG2jeB+mzLmFVBFVOadxxE=
github.com/aws/aws-sdk-go-v2/service/ecr v1.43.3/go.mod h1:iQ1skgw1XRK+6Lgkb0I9ODatAP72WoTILh0zXQ5DtbU= github.com/aws/aws-sdk-go-v2/service/ecr v1.50.1/go.mod h1:qt9OL5kXqWoSub4QAkOF74mS3M2zOTNxMODqgwEUjt8=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.37.2 h1:EfatDVSMFxaS5TiR0C0zssQU1Nm+rGx3VbUGIH1y274=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.37.2/go.mod h1:oRy1IEgzXtOkEk4B/J7HZbXUC258drDLtkmc++lN7IA=
github.com/aws/aws-sdk-go-v2/service/eks v1.73.1 h1:Txq5jxY/ao+2Vx/kX9+65WTqkzCnxSlXnwIj+Cr/fng=
github.com/aws/aws-sdk-go-v2/service/eks v1.73.1/go.mod h1:+hYFg3laewH0YCfJRv+o5R3bradDKmFIm/uaiaD1U7U=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 h1:lguz0bmOoGzozP9XfRJR1QIayEYo+2vP/No3OfLF0pU= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 h1:lguz0bmOoGzozP9XfRJR1QIayEYo+2vP/No3OfLF0pU=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0/go.mod h1:iu6FSzgt+M2/x3Dk8zhycdIcHjEFb36IS8HVUVFoMg0=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.6 h1:LHS1YAIJXJ4K9zS+1d/xa9JAA9sL2QyXIQCQFQW/X08=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.6/go.mod h1:c9PCiTEuh0wQID5/KqA32J+HAgZxN9tOGXKCiYJjTZI=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 h1:moLQUoVq91LiqT1nbvzDukyqAlCv89ZmwaHw/ZFlFZg=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15/go.mod h1:ZH34PJUc8ApjBIfgQCFvkWcUDBtl/WTD+uiYHjd8igA=
github.com/aws/aws-sdk-go-v2/service/kms v1.38.3 h1:RivOtUH3eEu6SWnUMFHKAW4MqDOzWn1vGQ3S38Y5QMg= github.com/aws/aws-sdk-go-v2/service/kms v1.38.3 h1:RivOtUH3eEu6SWnUMFHKAW4MqDOzWn1vGQ3S38Y5QMg=
github.com/aws/aws-sdk-go-v2/service/kms v1.38.3/go.mod h1:cQn6tAF77Di6m4huxovNM7NVAozWTZLsDRp9t8Z/WYk= github.com/aws/aws-sdk-go-v2/service/kms v1.38.3/go.mod h1:cQn6tAF77Di6m4huxovNM7NVAozWTZLsDRp9t8Z/WYk=
github.com/aws/aws-sdk-go-v2/service/s3 v1.79.2 h1:tWUG+4wZqdMl/znThEk9tcCy8tTMxq8dW0JTgamohrY= github.com/aws/aws-sdk-go-v2/service/s3 v1.79.2 h1:tWUG+4wZqdMl/znThEk9tcCy8tTMxq8dW0JTgamohrY=
github.com/aws/aws-sdk-go-v2/service/s3 v1.79.2/go.mod h1:U5SNqwhXB3Xe6F47kXvWihPl/ilGaEDe8HD/50Z9wxc= github.com/aws/aws-sdk-go-v2/service/s3 v1.79.2/go.mod h1:U5SNqwhXB3Xe6F47kXvWihPl/ilGaEDe8HD/50Z9wxc=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 h1:1Gw+9ajCV1jogloEv1RRnvfRFia2cL6c9cuKV2Ps+G8= github.com/aws/aws-sdk-go-v2/service/sso v1.29.1 h1:8OLZnVJPvjnrxEwHFg9hVUof/P4sibH+Ea4KKuqAGSg=
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= github.com/aws/aws-sdk-go-v2/service/sso v1.29.1/go.mod h1:27M3BpVi0C02UiQh1w9nsBEit6pLhlaH3NHna6WUbDE=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 h1:hXmVKytPfTy5axZ+fYbR5d0cFmC3JvwLm5kM83luako= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.2 h1:gKWSTnqudpo8dAxqBqZnDoDWCiEh/40FziUjr/mo6uA=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.2/go.mod h1:x7+rkNmRoEN1U13A6JE2fXne9EWyJy54o3n6d4mGaXQ=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 h1:1XuUZ8mYJw9B6lzAkXhqHlJd/XvaX32evhproijJEZY= github.com/aws/aws-sdk-go-v2/service/sts v1.38.2 h1:YZPjhyaGzhDQEvsffDEcpycq49nl7fiGcfJTIo8BszI=
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= github.com/aws/aws-sdk-go-v2/service/sts v1.38.2/go.mod h1:2dIN8qhQfv37BdUYGgEC8Q3tteM3zFxTI1MLO2O3J3c=
github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE=
github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
@ -127,15 +137,16 @@ github.com/chai2010/gettext-go v1.0.3 h1:9liNh8t+u26xl5ddmWLmsOsdNLwkdRTg5AG+JnT
github.com/chai2010/gettext-go v1.0.3/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/chai2010/gettext-go v1.0.3/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f h1:C5bqEmzEPLsHm9Mv73lSE9e9bKV23aB1vxOsmZrkl3k= github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls=
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk= github.com/coreos/go-oidc/v3 v3.15.0 h1:R6Oz8Z4bqWR7VFQ+sPSvZPQv4x8M+sJkDO5ojgwlyAg=
github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU= github.com/coreos/go-oidc/v3 v3.15.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
@ -144,16 +155,14 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5MdSpiItp8k= github.com/docker/cli v28.3.3+incompatible h1:fp9ZHAr1WWPGdIWBM1b3zLtgCF+83gRdVMTJsUeiyAo=
github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v28.3.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I= github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI=
github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@ -178,42 +187,42 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fluxcd/cli-utils v0.36.0-flux.13 h1:2X5yjz/rk9mg7+bMFBDZKGKzeZpAmY2s6iwbNZz7OzM= github.com/fluxcd/cli-utils v0.36.0-flux.15 h1:Et5QLnIpRjj+oZtM9gEybkAaoNsjysHq0y1253Ai94Y=
github.com/fluxcd/cli-utils v0.36.0-flux.13/go.mod h1:b2iSoIeDTtjfCB0IKtGgqlhhvWa1oux3e90CjOf81oA= github.com/fluxcd/cli-utils v0.36.0-flux.15/go.mod h1:AqRUmWIfNE7cdL6NWSGF0bAlypGs+9x5UQ2qOtlEzv4=
github.com/fluxcd/pkg/apis/acl v0.7.0 h1:dMhZJH+g6ZRPjs4zVOAN9vHBd1DcavFgcIFkg5ooOE0= github.com/fluxcd/pkg/apis/acl v0.9.0 h1:wBpgsKT+jcyZEcM//OmZr9RiF8klL3ebrDp2u2ThsnA=
github.com/fluxcd/pkg/apis/acl v0.7.0/go.mod h1:uv7pXXR/gydiX4MUwlQa7vS8JONEDztynnjTvY3JxKQ= github.com/fluxcd/pkg/apis/acl v0.9.0/go.mod h1:TttNS+gocsGLwnvmgVi3/Yscwqrjc17+vhgYfqkfrV4=
github.com/fluxcd/pkg/apis/event v0.17.0 h1:foEINE++pCJlWVhWjYDXfkVmGKu8mQ4BDBlbYi5NU7M= github.com/fluxcd/pkg/apis/event v0.19.0 h1:ZJU2voontkzp5rNYA4JMOu40S4tRcrWi4Do59EnyFwg=
github.com/fluxcd/pkg/apis/event v0.17.0/go.mod h1:0fLhLFiHlRTDKPDXdRnv+tS7mCMIQ0fJxnEfmvGM/5A= github.com/fluxcd/pkg/apis/event v0.19.0/go.mod h1:deuIyUb6lh+Z1Ccvwwxhm1wNM3kpSo+vF1IgRnpaZfQ=
github.com/fluxcd/pkg/apis/kustomize v1.10.0 h1:47EeSzkQvlQZdH92vHMe2lK2iR8aOSEJq95avw5idts= github.com/fluxcd/pkg/apis/kustomize v1.12.0 h1:KvZN6xwgP/dNSeckL4a/Uv715XqiN1C3xS+jGcPejtE=
github.com/fluxcd/pkg/apis/kustomize v1.10.0/go.mod h1:UsqMV4sqNa1Yg0pmTsdkHRJr7bafBOENIJoAN+3ezaQ= github.com/fluxcd/pkg/apis/kustomize v1.12.0/go.mod h1:OojLxIdKm1JAAdh3sL4j4F+vfrLKb7kq1vr8bpyEKgg=
github.com/fluxcd/pkg/apis/meta v1.12.0 h1:XW15TKZieC2b7MN8VS85stqZJOx+/b8jATQ/xTUhVYg= github.com/fluxcd/pkg/apis/meta v1.21.0 h1:R+bN02chcs0HUmyVDQhqe/FHmYLjipVDMLnyYfNX850=
github.com/fluxcd/pkg/apis/meta v1.12.0/go.mod h1:+son1Va60x2eiDcTwd7lcctbI6C+K3gM7R+ULmEq1SI= github.com/fluxcd/pkg/apis/meta v1.21.0/go.mod h1:XUAEUgT4gkWDAEN79E141tmL+v4SV50tVZ/Ojpc/ueg=
github.com/fluxcd/pkg/auth v0.16.0 h1:YEjSaNqlpYoXfoFAGhU/Z8y0322nGsT24W6zCh+sbGw= github.com/fluxcd/pkg/auth v0.30.0 h1:7JMnY1ClArvOsadt6hOxceu8Q2hLsYHFMt0DV3BQl4Q=
github.com/fluxcd/pkg/auth v0.16.0/go.mod h1:+BRnAO61Nr6fACEjJS6eNRdOk1nXhX/FCPylYn1ypNc= github.com/fluxcd/pkg/auth v0.30.0/go.mod h1:me38o1nDfSLw6YvnkT9Ce/zqJZICZSA7j5pNMR3JUbc=
github.com/fluxcd/pkg/cache v0.9.0 h1:EGKfOLMG3fOwWnH/4Axl5xd425mxoQbZzlZoLfd8PDk= github.com/fluxcd/pkg/cache v0.11.0 h1:fsE8S+una21fSNw4MDXGUIf0Gf1J+pqa4RbsVKf2aTI=
github.com/fluxcd/pkg/cache v0.9.0/go.mod h1:jMwabjWfsC5lW8hE7NM3wtGNwSJ38Javx6EKbEi7INU= github.com/fluxcd/pkg/cache v0.11.0/go.mod h1:2RTIU6PsJniHmfnllQWFEo7fa5V8KQlnMgn4o0sme40=
github.com/fluxcd/pkg/envsubst v1.4.0 h1:pYsb6wrmXOSfHXuXQHaaBBMt3LumhgCb8SMdBNAwV/U= github.com/fluxcd/pkg/envsubst v1.5.0 h1:S07mo+MkGhptdHA4pRze5HPKlc8tHxKswNdcMZi1WDY=
github.com/fluxcd/pkg/envsubst v1.4.0/go.mod h1:zSDFO3Wawi+vI2NPxsMQp+EkIsz/85MNg/s1Wzmqt+s= github.com/fluxcd/pkg/envsubst v1.5.0/go.mod h1:c3a8DYI855sZUubHFYQbjfjop6Wu4/zg1cLyf7SnCes=
github.com/fluxcd/pkg/http/fetch v0.16.0 h1:XzhBTSK5HNdAPEnEGMJHwtoN2LfqQ9QFDsu3DGzl908= github.com/fluxcd/pkg/http/fetch v0.19.0 h1:dRdErv8X2t/VKnh5kFRZrlqKzbq9Gh0n/z3XArjdLho=
github.com/fluxcd/pkg/http/fetch v0.16.0/go.mod h1:+A+yrOzwA5436ufD8NPeCCQFNzk4metoPUgRVCozvzw= github.com/fluxcd/pkg/http/fetch v0.19.0/go.mod h1:z32w8nezoBvQfMogCkhAoqKToOZzPSbIW0PeN2fKXqA=
github.com/fluxcd/pkg/kustomize v1.18.0 h1:wWK+qYwmBmba3N3VAqZ9ijnfVGGaIjcaHWo033URZTw= github.com/fluxcd/pkg/kustomize v1.21.0 h1:J+OcRTBWJcOwqhNW2oZyPHN7u54qBpScYLYnqTiM3gE=
github.com/fluxcd/pkg/kustomize v1.18.0/go.mod h1:Ij9722MdWIE6B1EPg2ZJUf6npycgfRfN4Lohi7D/Kic= github.com/fluxcd/pkg/kustomize v1.21.0/go.mod h1:drVtmqUCJc/0a178vjl67DSePQahOBI+aO4x1kzV13M=
github.com/fluxcd/pkg/runtime v0.60.0 h1:d++EkV3FlycB+bzakB5NumwY4J8xts8i7lbvD6jBLeU= github.com/fluxcd/pkg/runtime v0.83.0 h1:XzpwKzo7GqfBE/BKpxG5B4U7cUnojnB407S9Dpp6oLU=
github.com/fluxcd/pkg/runtime v0.60.0/go.mod h1:UeU0/eZLErYC/1bTmgzBfNXhiHy9fuQzjfLK0HxRgxY= github.com/fluxcd/pkg/runtime v0.83.0/go.mod h1:r8KLvXRguKtpLAa66fA19rIbwPViXm8az038IUabYvw=
github.com/fluxcd/pkg/sourceignore v0.12.0 h1:jCIe6d50rQ3wdXPF0+PhhqN0XrTRIq3upMomPelI8Mw= github.com/fluxcd/pkg/sourceignore v0.14.0 h1:ZiZzbXtXb/Qp7I7JCStsxOlX8ri8rWwCvmvIrJ0UzQQ=
github.com/fluxcd/pkg/sourceignore v0.12.0/go.mod h1:dc0zvkuXM5OgL/b3IkrVuwvPjj1zJn4NBUMH45uJ4Y0= github.com/fluxcd/pkg/sourceignore v0.14.0/go.mod h1:E3zKvyTyB+oQKqm/2I/jS6Rrt3B7fNuig/4bY2vi3bg=
github.com/fluxcd/pkg/ssa v0.48.0 h1:DW+4DG8L/yZEi30UltOEXPB1d/ZFn4HfVhpJQp5oc2o= github.com/fluxcd/pkg/ssa v0.53.0 h1:EtKFAYWXohIGkzPhIrv8NbV5zYr4iZHY6DaNxMR9+bU=
github.com/fluxcd/pkg/ssa v0.48.0/go.mod h1:T50TO0U2obLodZnrFgOrxollfBEy4V673OkM2aTUF1c= github.com/fluxcd/pkg/ssa v0.53.0/go.mod h1:0IZxnnH8XkDkFzWjoMJsFEwPIWPOk3Gc/WR5+gT/KgI=
github.com/fluxcd/pkg/tar v0.12.0 h1:og6F+ivnWNRbNJSq0ukCTVs7YrGIlzjxSVZU+E8NprM= github.com/fluxcd/pkg/tar v0.14.0 h1:9Gku8FIvPt2bixKldZnzXJ/t+7SloxePlzyVGOK8GVQ=
github.com/fluxcd/pkg/tar v0.12.0/go.mod h1:Ra5Cj++MD5iCy7bZGKJJX3GpOeMPv+ZDkPO9bBwpDeU= github.com/fluxcd/pkg/tar v0.14.0/go.mod h1:+rOWYk93qLEJ8WwmkvJOkB8i0dna1mrwJFybE8i9Udo=
github.com/fluxcd/pkg/testserver v0.11.0 h1:a/kxpFqv7XQxZjwVPP3voooRmSd/3ipLVolK0xUIxXQ= github.com/fluxcd/pkg/testserver v0.13.0 h1:xEpBcEYtD7bwvZ+i0ZmChxKkDo/wfQEV3xmnzVybSSg=
github.com/fluxcd/pkg/testserver v0.11.0/go.mod h1:E8LAH1jW9uClFjTRN27Y/gCCSrzNVx1/w/0NxKuNcas= github.com/fluxcd/pkg/testserver v0.13.0/go.mod h1:akRYv3FLQUsme15na9ihECRG6hBuqni4XEY9W8kzs8E=
github.com/fluxcd/source-controller/api v1.6.0 h1:IxfjUczJ2pzbXIef6iQ0RHEH4AYA9anJfTGK8dzwODM= github.com/fluxcd/source-controller/api v1.7.0-rc.3 h1:+9cd//77LAgp0XRe97CXUaPnu78jsRNWTXq95GHGhgc=
github.com/fluxcd/source-controller/api v1.6.0/go.mod h1:ZJcAi0nemsnBxjVgmJl0WQzNvB0rMETxQMTdoFosmMw= github.com/fluxcd/source-controller/api v1.7.0-rc.3/go.mod h1:sbJibK4Ik+2AuTRRLXPA+n2u6nLUIGaxC07ava+RqeM=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/getsops/gopgagent v0.0.0-20241224165529-7044f28e491e h1:y/1nzrdF+RPds4lfoEpNhjfmzlgZtPqyO3jMzrqDQws= github.com/getsops/gopgagent v0.0.0-20241224165529-7044f28e491e h1:y/1nzrdF+RPds4lfoEpNhjfmzlgZtPqyO3jMzrqDQws=
github.com/getsops/gopgagent v0.0.0-20241224165529-7044f28e491e/go.mod h1:awFzISqLJoZLm+i9QQ4SgMNHDqljH6jWV0B36V5MrUM= github.com/getsops/gopgagent v0.0.0-20241224165529-7044f28e491e/go.mod h1:awFzISqLJoZLm+i9QQ4SgMNHDqljH6jWV0B36V5MrUM=
github.com/getsops/sops/v3 v3.10.2 h1:7t7lBXFcXJPsDMrpYoI36r8xIhjWUmEc8Qdjuwyo+WY= github.com/getsops/sops/v3 v3.10.2 h1:7t7lBXFcXJPsDMrpYoI36r8xIhjWUmEc8Qdjuwyo+WY=
@ -224,13 +233,13 @@ github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66D
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM= github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git/v5 v5.16.0 h1:k3kuOEpkc0DeY7xlL6NaaNg39xdgQbtH5mwCafHO9AQ= github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
github.com/go-git/go-git/v5 v5.16.0/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
github.com/go-jose/go-jose/v4 v4.1.0 h1:cYSYxd3pw5zd2FSXk2vGdn9igQU2PS8MuxrCOCl0FdY= github.com/go-jose/go-jose/v4 v4.1.1 h1:JYhSgy4mXXzAdF3nUx3ygx347LRXJRrpgyU3adRmkAI=
github.com/go-jose/go-jose/v4 v4.1.0/go.mod h1:GG/vqmYm3Von2nYiB2vGTXzdoNKE5tix5tuc6iAd+sw= github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
@ -247,32 +256,32 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ=
github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI= github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU=
github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI= github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc=
github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 h1:xhMrHhTJ6zxu3gA4enFM9MLn9AY7613teCdFnlUVbSQ=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
@ -281,8 +290,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
github.com/goware/prefixer v0.0.0-20160118172347-395022866408 h1:Y9iQJfEqnN3/Nce9cOegemcy/9Ai5k3huT6E80F3zaw= github.com/goware/prefixer v0.0.0-20160118172347-395022866408 h1:Y9iQJfEqnN3/Nce9cOegemcy/9Ai5k3huT6E80F3zaw=
@ -298,8 +307,8 @@ github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB1
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 h1:U+kC2dOhMFQctRfhK0gRctKAPTloZdMU5ZJxaesJ/VM= github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 h1:U+kC2dOhMFQctRfhK0gRctKAPTloZdMU5ZJxaesJ/VM=
@ -308,10 +317,10 @@ github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw= github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw=
github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw= github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
github.com/hashicorp/vault/api v1.16.0 h1:nbEYGJiAPGzT9U4oWgaaB0g+Rj8E59QuHKyA5LhwQN4= github.com/hashicorp/vault/api v1.20.0 h1:KQMHElgudOsr+IbJgmbjHnCTxEpKs9LnozA1D3nozU4=
github.com/hashicorp/vault/api v1.16.0/go.mod h1:KhuUhzOD8lDSk29AtzNjgAu2kxRA9jL9NAbkFlqvkBA= github.com/hashicorp/vault/api v1.20.0/go.mod h1:GZ4pcjfzoOWpkJ3ijHNpEoAxKEsBJnVljyTe3jM2Sms=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
@ -361,18 +370,19 @@ github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFL
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0=
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus= github.com/onsi/ginkgo/v2 v2.25.2 h1:hepmgwx1D+llZleKQDMEvy8vIlCxMGt7W5ZxDjIEhsw=
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8= github.com/onsi/ginkgo/v2 v2.25.2/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE=
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
github.com/opencontainers/go-digest v1.0.1-0.20220411205349-bde1400a84be h1:f2PlhC9pm5sqpBZFvnAoKj+KzXRzbjFMA+TqXfJdgho= github.com/opencontainers/go-digest v1.0.1-0.20220411205349-bde1400a84be h1:f2PlhC9pm5sqpBZFvnAoKj+KzXRzbjFMA+TqXfJdgho=
github.com/opencontainers/go-digest v1.0.1-0.20220411205349-bde1400a84be/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/go-digest v1.0.1-0.20220411205349-bde1400a84be/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/go-digest/blake3 v0.0.0-20250116041648-1e56c6daea3b h1:nAiL9bmUK4IzFrKoVMRykv0iYGdoit5vpbPaVCZ+fI4= github.com/opencontainers/go-digest/blake3 v0.0.0-20250116041648-1e56c6daea3b h1:nAiL9bmUK4IzFrKoVMRykv0iYGdoit5vpbPaVCZ+fI4=
@ -398,16 +408,14 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
@ -420,8 +428,9 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M=
github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE=
github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g=
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
@ -439,8 +448,8 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ= github.com/urfave/cli v1.22.16 h1:MH0k6uJxdwdeWQTwhSO42Pwr4YLrNLwBtg1MRgTqPdQ=
github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po= github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
@ -466,24 +475,24 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/detectors/gcp v1.35.0 h1:bGvFt68+KTiAKFlacHW6AhA56GF2rS0bdD3aJYEnmzA= go.opentelemetry.io/contrib/detectors/gcp v1.38.0 h1:ZoYbqX7OaA/TAikspPl3ozPI6iY6LiIY9I8cUfm+pJs=
go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA= go.opentelemetry.io/contrib/detectors/gcp v1.38.0/go.mod h1:SU+iU7nu5ud4oCb3LQOhIZ3nRLj6FNVrKgtflbaf2ts=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0 h1:rixTyDGXFxRy1xzhKrotaHy3/KXdPhlWARrCgK+eqUY=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.36.0/go.mod h1:dowW6UsM9MKbJq5JTz2AMVp3/5iW5I/TStsk8S+CfHw=
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@ -492,11 +501,15 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@ -505,15 +518,15 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -521,40 +534,42 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0=
gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
google.golang.org/api v0.230.0 h1:2u1hni3E+UXAXrONrrkfWpi/V6cyKVAbfGVeGtC3OxM= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
google.golang.org/api v0.230.0/go.mod h1:aqvtoMk7YkiXx+6U12arQFExiRV9D/ekvMCwCd/TksQ= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto v0.0.0-20250425173222-7b384671a197 h1:qWb9n6MA4nHA/g2varvEG/jTCs8zUuSa+5VqFgX2K+0= google.golang.org/api v0.248.0 h1:hUotakSkcwGdYUqzCRc5yGYsg4wXxpkKlW5ryVqvC1Y=
google.golang.org/genproto v0.0.0-20250425173222-7b384671a197/go.mod h1:Cej/8iHf9mPl71o/a+R1rrvSFrAAVCUFX9s/sbNttBc= google.golang.org/api v0.248.0/go.mod h1:yAFUAF56Li7IuIQbTFoLwXTCI6XCFKueOlS7S9e4F9k=
google.golang.org/genproto/googleapis/api v0.0.0-20250425173222-7b384671a197 h1:9DuBh3k1jUho2DHdxH+kbJwthIAq02vGvZNrD2ggF+Y= google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
google.golang.org/genproto/googleapis/api v0.0.0-20250425173222-7b384671a197/go.mod h1:Cd8IzgPo5Akum2c9R6FsXNaZbH3Jpa2gpHlW89FqlyQ= google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197 h1:29cjnHVylHwTzH66WfFZqgSQgnxzvWE+jvBwpZCLRxY= google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074 h1:mVXdvnmR3S3BQOqHECm9NGMjYiRtEvDYcqAqedTXY6s=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250425173222-7b384671a197/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/api v0.0.0-20250721164621-a45f3dfb1074/go.mod h1:vYFwMYFbmA8vl6Z/krj/h7+U/AqpHknwJX4Uqgfyc7I=
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM= google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@ -571,38 +586,41 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU= k8s.io/api v0.34.0 h1:L+JtP2wDbEYPUeNGbeSa/5GwFtIA662EmT2YSLOkAVE=
k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM= k8s.io/api v0.34.0/go.mod h1:YzgkIzOOlhl9uwWCZNqpw6RJy9L2FK4dlJeayUoydug=
k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs= k8s.io/apiextensions-apiserver v0.34.0 h1:B3hiB32jV7BcyKcMU5fDaDxk882YrJ1KU+ZSkA9Qxoc=
k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc= k8s.io/apiextensions-apiserver v0.34.0/go.mod h1:hLI4GxE1BDBy9adJKxUxCEHBGZtGfIg98Q+JmTD7+g0=
k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ= k8s.io/apimachinery v0.34.0 h1:eR1WO5fo0HyoQZt1wdISpFDffnWOvFLOOeJ7MgIv4z0=
k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= k8s.io/apimachinery v0.34.0/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
k8s.io/cli-runtime v0.33.0 h1:Lbl/pq/1o8BaIuyn+aVLdEPHVN665tBAXUePs8wjX7c= k8s.io/cli-runtime v0.34.0 h1:N2/rUlJg6TMEBgtQ3SDRJwa8XyKUizwjlOknT1mB2Cw=
k8s.io/cli-runtime v0.33.0/go.mod h1:QcA+r43HeUM9jXFJx7A+yiTPfCooau/iCcP1wQh4NFw= k8s.io/cli-runtime v0.34.0/go.mod h1:t/skRecS73Piv+J+FmWIQA2N2/rDjdYSQzEE67LUUs8=
k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98= k8s.io/client-go v0.34.0 h1:YoWv5r7bsBfb0Hs2jh8SOvFbKzzxyNo0nSb0zC19KZo=
k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg= k8s.io/client-go v0.34.0/go.mod h1:ozgMnEKXkRjeMvBZdV1AijMHLTh3pbACPvK7zFR+QQY=
k8s.io/component-base v0.33.0 h1:Ot4PyJI+0JAD9covDhwLp9UNkUja209OzsJ4FzScBNk= k8s.io/component-base v0.34.0 h1:bS8Ua3zlJzapklsB1dZgjEJuJEeHjj8yTu1gxE2zQX8=
k8s.io/component-base v0.33.0/go.mod h1:aXYZLbw3kihdkOPMDhWbjGCO6sg+luw554KP51t8qCU= k8s.io/component-base v0.34.0/go.mod h1:RSCqUdvIjjrEm81epPcjQ/DS+49fADvGSCkIP3IC6vg=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
k8s.io/kubectl v0.33.0 h1:HiRb1yqibBSCqic4pRZP+viiOBAnIdwYDpzUFejs07g= k8s.io/kubectl v0.34.0 h1:NcXz4TPTaUwhiX4LU+6r6udrlm0NsVnSkP3R9t0dmxs=
k8s.io/kubectl v0.33.0/go.mod h1:gAlGBuS1Jq1fYZ9AjGWbI/5Vk3M/VW2DK4g10Fpyn/0= k8s.io/kubectl v0.34.0/go.mod h1:bmd0W5i+HuG7/p5sqicr0Li0rR2iIhXL0oUyLF3OjR4=
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e h1:KqK5c/ghOm8xkHYhlodbp6i6+r+ChV2vuAuVRdFbLro= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= sigs.k8s.io/controller-runtime v0.22.0 h1:mTOfibb8Hxwpx3xEkR56i7xSjB+nH4hZG37SrlCY5e0=
sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= sigs.k8s.io/controller-runtime v0.22.0/go.mod h1:FwiwRjkRPbiN+zp2QRp7wlTCzbUXxZ/D4OzuQUDwBHY=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
sigs.k8s.io/kustomize/api v0.19.0 h1:F+2HB2mU1MSiR9Hp1NEgoU2q9ItNOaBJl0I4Dlus5SQ= sigs.k8s.io/kustomize/api v0.20.1 h1:iWP1Ydh3/lmldBnH/S5RXgT98vWYMaTUL1ADcr+Sv7I=
sigs.k8s.io/kustomize/api v0.19.0/go.mod h1:/BbwnivGVcBh1r+8m3tH1VNxJmHSk1PzP5fkP6lbL1o= sigs.k8s.io/kustomize/api v0.20.1/go.mod h1:t6hUFxO+Ph0VxIk1sKp1WS0dOjbPCtLJ4p8aADLwqjM=
sigs.k8s.io/kustomize/kyaml v0.19.0 h1:RFge5qsO1uHhwJsu3ipV7RNolC7Uozc0jUBC/61XSlA= sigs.k8s.io/kustomize/kyaml v0.20.1 h1:PCMnA2mrVbRP3NIB6v9kYCAc38uvFLVs8j/CD567A78=
sigs.k8s.io/kustomize/kyaml v0.19.0/go.mod h1:FeKD5jEOH+FbZPpqUghBP8mrLjJ3+zD3/rf9NNu1cwY= sigs.k8s.io/kustomize/kyaml v0.20.1/go.mod h1:0EmkQHRUsJxY8Ug9Niig1pUMSCGHxQ5RklbpV/Ri6po=
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI=
sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=

View File

@ -1,5 +1,5 @@
/* /*
Copyright 2023 The Flux authors Copyright 2025 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -14,15 +14,17 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package intcache package cache
const ( const (
OperationFetchKubeConfig = "fetch_kubeconfig"
OperationDecryptWithAWS = "decrypt_with_aws" OperationDecryptWithAWS = "decrypt_with_aws"
OperationDecryptWithAzure = "decrypt_with_azure" OperationDecryptWithAzure = "decrypt_with_azure"
OperationDecryptWithGCP = "decrypt_with_gcp" OperationDecryptWithGCP = "decrypt_with_gcp"
) )
var AllOperations = []string{ var AllOperations = []string{
OperationFetchKubeConfig,
OperationDecryptWithAWS, OperationDecryptWithAWS,
OperationDecryptWithAzure, OperationDecryptWithAzure,
OperationDecryptWithGCP, OperationDecryptWithGCP,

View File

@ -16,4 +16,7 @@ limitations under the License.
package controller package controller
const OCIArtifactOriginRevisionAnnotation = "org.opencontainers.image.revision" const (
OCIArtifactOriginRevisionAnnotation = "org.opencontainers.image.revision"
TerminalErrorMessage = "Reconciliation failed terminally due to configuration error"
)

View File

@ -90,7 +90,7 @@ stringData:
Interval: metav1.Duration{Duration: reconciliationInterval}, Interval: metav1.Duration{Duration: reconciliationInterval},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },
@ -120,6 +120,7 @@ stringData:
t.Run("fails to reconcile from cross-namespace source", func(t *testing.T) { t.Run("fails to reconcile from cross-namespace source", func(t *testing.T) {
reconciler.NoCrossNamespaceRefs = true reconciler.NoCrossNamespaceRefs = true
defer func() { reconciler.NoCrossNamespaceRefs = false }()
revision = "v2.0.0" revision = "v2.0.0"
err = applyGitRepository(repositoryName, artifact, revision) err = applyGitRepository(repositoryName, artifact, revision)
@ -132,5 +133,6 @@ stringData:
}, timeout, time.Second).Should(BeTrue()) }, timeout, time.Second).Should(BeTrue())
g.Expect(readyCondition.Reason).To(Equal(apiacl.AccessDeniedReason)) g.Expect(readyCondition.Reason).To(Equal(apiacl.AccessDeniedReason))
g.Expect(apimeta.IsStatusConditionTrue(resultK.Status.Conditions, meta.StalledCondition)).Should(BeTrue())
}) })
} }

View File

@ -178,7 +178,8 @@ data: {}
t.Run("object level workload identity feature gate enabled", func(t *testing.T) { t.Run("object level workload identity feature gate enabled", func(t *testing.T) {
g := NewWithT(t) g := NewWithT(t)
t.Setenv(auth.EnvVarEnableObjectLevelWorkloadIdentity, "true") auth.EnableObjectLevelWorkloadIdentity()
t.Cleanup(auth.DisableObjectLevelWorkloadIdentity)
kustomizationKey := types.NamespacedName{ kustomizationKey := types.NamespacedName{
Name: fmt.Sprintf("invalid-config-%s", randStringRunes(5)), Name: fmt.Sprintf("invalid-config-%s", randStringRunes(5)),

View File

@ -22,11 +22,12 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"sort"
"strings" "strings"
"time" "time"
securejoin "github.com/cyphar/filepath-securejoin" securejoin "github.com/cyphar/filepath-securejoin"
celtypes "github.com/google/cel-go/common/types"
"github.com/opencontainers/go-digest"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
apimeta "k8s.io/apimachinery/pkg/api/meta" apimeta "k8s.io/apimachinery/pkg/api/meta"
@ -36,14 +37,9 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
kerrors "k8s.io/apimachinery/pkg/util/errors" kerrors "k8s.io/apimachinery/pkg/util/errors"
kuberecorder "k8s.io/client-go/tools/record" kuberecorder "k8s.io/client-go/tools/record"
"k8s.io/client-go/util/workqueue"
ctrl "sigs.k8s.io/controller-runtime" ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/fluxcd/cli-utils/pkg/kstatus/polling" "github.com/fluxcd/cli-utils/pkg/kstatus/polling"
@ -53,6 +49,7 @@ import (
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/auth" "github.com/fluxcd/pkg/auth"
authutils "github.com/fluxcd/pkg/auth/utils"
"github.com/fluxcd/pkg/cache" "github.com/fluxcd/pkg/cache"
"github.com/fluxcd/pkg/http/fetch" "github.com/fluxcd/pkg/http/fetch"
generator "github.com/fluxcd/pkg/kustomize" generator "github.com/fluxcd/pkg/kustomize"
@ -63,7 +60,6 @@ import (
runtimeCtrl "github.com/fluxcd/pkg/runtime/controller" runtimeCtrl "github.com/fluxcd/pkg/runtime/controller"
"github.com/fluxcd/pkg/runtime/jitter" "github.com/fluxcd/pkg/runtime/jitter"
"github.com/fluxcd/pkg/runtime/patch" "github.com/fluxcd/pkg/runtime/patch"
"github.com/fluxcd/pkg/runtime/predicates"
"github.com/fluxcd/pkg/runtime/statusreaders" "github.com/fluxcd/pkg/runtime/statusreaders"
"github.com/fluxcd/pkg/ssa" "github.com/fluxcd/pkg/ssa"
"github.com/fluxcd/pkg/ssa/normalize" "github.com/fluxcd/pkg/ssa/normalize"
@ -74,7 +70,9 @@ import (
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
intcache "github.com/fluxcd/kustomize-controller/internal/cache" intcache "github.com/fluxcd/kustomize-controller/internal/cache"
"github.com/fluxcd/kustomize-controller/internal/decryptor" "github.com/fluxcd/kustomize-controller/internal/decryptor"
"github.com/fluxcd/kustomize-controller/internal/features"
"github.com/fluxcd/kustomize-controller/internal/inventory" "github.com/fluxcd/kustomize-controller/internal/inventory"
intruntime "github.com/fluxcd/kustomize-controller/internal/runtime"
) )
// +kubebuilder:rbac:groups=kustomize.toolkit.fluxcd.io,resources=kustomizations,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=kustomize.toolkit.fluxcd.io,resources=kustomizations,verbs=get;list;watch;create;update;patch;delete
@ -92,85 +90,37 @@ type KustomizationReconciler struct {
kuberecorder.EventRecorder kuberecorder.EventRecorder
runtimeCtrl.Metrics runtimeCtrl.Metrics
artifactFetchRetries int // Kubernetes options
requeueDependency time.Duration
Mapper apimeta.RESTMapper
APIReader client.Reader APIReader client.Reader
ClusterReader engine.ClusterReaderFactory ClusterReader engine.ClusterReaderFactory
ConcurrentSSA int
ControllerName string ControllerName string
statusManager string KubeConfigOpts runtimeClient.KubeConfigOptions
Mapper apimeta.RESTMapper
StatusManager string
// Multi-tenancy and security options
DefaultServiceAccount string
DisallowedFieldManagers []string
NoCrossNamespaceRefs bool NoCrossNamespaceRefs bool
NoRemoteBases bool NoRemoteBases bool
FailFast bool SOPSAgeSecret string
DefaultServiceAccount string
KubeConfigOpts runtimeClient.KubeConfigOptions
ConcurrentSSA int
DisallowedFieldManagers []string
StrictSubstitutions bool
GroupChangeLog bool
TokenCache *cache.TokenCache TokenCache *cache.TokenCache
}
// KustomizationReconcilerOptions contains options for the KustomizationReconciler. // Retry and requeue options
type KustomizationReconcilerOptions struct {
HTTPRetry int ArtifactFetchRetries int
DependencyRequeueInterval time.Duration DependencyRequeueInterval time.Duration
RateLimiter workqueue.TypedRateLimiter[reconcile.Request]
}
func (r *KustomizationReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opts KustomizationReconcilerOptions) error { // Feature gates
const (
ociRepositoryIndexKey string = ".metadata.ociRepository"
gitRepositoryIndexKey string = ".metadata.gitRepository"
bucketIndexKey string = ".metadata.bucket"
)
// Index the Kustomizations by the OCIRepository references they (may) point at. AdditiveCELDependencyCheck bool
if err := mgr.GetCache().IndexField(ctx, &kustomizev1.Kustomization{}, ociRepositoryIndexKey, AllowExternalArtifact bool
r.indexBy(sourcev1.OCIRepositoryKind)); err != nil { FailFast bool
return fmt.Errorf("failed setting index fields: %w", err) GroupChangeLog bool
} StrictSubstitutions bool
// Index the Kustomizations by the GitRepository references they (may) point at.
if err := mgr.GetCache().IndexField(ctx, &kustomizev1.Kustomization{}, gitRepositoryIndexKey,
r.indexBy(sourcev1.GitRepositoryKind)); err != nil {
return fmt.Errorf("failed setting index fields: %w", err)
}
// Index the Kustomizations by the Bucket references they (may) point at.
if err := mgr.GetCache().IndexField(ctx, &kustomizev1.Kustomization{}, bucketIndexKey,
r.indexBy(sourcev1.BucketKind)); err != nil {
return fmt.Errorf("failed setting index fields: %w", err)
}
r.requeueDependency = opts.DependencyRequeueInterval
r.statusManager = fmt.Sprintf("gotk-%s", r.ControllerName)
r.artifactFetchRetries = opts.HTTPRetry
return ctrl.NewControllerManagedBy(mgr).
For(&kustomizev1.Kustomization{}, builder.WithPredicates(
predicate.Or(predicate.GenerationChangedPredicate{}, predicates.ReconcileRequestedPredicate{}),
)).
Watches(
&sourcev1.OCIRepository{},
handler.EnqueueRequestsFromMapFunc(r.requestsForRevisionChangeOf(ociRepositoryIndexKey)),
builder.WithPredicates(SourceRevisionChangePredicate{}),
).
Watches(
&sourcev1.GitRepository{},
handler.EnqueueRequestsFromMapFunc(r.requestsForRevisionChangeOf(gitRepositoryIndexKey)),
builder.WithPredicates(SourceRevisionChangePredicate{}),
).
Watches(
&sourcev1.Bucket{},
handler.EnqueueRequestsFromMapFunc(r.requestsForRevisionChangeOf(bucketIndexKey)),
builder.WithPredicates(SourceRevisionChangePredicate{}),
).
WithOptions(controller.Options{
RateLimiter: opts.RateLimiter,
}).
Complete(r)
} }
func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, retErr error) { func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, retErr error) {
@ -236,17 +186,15 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques
// Configure custom health checks. // Configure custom health checks.
statusReaders, err := cel.PollerWithCustomHealthChecks(ctx, obj.Spec.HealthCheckExprs) statusReaders, err := cel.PollerWithCustomHealthChecks(ctx, obj.Spec.HealthCheckExprs)
if err != nil { if err != nil {
const msg = "Reconciliation failed terminally due to configuration error" errMsg := fmt.Sprintf("%s: %v", TerminalErrorMessage, err)
errMsg := fmt.Sprintf("%s: %v", msg, err)
conditions.MarkFalse(obj, meta.ReadyCondition, meta.InvalidCELExpressionReason, "%s", errMsg) conditions.MarkFalse(obj, meta.ReadyCondition, meta.InvalidCELExpressionReason, "%s", errMsg)
conditions.MarkStalled(obj, meta.InvalidCELExpressionReason, "%s", errMsg) conditions.MarkStalled(obj, meta.InvalidCELExpressionReason, "%s", errMsg)
obj.Status.ObservedGeneration = obj.Generation obj.Status.ObservedGeneration = obj.Generation
log.Error(err, msg)
r.event(obj, "", "", eventv1.EventSeverityError, errMsg, nil) r.event(obj, "", "", eventv1.EventSeverityError, errMsg, nil)
return ctrl.Result{}, nil return ctrl.Result{}, reconcile.TerminalError(err)
} }
// Check object-level workload identity feature gate. // Check object-level workload identity feature gate and decryption with service account.
if d := obj.Spec.Decryption; d != nil && d.ServiceAccountName != "" && !auth.IsObjectLevelWorkloadIdentityEnabled() { if d := obj.Spec.Decryption; d != nil && d.ServiceAccountName != "" && !auth.IsObjectLevelWorkloadIdentityEnabled() {
const gate = auth.FeatureGateObjectLevelWorkloadIdentity const gate = auth.FeatureGateObjectLevelWorkloadIdentity
const msgFmt = "to use spec.decryption.serviceAccountName for decryption authentication please enable the %s feature gate in the controller" const msgFmt = "to use spec.decryption.serviceAccountName for decryption authentication please enable the %s feature gate in the controller"
@ -271,9 +219,9 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques
if acl.IsAccessDenied(err) { if acl.IsAccessDenied(err) {
conditions.MarkFalse(obj, meta.ReadyCondition, apiacl.AccessDeniedReason, "%s", err) conditions.MarkFalse(obj, meta.ReadyCondition, apiacl.AccessDeniedReason, "%s", err)
log.Error(err, "Access denied to cross-namespace source") conditions.MarkStalled(obj, apiacl.AccessDeniedReason, "%s", err)
r.event(obj, "", "", eventv1.EventSeverityError, err.Error(), nil) r.event(obj, "", "", eventv1.EventSeverityError, err.Error(), nil)
return ctrl.Result{RequeueAfter: obj.GetRetryInterval()}, nil return ctrl.Result{}, reconcile.TerminalError(err)
} }
// Retry with backoff on transient errors. // Retry with backoff on transient errors.
@ -282,10 +230,10 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques
// Requeue the reconciliation if the source artifact is not found. // Requeue the reconciliation if the source artifact is not found.
if artifactSource.GetArtifact() == nil { if artifactSource.GetArtifact() == nil {
msg := fmt.Sprintf("Source artifact not found, retrying in %s", r.requeueDependency.String()) msg := fmt.Sprintf("Source artifact not found, retrying in %s", r.DependencyRequeueInterval.String())
conditions.MarkFalse(obj, meta.ReadyCondition, meta.ArtifactFailedReason, "%s", msg) conditions.MarkFalse(obj, meta.ReadyCondition, meta.ArtifactFailedReason, "%s", msg)
log.Info(msg) log.Info(msg)
return ctrl.Result{RequeueAfter: r.requeueDependency}, nil return ctrl.Result{RequeueAfter: r.DependencyRequeueInterval}, nil
} }
revision := artifactSource.GetArtifact().Revision revision := artifactSource.GetArtifact().Revision
originRevision := getOriginRevision(artifactSource) originRevision := getOriginRevision(artifactSource)
@ -293,11 +241,22 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques
// Check dependencies and requeue the reconciliation if the check fails. // Check dependencies and requeue the reconciliation if the check fails.
if len(obj.Spec.DependsOn) > 0 { if len(obj.Spec.DependsOn) > 0 {
if err := r.checkDependencies(ctx, obj, artifactSource); err != nil { if err := r.checkDependencies(ctx, obj, artifactSource); err != nil {
// Check if this is a terminal error that should not trigger retries
if errors.Is(err, reconcile.TerminalError(nil)) {
errMsg := fmt.Sprintf("%s: %v", TerminalErrorMessage, err)
conditions.MarkFalse(obj, meta.ReadyCondition, meta.InvalidCELExpressionReason, "%s", errMsg)
conditions.MarkStalled(obj, meta.InvalidCELExpressionReason, "%s", errMsg)
obj.Status.ObservedGeneration = obj.Generation
r.event(obj, revision, originRevision, eventv1.EventSeverityError, errMsg, nil)
return ctrl.Result{}, err
}
// Retry on transient errors.
conditions.MarkFalse(obj, meta.ReadyCondition, meta.DependencyNotReadyReason, "%s", err) conditions.MarkFalse(obj, meta.ReadyCondition, meta.DependencyNotReadyReason, "%s", err)
msg := fmt.Sprintf("Dependencies do not meet ready condition, retrying in %s", r.requeueDependency.String()) msg := fmt.Sprintf("Dependencies do not meet ready condition, retrying in %s", r.DependencyRequeueInterval.String())
log.Info(msg) log.Info(msg)
r.event(obj, revision, originRevision, eventv1.EventSeverityInfo, msg, nil) r.event(obj, revision, originRevision, eventv1.EventSeverityInfo, msg, nil)
return ctrl.Result{RequeueAfter: r.requeueDependency}, nil return ctrl.Result{RequeueAfter: r.DependencyRequeueInterval}, nil
} }
log.Info("All dependencies are ready, proceeding with reconciliation") log.Info("All dependencies are ready, proceeding with reconciliation")
} }
@ -307,10 +266,10 @@ func (r *KustomizationReconciler) Reconcile(ctx context.Context, req ctrl.Reques
// Requeue at the specified retry interval if the artifact tarball is not found. // Requeue at the specified retry interval if the artifact tarball is not found.
if errors.Is(reconcileErr, fetch.ErrFileNotFound) { if errors.Is(reconcileErr, fetch.ErrFileNotFound) {
msg := fmt.Sprintf("Source is not ready, artifact not found, retrying in %s", r.requeueDependency.String()) msg := fmt.Sprintf("Source is not ready, artifact not found, retrying in %s", r.DependencyRequeueInterval.String())
conditions.MarkFalse(obj, meta.ReadyCondition, meta.ArtifactFailedReason, "%s", msg) conditions.MarkFalse(obj, meta.ReadyCondition, meta.ArtifactFailedReason, "%s", msg)
log.Info(msg) log.Info(msg)
return ctrl.Result{RequeueAfter: r.requeueDependency}, nil return ctrl.Result{RequeueAfter: r.DependencyRequeueInterval}, nil
} }
// Broadcast the reconciliation failure and requeue at the specified retry interval. // Broadcast the reconciliation failure and requeue at the specified retry interval.
@ -335,6 +294,7 @@ func (r *KustomizationReconciler) reconcile(
src sourcev1.Source, src sourcev1.Source,
patcher *patch.SerialPatcher, patcher *patch.SerialPatcher,
statusReaders []func(apimeta.RESTMapper) engine.StatusReader) error { statusReaders []func(apimeta.RESTMapper) engine.StatusReader) error {
reconcileStart := time.Now()
log := ctrl.LoggerFrom(ctx) log := ctrl.LoggerFrom(ctx)
// Update status with the reconciliation progress. // Update status with the reconciliation progress.
@ -368,13 +328,14 @@ func (r *KustomizationReconciler) reconcile(
}(tmpDir) }(tmpDir)
// Download artifact and extract files to the tmp dir. // Download artifact and extract files to the tmp dir.
if err = fetch.NewArchiveFetcherWithLogger( fetcher := fetch.New(
r.artifactFetchRetries, fetch.WithLogger(ctrl.LoggerFrom(ctx)),
tar.UnlimitedUntarSize, fetch.WithRetries(r.ArtifactFetchRetries),
tar.UnlimitedUntarSize, fetch.WithMaxDownloadSize(tar.UnlimitedUntarSize),
os.Getenv("SOURCE_CONTROLLER_LOCALHOST"), fetch.WithUntar(tar.WithMaxUntarSize(tar.UnlimitedUntarSize)),
ctrl.LoggerFrom(ctx), fetch.WithHostnameOverwrite(os.Getenv("SOURCE_CONTROLLER_LOCALHOST")),
).Fetch(src.GetArtifact().URL, src.GetArtifact().Digest, tmpDir); err != nil { )
if err = fetcher.Fetch(src.GetArtifact().URL, src.GetArtifact().Digest, tmpDir); err != nil {
conditions.MarkFalse(obj, meta.ReadyCondition, meta.ArtifactFailedReason, "%s", err) conditions.MarkFalse(obj, meta.ReadyCondition, meta.ArtifactFailedReason, "%s", err)
return err return err
} }
@ -410,8 +371,9 @@ func (r *KustomizationReconciler) reconcile(
} }
if obj.Spec.KubeConfig != nil { if obj.Spec.KubeConfig != nil {
mustImpersonate = true mustImpersonate = true
provider := r.getProviderRESTConfigFetcher(obj)
impersonatorOpts = append(impersonatorOpts, impersonatorOpts = append(impersonatorOpts,
runtimeClient.WithKubeConfig(obj.Spec.KubeConfig, r.KubeConfigOpts, obj.GetNamespace())) runtimeClient.WithKubeConfig(obj.Spec.KubeConfig, r.KubeConfigOpts, obj.GetNamespace(), provider))
} }
if r.ClusterReader != nil || len(statusReaders) > 0 { if r.ClusterReader != nil || len(statusReaders) > 0 {
impersonatorOpts = append(impersonatorOpts, impersonatorOpts = append(impersonatorOpts,
@ -451,6 +413,13 @@ func (r *KustomizationReconciler) reconcile(
return err return err
} }
// Calculate the digest of the built resources for history tracking.
checksum := digest.FromBytes(resources).String()
historyMeta := map[string]string{"revision": revision}
if originRevision != "" {
historyMeta["originRevision"] = originRevision
}
// Convert the build result into Kubernetes unstructured objects. // Convert the build result into Kubernetes unstructured objects.
objects, err := ssautil.ReadObjects(bytes.NewReader(resources)) objects, err := ssautil.ReadObjects(bytes.NewReader(resources))
if err != nil { if err != nil {
@ -476,6 +445,7 @@ func (r *KustomizationReconciler) reconcile(
// Validate and apply resources in stages. // Validate and apply resources in stages.
drifted, changeSet, err := r.apply(ctx, resourceManager, obj, revision, originRevision, objects) drifted, changeSet, err := r.apply(ctx, resourceManager, obj, revision, originRevision, objects)
if err != nil { if err != nil {
obj.Status.History.Upsert(checksum, time.Now(), time.Since(reconcileStart), meta.ReconciliationFailedReason, historyMeta)
conditions.MarkFalse(obj, meta.ReadyCondition, meta.ReconciliationFailedReason, "%s", err) conditions.MarkFalse(obj, meta.ReadyCondition, meta.ReconciliationFailedReason, "%s", err)
return err return err
} }
@ -484,6 +454,7 @@ func (r *KustomizationReconciler) reconcile(
newInventory := inventory.New() newInventory := inventory.New()
err = inventory.AddChangeSet(newInventory, changeSet) err = inventory.AddChangeSet(newInventory, changeSet)
if err != nil { if err != nil {
obj.Status.History.Upsert(checksum, time.Now(), time.Since(reconcileStart), meta.ReconciliationFailedReason, historyMeta)
conditions.MarkFalse(obj, meta.ReadyCondition, meta.ReconciliationFailedReason, "%s", err) conditions.MarkFalse(obj, meta.ReadyCondition, meta.ReconciliationFailedReason, "%s", err)
return err return err
} }
@ -494,12 +465,14 @@ func (r *KustomizationReconciler) reconcile(
// Detect stale resources which are subject to garbage collection. // Detect stale resources which are subject to garbage collection.
staleObjects, err := inventory.Diff(oldInventory, newInventory) staleObjects, err := inventory.Diff(oldInventory, newInventory)
if err != nil { if err != nil {
obj.Status.History.Upsert(checksum, time.Now(), time.Since(reconcileStart), meta.ReconciliationFailedReason, historyMeta)
conditions.MarkFalse(obj, meta.ReadyCondition, meta.ReconciliationFailedReason, "%s", err) conditions.MarkFalse(obj, meta.ReadyCondition, meta.ReconciliationFailedReason, "%s", err)
return err return err
} }
// Run garbage collection for stale resources that do not have pruning disabled. // Run garbage collection for stale resources that do not have pruning disabled.
if _, err := r.prune(ctx, resourceManager, obj, revision, originRevision, staleObjects); err != nil { if _, err := r.prune(ctx, resourceManager, obj, revision, originRevision, staleObjects); err != nil {
obj.Status.History.Upsert(checksum, time.Now(), time.Since(reconcileStart), meta.PruneFailedReason, historyMeta)
conditions.MarkFalse(obj, meta.ReadyCondition, meta.PruneFailedReason, "%s", err) conditions.MarkFalse(obj, meta.ReadyCondition, meta.PruneFailedReason, "%s", err)
return err return err
} }
@ -515,6 +488,7 @@ func (r *KustomizationReconciler) reconcile(
isNewRevision, isNewRevision,
drifted, drifted,
changeSet.ToObjMetadataSet()); err != nil { changeSet.ToObjMetadataSet()); err != nil {
obj.Status.History.Upsert(checksum, time.Now(), time.Since(reconcileStart), meta.HealthCheckFailedReason, historyMeta)
conditions.MarkFalse(obj, meta.ReadyCondition, meta.HealthCheckFailedReason, "%s", err) conditions.MarkFalse(obj, meta.ReadyCondition, meta.HealthCheckFailedReason, "%s", err)
return err return err
} }
@ -528,55 +502,131 @@ func (r *KustomizationReconciler) reconcile(
meta.ReadyCondition, meta.ReadyCondition,
meta.ReconciliationSucceededReason, meta.ReconciliationSucceededReason,
"Applied revision: %s", revision) "Applied revision: %s", revision)
obj.Status.History.Upsert(checksum,
time.Now(),
time.Since(reconcileStart),
meta.ReconciliationSucceededReason,
historyMeta)
return nil return nil
} }
// checkDependencies checks if the dependencies of the current Kustomization are ready.
// To be considered ready, a dependencies must meet the following criteria:
// - The dependency exists in the API server.
// - The CEL expression (if provided) must evaluate to true.
// - The dependency observed generation must match the current generation.
// - The dependency Ready condition must be true.
// - The dependency last applied revision must match the current source artifact revision.
func (r *KustomizationReconciler) checkDependencies(ctx context.Context, func (r *KustomizationReconciler) checkDependencies(ctx context.Context,
obj *kustomizev1.Kustomization, obj *kustomizev1.Kustomization,
source sourcev1.Source) error { source sourcev1.Source) error {
for _, d := range obj.Spec.DependsOn {
if d.Namespace == "" { // Convert the Kustomization object to Unstructured for CEL evaluation.
d.Namespace = obj.GetNamespace() objMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
}
dName := types.NamespacedName{
Namespace: d.Namespace,
Name: d.Name,
}
var k kustomizev1.Kustomization
err := r.APIReader.Get(ctx, dName, &k)
if err != nil { if err != nil {
return fmt.Errorf("dependency '%s' not found: %w", dName, err) return fmt.Errorf("failed to convert Kustomization to unstructured: %w", err)
} }
if len(k.Status.Conditions) == 0 || k.Generation != k.Status.ObservedGeneration { for _, depRef := range obj.Spec.DependsOn {
return fmt.Errorf("dependency '%s' is not ready", dName) // Check if the dependency exists by querying
// the API server bypassing the cache.
if depRef.Namespace == "" {
depRef.Namespace = obj.GetNamespace()
}
depName := types.NamespacedName{
Namespace: depRef.Namespace,
Name: depRef.Name,
}
var dep kustomizev1.Kustomization
err := r.APIReader.Get(ctx, depName, &dep)
if err != nil {
return fmt.Errorf("dependency '%s' not found: %w", depName, err)
} }
if !apimeta.IsStatusConditionTrue(k.Status.Conditions, meta.ReadyCondition) { // Evaluate the CEL expression (if specified) to determine if the dependency is ready.
return fmt.Errorf("dependency '%s' is not ready", dName) if depRef.ReadyExpr != "" {
ready, err := r.evalReadyExpr(ctx, depRef.ReadyExpr, objMap, &dep)
if err != nil {
return err
}
if !ready {
return fmt.Errorf("dependency '%s' is not ready according to readyExpr eval", depName)
}
} }
srcNamespace := k.Spec.SourceRef.Namespace // Skip the built-in readiness check if the CEL expression is provided
// and the AdditiveCELDependencyCheck feature gate is not enabled.
if depRef.ReadyExpr != "" && !r.AdditiveCELDependencyCheck {
continue
}
// Check if the dependency observed generation is up to date
// and if the dependency is in a ready state.
if len(dep.Status.Conditions) == 0 || dep.Generation != dep.Status.ObservedGeneration {
return fmt.Errorf("dependency '%s' is not ready", depName)
}
if !apimeta.IsStatusConditionTrue(dep.Status.Conditions, meta.ReadyCondition) {
return fmt.Errorf("dependency '%s' is not ready", depName)
}
// Check if the dependency source matches the current source
// and if so, verify that the last applied revision of the dependency
// matches the current source artifact revision.
srcNamespace := dep.Spec.SourceRef.Namespace
if srcNamespace == "" { if srcNamespace == "" {
srcNamespace = k.GetNamespace() srcNamespace = dep.GetNamespace()
} }
dSrcNamespace := obj.Spec.SourceRef.Namespace depSrcNamespace := obj.Spec.SourceRef.Namespace
if dSrcNamespace == "" { if depSrcNamespace == "" {
dSrcNamespace = obj.GetNamespace() depSrcNamespace = obj.GetNamespace()
} }
if dep.Spec.SourceRef.Name == obj.Spec.SourceRef.Name &&
if k.Spec.SourceRef.Name == obj.Spec.SourceRef.Name && srcNamespace == depSrcNamespace &&
srcNamespace == dSrcNamespace && dep.Spec.SourceRef.Kind == obj.Spec.SourceRef.Kind &&
k.Spec.SourceRef.Kind == obj.Spec.SourceRef.Kind && !source.GetArtifact().HasRevision(dep.Status.LastAppliedRevision) {
!source.GetArtifact().HasRevision(k.Status.LastAppliedRevision) { return fmt.Errorf("dependency '%s' revision is not up to date", depName)
return fmt.Errorf("dependency '%s' revision is not up to date", dName)
} }
} }
return nil return nil
} }
// evalReadyExpr evaluates the CEL expression for the dependency readiness check.
func (r *KustomizationReconciler) evalReadyExpr(
ctx context.Context,
expr string,
selfMap map[string]any,
dep *kustomizev1.Kustomization,
) (bool, error) {
const (
selfName = "self"
depName = "dep"
)
celExpr, err := cel.NewExpression(expr,
cel.WithCompile(),
cel.WithOutputType(celtypes.BoolType),
cel.WithStructVariables(selfName, depName))
if err != nil {
return false, reconcile.TerminalError(fmt.Errorf("failed to evaluate dependency %s: %w", dep.Name, err))
}
depMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(dep)
if err != nil {
return false, fmt.Errorf("failed to convert %s object to map: %w", depName, err)
}
vars := map[string]any{
selfName: selfMap,
depName: depMap,
}
return celExpr.EvaluateBoolean(ctx, vars)
}
// getSource resolves the source reference and returns the source object containing the artifact.
// It returns an error if the source is not found or if access is denied.
func (r *KustomizationReconciler) getSource(ctx context.Context, func (r *KustomizationReconciler) getSource(ctx context.Context,
obj *kustomizev1.Kustomization) (sourcev1.Source, error) { obj *kustomizev1.Kustomization) (sourcev1.Source, error) {
var src sourcev1.Source var src sourcev1.Source
@ -589,12 +639,20 @@ func (r *KustomizationReconciler) getSource(ctx context.Context,
Name: obj.Spec.SourceRef.Name, Name: obj.Spec.SourceRef.Name,
} }
// Check if cross-namespace references are allowed.
if r.NoCrossNamespaceRefs && sourceNamespace != obj.GetNamespace() { if r.NoCrossNamespaceRefs && sourceNamespace != obj.GetNamespace() {
return src, acl.AccessDeniedError( return src, acl.AccessDeniedError(
fmt.Sprintf("can't access '%s/%s', cross-namespace references have been blocked", fmt.Sprintf("can't access '%s/%s', cross-namespace references have been blocked",
obj.Spec.SourceRef.Kind, namespacedName)) obj.Spec.SourceRef.Kind, namespacedName))
} }
// Check if ExternalArtifact kind is allowed.
if obj.Spec.SourceRef.Kind == sourcev1.ExternalArtifactKind && !r.AllowExternalArtifact {
return src, acl.AccessDeniedError(
fmt.Sprintf("can't access '%s/%s', %s feature gate is disabled",
obj.Spec.SourceRef.Kind, namespacedName, features.ExternalArtifact))
}
switch obj.Spec.SourceRef.Kind { switch obj.Spec.SourceRef.Kind {
case sourcev1.OCIRepositoryKind: case sourcev1.OCIRepositoryKind:
var repository sourcev1.OCIRepository var repository sourcev1.OCIRepository
@ -626,6 +684,16 @@ func (r *KustomizationReconciler) getSource(ctx context.Context,
return src, fmt.Errorf("unable to get source '%s': %w", namespacedName, err) return src, fmt.Errorf("unable to get source '%s': %w", namespacedName, err)
} }
src = &bucket src = &bucket
case sourcev1.ExternalArtifactKind:
var ea sourcev1.ExternalArtifact
err := r.Client.Get(ctx, namespacedName, &ea)
if err != nil {
if apierrors.IsNotFound(err) {
return src, err
}
return src, fmt.Errorf("unable to get source '%s': %w", namespacedName, err)
}
src = &ea
default: default:
return src, fmt.Errorf("source `%s` kind '%s' not supported", return src, fmt.Errorf("source `%s` kind '%s' not supported",
obj.Spec.SourceRef.Name, obj.Spec.SourceRef.Kind) obj.Spec.SourceRef.Name, obj.Spec.SourceRef.Kind)
@ -642,7 +710,18 @@ func (r *KustomizationReconciler) generate(obj unstructured.Unstructured,
func (r *KustomizationReconciler) build(ctx context.Context, func (r *KustomizationReconciler) build(ctx context.Context,
obj *kustomizev1.Kustomization, u unstructured.Unstructured, obj *kustomizev1.Kustomization, u unstructured.Unstructured,
workDir, dirPath string) ([]byte, error) { workDir, dirPath string) ([]byte, error) {
dec, cleanup, err := decryptor.NewTempDecryptor(workDir, r.Client, obj, r.TokenCache)
// Build decryptor.
decryptorOpts := []decryptor.Option{
decryptor.WithRoot(workDir),
}
if r.TokenCache != nil {
decryptorOpts = append(decryptorOpts, decryptor.WithTokenCache(*r.TokenCache))
}
if name, ns := r.SOPSAgeSecret, intruntime.Namespace(); name != "" && ns != "" {
decryptorOpts = append(decryptorOpts, decryptor.WithSOPSAgeSecret(name, ns))
}
dec, cleanup, err := decryptor.New(r.Client, obj, decryptorOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -794,120 +873,44 @@ func (r *KustomizationReconciler) apply(ctx context.Context,
}, },
} }
// contains only CRDs and Namespaces
var defStage []*unstructured.Unstructured
// contains only Kubernetes Class types e.g.: RuntimeClass, PriorityClass,
// StorageClass, VolumeSnapshotClass, IngressClass, GatewayClass, ClusterClass, etc
var classStage []*unstructured.Unstructured
// contains all objects except for CRDs, Namespaces and Class type objects
var resStage []*unstructured.Unstructured
// contains the objects' metadata after apply
resultSet := ssa.NewChangeSet()
for _, u := range objects { for _, u := range objects {
if decryptor.IsEncryptedSecret(u) { if decryptor.IsEncryptedSecret(u) {
return false, nil, return false, nil,
fmt.Errorf("%s is SOPS encrypted, configuring decryption is required for this secret to be reconciled", fmt.Errorf("%s is SOPS encrypted, configuring decryption is required for this secret to be reconciled",
ssautil.FmtUnstructured(u)) ssautil.FmtUnstructured(u))
} }
switch {
case ssautil.IsClusterDefinition(u):
defStage = append(defStage, u)
case strings.HasSuffix(u.GetKind(), "Class"):
classStage = append(classStage, u)
default:
resStage = append(resStage, u)
}
} }
// contains the objects' metadata after apply
resultSet := ssa.NewChangeSet()
var changeSetLog strings.Builder var changeSetLog strings.Builder
// validate, apply and wait for CRDs and Namespaces to register if len(objects) > 0 {
if len(defStage) > 0 { changeSet, err := manager.ApplyAllStaged(ctx, objects, applyOpts)
changeSet, err := manager.ApplyAll(ctx, defStage, applyOpts)
if err != nil {
return false, nil, err
}
if changeSet != nil && len(changeSet.Entries) > 0 { if changeSet != nil && len(changeSet.Entries) > 0 {
resultSet.Append(changeSet.Entries) resultSet.Append(changeSet.Entries)
if r.GroupChangeLog { // filter out the objects that have not changed
log.Info("server-side apply for cluster definitions completed", "output", changeSet.ToGroupedMap())
} else {
log.Info("server-side apply for cluster definitions completed", "output", changeSet.ToMap())
}
for _, change := range changeSet.Entries { for _, change := range changeSet.Entries {
if HasChanged(change.Action) { if HasChanged(change.Action) {
changeSetLog.WriteString(change.String() + "\n") changeSetLog.WriteString(change.String() + "\n")
} }
} }
if err := manager.WaitForSet(changeSet.ToObjMetadataSet(), ssa.WaitOptions{
Interval: 2 * time.Second,
Timeout: obj.GetTimeout(),
}); err != nil {
return false, nil, err
}
}
} }
// validate, apply and wait for Class type objects to register // include the change log in the error message in case af a partial apply
if len(classStage) > 0 {
changeSet, err := manager.ApplyAll(ctx, classStage, applyOpts)
if err != nil {
return false, nil, err
}
if changeSet != nil && len(changeSet.Entries) > 0 {
resultSet.Append(changeSet.Entries)
if r.GroupChangeLog {
log.Info("server-side apply for cluster definitions completed", "output", changeSet.ToGroupedMap())
} else {
log.Info("server-side apply for cluster class types completed", "output", changeSet.ToMap())
}
for _, change := range changeSet.Entries {
if HasChanged(change.Action) {
changeSetLog.WriteString(change.String() + "\n")
}
}
if err := manager.WaitForSet(changeSet.ToObjMetadataSet(), ssa.WaitOptions{
Interval: 2 * time.Second,
Timeout: obj.GetTimeout(),
}); err != nil {
return false, nil, err
}
}
}
// sort by kind, validate and apply all the others objects
sort.Sort(ssa.SortableUnstructureds(resStage))
if len(resStage) > 0 {
changeSet, err := manager.ApplyAll(ctx, resStage, applyOpts)
if err != nil { if err != nil {
return false, nil, fmt.Errorf("%w\n%s", err, changeSetLog.String()) return false, nil, fmt.Errorf("%w\n%s", err, changeSetLog.String())
} }
// log all applied objects
if changeSet != nil && len(changeSet.Entries) > 0 { if changeSet != nil && len(changeSet.Entries) > 0 {
resultSet.Append(changeSet.Entries)
if r.GroupChangeLog { if r.GroupChangeLog {
log.Info("server-side apply for cluster definitions completed", "output", changeSet.ToGroupedMap()) log.Info("server-side apply completed", "output", changeSet.ToGroupedMap(), "revision", revision)
} else { } else {
log.Info("server-side apply completed", "output", changeSet.ToMap(), "revision", revision) log.Info("server-side apply completed", "output", changeSet.ToMap(), "revision", revision)
} }
for _, change := range changeSet.Entries {
if HasChanged(change.Action) {
changeSetLog.WriteString(change.String() + "\n")
}
}
} }
} }
@ -1076,8 +1079,9 @@ func (r *KustomizationReconciler) finalize(ctx context.Context,
} }
if obj.Spec.KubeConfig != nil { if obj.Spec.KubeConfig != nil {
mustImpersonate = true mustImpersonate = true
provider := r.getProviderRESTConfigFetcher(obj)
impersonatorOpts = append(impersonatorOpts, impersonatorOpts = append(impersonatorOpts,
runtimeClient.WithKubeConfig(obj.Spec.KubeConfig, r.KubeConfigOpts, obj.GetNamespace())) runtimeClient.WithKubeConfig(obj.Spec.KubeConfig, r.KubeConfigOpts, obj.GetNamespace(), provider))
} }
if r.ClusterReader != nil { if r.ClusterReader != nil {
impersonatorOpts = append(impersonatorOpts, runtimeClient.WithPolling(r.ClusterReader)) impersonatorOpts = append(impersonatorOpts, runtimeClient.WithPolling(r.ClusterReader))
@ -1222,7 +1226,7 @@ func (r *KustomizationReconciler) patch(ctx context.Context,
patchOpts = append(patchOpts, patchOpts = append(patchOpts,
patch.WithOwnedConditions{Conditions: ownedConditions}, patch.WithOwnedConditions{Conditions: ownedConditions},
patch.WithForceOverwriteConditions{}, patch.WithForceOverwriteConditions{},
patch.WithFieldOwner(r.statusManager), patch.WithFieldOwner(r.StatusManager),
) )
// Patch the object status, conditions and finalizers. // Patch the object status, conditions and finalizers.
@ -1262,6 +1266,27 @@ func (r *KustomizationReconciler) getClientAndPoller(
return r.Client, poller return r.Client, poller
} }
// getProviderRESTConfigFetcher returns a ProviderRESTConfigFetcher for the
// Kustomization object, which is used to fetch the kubeconfig for a ConfigMap
// reference in the Kustomization spec.
func (r *KustomizationReconciler) getProviderRESTConfigFetcher(obj *kustomizev1.Kustomization) runtimeClient.ProviderRESTConfigFetcher {
var provider runtimeClient.ProviderRESTConfigFetcher
if kc := obj.Spec.KubeConfig; kc != nil && kc.SecretRef == nil && kc.ConfigMapRef != nil {
var opts []auth.Option
if r.TokenCache != nil {
involvedObject := cache.InvolvedObject{
Kind: kustomizev1.KustomizationKind,
Name: obj.GetName(),
Namespace: obj.GetNamespace(),
Operation: intcache.OperationFetchKubeConfig,
}
opts = append(opts, auth.WithCache(*r.TokenCache, involvedObject))
}
provider = runtimeClient.ProviderRESTConfigFetcher(authutils.GetRESTConfigFetcher(opts...))
}
return provider
}
// getOriginRevision returns the origin revision of the source artifact, // getOriginRevision returns the origin revision of the source artifact,
// or the empty string if it's not present, or if the artifact itself // or the empty string if it's not present, or if the artifact itself
// is not present. // is not present.

View File

@ -76,7 +76,7 @@ func TestKustomizationReconciler_StagedApply(t *testing.T) {
Kind: sourcev1.GitRepositoryKind, Kind: sourcev1.GitRepositoryKind,
}, },
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },

View File

@ -114,7 +114,7 @@ func TestKustomizationReconciler_Decryptor(t *testing.T) {
Interval: metav1.Duration{Duration: 2 * time.Minute}, Interval: metav1.Duration{Duration: 2 * time.Minute},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },
@ -194,4 +194,172 @@ func TestKustomizationReconciler_Decryptor(t *testing.T) {
g.Expect(events[0].Message).Should(ContainSubstring("Reconciliation finished")) g.Expect(events[0].Message).Should(ContainSubstring("Reconciliation finished"))
g.Expect(events[0].Message).ShouldNot(ContainSubstring("configured")) g.Expect(events[0].Message).ShouldNot(ContainSubstring("configured"))
}) })
t.Run("global SOPS age secret as fallback", func(t *testing.T) {
g := NewWithT(t)
namespace := "global-sops-" + randStringRunes(5)
t.Setenv("RUNTIME_NAMESPACE", namespace)
err := createNamespace(namespace)
g.Expect(err).NotTo(HaveOccurred())
// Create the global SOPS age secret with the private key
ageKey, err := os.ReadFile("testdata/sops/keys/age-global.txt")
g.Expect(err).NotTo(HaveOccurred())
globalSOPSSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: sopsAgeSecret,
Namespace: namespace,
},
StringData: map[string]string{
"identity.agekey": string(ageKey),
},
}
g.Expect(k8sClient.Create(context.Background(), globalSOPSSecret)).To(Succeed())
artifactName := "global-sops-" + randStringRunes(5)
artifactChecksum, err := testServer.ArtifactFromDir("testdata/sops/global", artifactName)
g.Expect(err).NotTo(HaveOccurred())
repositoryName := types.NamespacedName{
Name: fmt.Sprintf("global-sops-%s", randStringRunes(5)),
Namespace: namespace,
}
err = applyGitRepository(repositoryName, artifactName, "main/"+artifactChecksum)
g.Expect(err).NotTo(HaveOccurred())
// Create Kustomization WITHOUT spec.decryption.secretRef
kustomizationKey := types.NamespacedName{
Name: fmt.Sprintf("global-sops-%s", randStringRunes(5)),
Namespace: namespace,
}
kustomization := &kustomizev1.Kustomization{
ObjectMeta: metav1.ObjectMeta{
Name: kustomizationKey.Name,
Namespace: kustomizationKey.Namespace,
},
Spec: kustomizev1.KustomizationSpec{
Interval: metav1.Duration{Duration: 2 * time.Minute},
Path: "./",
SourceRef: kustomizev1.CrossNamespaceSourceReference{
Name: repositoryName.Name,
Namespace: repositoryName.Namespace,
Kind: sourcev1.GitRepositoryKind,
},
TargetNamespace: namespace,
Decryption: &kustomizev1.Decryption{
Provider: "sops",
},
},
}
g.Expect(k8sClient.Create(context.TODO(), kustomization)).To(Succeed())
g.Eventually(func() bool {
var obj kustomizev1.Kustomization
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), &obj)
return obj.Status.LastAppliedRevision == "main/"+artifactChecksum
}, timeout, time.Second).Should(BeTrue())
// Verify the SOPS encrypted secret was decrypted using the global secret
var secret corev1.Secret
g.Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: "global-age-secret", Namespace: namespace}, &secret)).To(Succeed())
g.Expect(string(secret.Data["key"])).To(Equal("global-value"))
})
t.Run("spec.decryption.secretRef takes precedence over global secret", func(t *testing.T) {
g := NewWithT(t)
namespace := "precedence-" + randStringRunes(5)
t.Setenv("RUNTIME_NAMESPACE", namespace)
err := createNamespace(namespace)
g.Expect(err).NotTo(HaveOccurred())
// Create global SOPS secret
ageGlobalKey, err := os.ReadFile("testdata/sops/keys/age-global.txt")
g.Expect(err).NotTo(HaveOccurred())
globalSOPSSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: sopsAgeSecret,
Namespace: namespace,
},
StringData: map[string]string{
"identity.agekey": string(ageGlobalKey),
},
}
g.Expect(k8sClient.Create(context.Background(), globalSOPSSecret)).To(Succeed())
localSOPSSecretKey := types.NamespacedName{
Name: "local-sops-" + randStringRunes(5),
Namespace: namespace,
}
localSOPSSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: localSOPSSecretKey.Name,
Namespace: localSOPSSecretKey.Namespace,
},
StringData: map[string]string{
"pgp.asc": string(pgpKey),
"age.agekey": string(ageKey),
"sops.vault-token": "secret",
},
}
g.Expect(k8sClient.Create(context.Background(), localSOPSSecret)).To(Succeed())
artifactName := "precedence-" + randStringRunes(5)
artifactChecksum, err := testServer.ArtifactFromDir("testdata/sops/algorithms", artifactName)
g.Expect(err).NotTo(HaveOccurred())
repositoryName := types.NamespacedName{
Name: fmt.Sprintf("precedence-%s", randStringRunes(5)),
Namespace: namespace,
}
err = applyGitRepository(repositoryName, artifactName, "main/"+artifactChecksum)
g.Expect(err).NotTo(HaveOccurred())
// Create Kustomization WITH spec.decryption.secretRef
kustomizationKey := types.NamespacedName{
Name: fmt.Sprintf("precedence-%s", randStringRunes(5)),
Namespace: namespace,
}
kustomization := &kustomizev1.Kustomization{
ObjectMeta: metav1.ObjectMeta{
Name: kustomizationKey.Name,
Namespace: kustomizationKey.Namespace,
},
Spec: kustomizev1.KustomizationSpec{
Interval: metav1.Duration{Duration: 2 * time.Minute},
Path: "./",
SourceRef: kustomizev1.CrossNamespaceSourceReference{
Name: repositoryName.Name,
Namespace: repositoryName.Namespace,
Kind: sourcev1.GitRepositoryKind,
},
Decryption: &kustomizev1.Decryption{
Provider: "sops",
SecretRef: &meta.LocalObjectReference{
Name: localSOPSSecretKey.Name,
},
},
TargetNamespace: namespace,
},
}
g.Expect(k8sClient.Create(context.TODO(), kustomization)).To(Succeed())
g.Eventually(func() bool {
var obj kustomizev1.Kustomization
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), &obj)
return obj.Status.LastAppliedRevision == "main/"+artifactChecksum
}, timeout, time.Second).Should(BeTrue())
// Verify the secret was decrypted using the local secret (not the global one)
var secret corev1.Secret
g.Expect(k8sClient.Get(context.TODO(), types.NamespacedName{Name: "algo-age", Namespace: namespace}, &secret)).To(Succeed())
g.Expect(string(secret.Data["key"])).To(Equal("value"))
})
} }

View File

@ -125,7 +125,7 @@ data:
Interval: metav1.Duration{Duration: reconciliationInterval}, Interval: metav1.Duration{Duration: reconciliationInterval},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },

View File

@ -19,14 +19,16 @@ package controller
import ( import (
"context" "context"
"fmt" "fmt"
"strings"
"testing" "testing"
"time" "time"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/conditions"
"github.com/fluxcd/pkg/testserver" "github.com/fluxcd/pkg/testserver"
sourcev1 "github.com/fluxcd/source-controller/api/v1" sourcev1 "github.com/fluxcd/source-controller/api/v1"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
apimeta "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
@ -117,10 +119,10 @@ spec:
Namespace: kustomizationKey.Namespace, Namespace: kustomizationKey.Namespace,
}, },
Spec: kustomizev1.KustomizationSpec{ Spec: kustomizev1.KustomizationSpec{
Interval: metav1.Duration{Duration: reconciliationInterval}, Interval: metav1.Duration{Duration: time.Hour},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },
@ -140,15 +142,14 @@ spec:
g.Eventually(func() bool { g.Eventually(func() bool {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK) _ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
return apimeta.FindStatusCondition(resultK.Status.Conditions, meta.ReadyCondition) != nil return conditions.Has(resultK, meta.ReadyCondition)
}, timeout, time.Second).Should(BeTrue()) }, timeout, time.Second).Should(BeTrue())
t.Run("fails due to source not found", func(t *testing.T) { t.Run("fails due to source not found", func(t *testing.T) {
g := NewWithT(t) g := NewWithT(t)
g.Eventually(func() bool { g.Eventually(func() bool {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK) _ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
ready := apimeta.FindStatusCondition(resultK.Status.Conditions, meta.ReadyCondition) return conditions.HasAnyReason(resultK, meta.ReadyCondition, meta.ArtifactFailedReason)
return ready.Reason == meta.ArtifactFailedReason
}, timeout, time.Second).Should(BeTrue()) }, timeout, time.Second).Should(BeTrue())
}) })
@ -159,8 +160,7 @@ spec:
g.Eventually(func() bool { g.Eventually(func() bool {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK) _ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
ready := apimeta.FindStatusCondition(resultK.Status.Conditions, meta.ReadyCondition) return conditions.IsReady(resultK)
return ready.Reason == meta.ReconciliationSucceededReason
}, timeout, time.Second).Should(BeTrue()) }, timeout, time.Second).Should(BeTrue())
}) })
@ -168,7 +168,7 @@ spec:
g := NewWithT(t) g := NewWithT(t)
g.Eventually(func() error { g.Eventually(func() error {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK) _ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
resultK.Spec.DependsOn = []meta.NamespacedObjectReference{ resultK.Spec.DependsOn = []kustomizev1.DependencyReference{
{ {
Namespace: id, Namespace: id,
Name: "root", Name: "root",
@ -179,8 +179,203 @@ spec:
g.Eventually(func() bool { g.Eventually(func() bool {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK) _ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
ready := apimeta.FindStatusCondition(resultK.Status.Conditions, meta.ReadyCondition) return conditions.HasAnyReason(resultK, meta.ReadyCondition, meta.DependencyNotReadyReason)
return ready.Reason == meta.DependencyNotReadyReason }, timeout, time.Second).Should(BeTrue())
})
}
func TestKustomizationReconciler_DependsOn_CEL(t *testing.T) {
g := NewWithT(t)
id := "dep-cel" + randStringRunes(5)
depID := "test-dep-" + randStringRunes(5)
revision := "v1.0.0"
err := createNamespace(id)
g.Expect(err).NotTo(HaveOccurred(), "failed to create test namespace")
err = createKubeConfigSecret(id)
g.Expect(err).NotTo(HaveOccurred(), "failed to create kubeconfig secret")
manifests := func(name string, data string) []testserver.File {
return []testserver.File{
{
Name: "config.yaml",
Body: fmt.Sprintf(`---
apiVersion: v1
kind: ConfigMap
metadata:
name: %[1]s
data:
key: "%[2]s"
`, name, data),
},
}
}
artifact, err := testServer.ArtifactFromFiles(manifests(id, id))
g.Expect(err).NotTo(HaveOccurred())
repositoryName := types.NamespacedName{
Name: fmt.Sprintf("dep-%s", randStringRunes(5)),
Namespace: id,
}
err = applyGitRepository(repositoryName, artifact, revision)
g.Expect(err).NotTo(HaveOccurred())
kustomizationKey := types.NamespacedName{
Name: fmt.Sprintf("dep-%s", randStringRunes(5)),
Namespace: id,
}
kustomization := &kustomizev1.Kustomization{
ObjectMeta: metav1.ObjectMeta{
Name: kustomizationKey.Name,
Namespace: kustomizationKey.Namespace,
},
Spec: kustomizev1.KustomizationSpec{
Interval: metav1.Duration{Duration: time.Hour},
Path: "./",
KubeConfig: &meta.KubeConfigReference{
SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig",
},
},
SourceRef: kustomizev1.CrossNamespaceSourceReference{
Name: repositoryName.Name,
Namespace: repositoryName.Namespace,
Kind: sourcev1.GitRepositoryKind,
},
TargetNamespace: id,
Prune: true,
},
}
g.Expect(k8sClient.Create(context.Background(), kustomization)).To(Succeed())
resultK := &kustomizev1.Kustomization{}
g.Eventually(func() bool {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
return conditions.Has(resultK, meta.ReadyCondition)
}, timeout, time.Second).Should(BeTrue())
t.Run("succeeds with readyExpr dependency check", func(t *testing.T) {
g := NewWithT(t)
// Create a dependency Kustomization with matching annotations
dependency := &kustomizev1.Kustomization{
ObjectMeta: metav1.ObjectMeta{
Name: depID,
Namespace: id,
Annotations: map[string]string{
"app/version": "v1.2.3",
},
},
Spec: kustomizev1.KustomizationSpec{
Suspend: true, // Suspended dependency should work with readyExpr and AdditiveCELDependencyCheck disabled
Interval: metav1.Duration{Duration: reconciliationInterval},
Path: "./",
KubeConfig: &meta.KubeConfigReference{
SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig",
},
},
SourceRef: kustomizev1.CrossNamespaceSourceReference{
Name: repositoryName.Name,
Namespace: repositoryName.Namespace,
Kind: sourcev1.GitRepositoryKind,
},
Prune: true,
},
}
g.Expect(k8sClient.Create(context.Background(), dependency)).To(Succeed())
// Update the main Kustomization with matching annotations and readyExpr
g.Eventually(func() error {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
resultK.ObjectMeta.Annotations = map[string]string{
"app/version": "v1.2.3",
}
resultK.Spec.DependsOn = []kustomizev1.DependencyReference{
{
Name: dependency.Name,
ReadyExpr: `self.metadata.annotations['app/version'] == dep.metadata.annotations['app/version']`,
},
}
return k8sClient.Update(context.Background(), resultK)
}, timeout, time.Second).Should(BeNil())
// Should succeed because CEL expression evaluates to true
g.Eventually(func() bool {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
return conditions.IsReady(resultK)
}, timeout, time.Second).Should(BeTrue())
})
t.Run("fails with readyExpr when condition not met", func(t *testing.T) {
g := NewWithT(t)
// Update the main kustomization with mismatched annotations
g.Eventually(func() error {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
resultK.ObjectMeta.Annotations = map[string]string{
"app/version": "v1.2.4",
}
resultK.Spec.DependsOn = []kustomizev1.DependencyReference{
{
Namespace: id,
Name: depID,
ReadyExpr: `self.metadata.annotations['app/version'] == dep.metadata.annotations['app/version']`,
},
}
return k8sClient.Update(context.Background(), resultK)
}, timeout, time.Second).Should(BeNil())
// Should fail because CEL expression evaluates to false
g.Eventually(func() bool {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
ready := conditions.Get(resultK, meta.ReadyCondition)
return ready.Reason == meta.DependencyNotReadyReason &&
strings.Contains(ready.Message, "not ready according to readyExpr")
}, timeout, time.Second).Should(BeTrue())
g.Expect(conditions.IsStalled(resultK)).Should(BeFalse())
})
t.Run("fails terminally with invalid readyExpr", func(t *testing.T) {
g := NewWithT(t)
// Update the main kustomization with invalid CEL expression
g.Eventually(func() error {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
resultK.Spec.DependsOn = []kustomizev1.DependencyReference{
{
Name: depID,
ReadyExpr: `self.generation == deps.generation`, // Invalid vars
},
}
return k8sClient.Update(context.Background(), resultK)
}, timeout, time.Second).Should(BeNil())
// Should be marked as stalled because CEL expression is invalid
g.Eventually(func() bool {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
return conditions.IsStalled(resultK)
}, timeout, time.Second).Should(BeTrue())
g.Expect(conditions.IsReady(resultK)).Should(BeFalse())
g.Expect(conditions.GetReason(resultK, meta.ReadyCondition)).Should(BeIdenticalTo(meta.InvalidCELExpressionReason))
g.Expect(conditions.GetMessage(resultK, meta.ReadyCondition)).Should(ContainSubstring("failed to parse"))
})
t.Run("GC works with failing dependency", func(t *testing.T) {
g := NewWithT(t)
g.Expect(k8sClient.Delete(context.Background(), kustomization)).To(Succeed())
g.Eventually(func() bool {
err = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
return errors.IsNotFound(err)
}, timeout, time.Second).Should(BeTrue()) }, timeout, time.Second).Should(BeTrue())
}) })
} }

View File

@ -86,7 +86,7 @@ data:
Interval: metav1.Duration{Duration: reconciliationInterval}, Interval: metav1.Duration{Duration: reconciliationInterval},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },

View File

@ -0,0 +1,228 @@
/*
Copyright 2025 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
import (
"context"
"fmt"
"os"
"path/filepath"
"testing"
"time"
apiacl "github.com/fluxcd/pkg/apis/acl"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/testserver"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
. "github.com/onsi/gomega"
"github.com/opencontainers/go-digest"
apimeta "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
)
func TestKustomizationReconciler_ExternalArtifact(t *testing.T) {
g := NewWithT(t)
id := "ea-" + randStringRunes(5)
revision := "v1.0.0"
reconciler.AllowExternalArtifact = true
err := createNamespace(id)
g.Expect(err).NotTo(HaveOccurred(), "failed to create test namespace")
err = createKubeConfigSecret(id)
g.Expect(err).NotTo(HaveOccurred(), "failed to create kubeconfig secret")
manifests := func(name string, data string) []testserver.File {
return []testserver.File{
{
Name: "secret.yaml",
Body: fmt.Sprintf(`---
apiVersion: v1
kind: Secret
metadata:
name: %[1]s
stringData:
key: "%[2]s"
`, name, data),
},
}
}
artifact, err := testServer.ArtifactFromFiles(manifests(id, randStringRunes(5)))
g.Expect(err).NotTo(HaveOccurred(), "failed to create artifact from files")
eaName := types.NamespacedName{
Name: randStringRunes(5),
Namespace: id,
}
err = applyExternalArtifact(eaName, artifact, revision)
g.Expect(err).NotTo(HaveOccurred())
kustomizationKey := types.NamespacedName{
Name: fmt.Sprintf("ea-%s", randStringRunes(5)),
Namespace: id,
}
kustomization := &kustomizev1.Kustomization{
ObjectMeta: metav1.ObjectMeta{
Name: kustomizationKey.Name,
Namespace: kustomizationKey.Namespace,
},
Spec: kustomizev1.KustomizationSpec{
Interval: metav1.Duration{Duration: time.Hour},
Path: "./",
KubeConfig: &meta.KubeConfigReference{
SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig",
},
},
SourceRef: kustomizev1.CrossNamespaceSourceReference{
Name: eaName.Name,
Namespace: eaName.Namespace,
Kind: sourcev1.ExternalArtifactKind,
},
TargetNamespace: id,
Wait: true,
},
}
g.Expect(k8sClient.Create(context.Background(), kustomization)).To(Succeed())
resultK := &kustomizev1.Kustomization{}
readyCondition := &metav1.Condition{}
t.Run("reconciles from external artifact source", func(t *testing.T) {
g.Eventually(func() bool {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
readyCondition = apimeta.FindStatusCondition(resultK.Status.Conditions, meta.ReadyCondition)
return resultK.Status.LastAppliedRevision == revision
}, timeout, time.Second).Should(BeTrue())
g.Expect(readyCondition.Reason).To(Equal(meta.ReconciliationSucceededReason))
g.Expect(resultK.Status.LastAppliedRevision).To(Equal(revision))
events := getEvents(resultK.GetName(), map[string]string{"kustomize.toolkit.fluxcd.io/revision": revision})
g.Expect(len(events) > 2).To(BeTrue())
g.Expect(events[0].Reason).To(BeIdenticalTo(meta.ProgressingReason))
g.Expect(events[0].Message).To(ContainSubstring("created"))
g.Expect(events[1].Reason).To(BeIdenticalTo(meta.ProgressingReason))
g.Expect(events[1].Message).To(ContainSubstring("check passed"))
g.Expect(events[2].Reason).To(BeIdenticalTo(meta.ReconciliationSucceededReason))
g.Expect(events[2].Message).To(ContainSubstring("finished"))
})
t.Run("watches for external artifact revision change", func(t *testing.T) {
newRev := "v2.0.0"
err = applyExternalArtifact(eaName, artifact, newRev)
g.Expect(err).NotTo(HaveOccurred())
g.Eventually(func() bool {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
return resultK.Status.LastAppliedRevision == newRev
}, timeout, time.Second).Should(BeTrue())
g.Expect(resultK.Status.History).To(HaveLen(1))
g.Expect(resultK.Status.History[0].TotalReconciliations).To(BeEquivalentTo(2))
g.Expect(resultK.Status.History[0].LastReconciledStatus).To(Equal(meta.ReconciliationSucceededReason))
g.Expect(resultK.Status.History[0].Metadata).To(ContainElements(newRev))
})
t.Run("fails when external artifact feature gate is disable", func(t *testing.T) {
newRev := "v3.0.0"
reconciler.AllowExternalArtifact = false
err = applyExternalArtifact(eaName, artifact, newRev)
g.Expect(err).NotTo(HaveOccurred())
g.Eventually(func() bool {
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), resultK)
readyCondition = apimeta.FindStatusCondition(resultK.Status.Conditions, meta.ReadyCondition)
return apimeta.IsStatusConditionFalse(resultK.Status.Conditions, meta.ReadyCondition)
}, timeout, time.Second).Should(BeTrue())
g.Expect(readyCondition.Reason).To(Equal(apiacl.AccessDeniedReason))
g.Expect(apimeta.IsStatusConditionTrue(resultK.Status.Conditions, meta.StalledCondition)).Should(BeTrue())
events := getEvents(resultK.GetName(), nil)
g.Expect(events[len(events)-1].Reason).To(BeIdenticalTo(apiacl.AccessDeniedReason))
g.Expect(events[len(events)-1].Message).To(ContainSubstring("feature gate is disabled"))
})
}
func applyExternalArtifact(objKey client.ObjectKey, artifactName string, revision string) error {
ea := &sourcev1.ExternalArtifact{
TypeMeta: metav1.TypeMeta{
Kind: sourcev1.ExternalArtifactKind,
APIVersion: sourcev1.GroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: objKey.Name,
Namespace: objKey.Namespace,
},
}
b, _ := os.ReadFile(filepath.Join(testServer.Root(), artifactName))
dig := digest.SHA256.FromBytes(b)
url := fmt.Sprintf("%s/%s", testServer.URL(), artifactName)
status := sourcev1.ExternalArtifactStatus{
Conditions: []metav1.Condition{
{
Type: meta.ReadyCondition,
Status: metav1.ConditionTrue,
LastTransitionTime: metav1.Now(),
Reason: meta.SucceededReason,
},
},
Artifact: &meta.Artifact{
Path: url,
URL: url,
Revision: revision,
Digest: dig.String(),
LastUpdateTime: metav1.Now(),
},
}
patchOpts := []client.PatchOption{
client.ForceOwnership,
client.FieldOwner("kustomize-controller"),
}
if err := k8sClient.Patch(context.Background(), ea, client.Apply, patchOpts...); err != nil {
return err
}
ea.ManagedFields = nil
ea.Status = status
statusOpts := &client.SubResourcePatchOptions{
PatchOptions: client.PatchOptions{
FieldManager: "source-controller",
},
}
if err := k8sClient.Status().Patch(context.Background(), ea, client.Apply, statusOpts); err != nil {
return err
}
return nil
}

View File

@ -86,7 +86,7 @@ stringData:
Interval: metav1.Duration{Duration: reconciliationInterval}, Interval: metav1.Duration{Duration: reconciliationInterval},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },

View File

@ -87,7 +87,7 @@ stringData:
Interval: metav1.Duration{Duration: reconciliationInterval}, Interval: metav1.Duration{Duration: reconciliationInterval},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },

View File

@ -232,7 +232,7 @@ func Fuzz_Controllers(f *testing.F) {
Spec: kustomizev1.KustomizationSpec{ Spec: kustomizev1.KustomizationSpec{
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },

View File

@ -93,7 +93,7 @@ data:
Interval: metav1.Duration{Duration: time.Minute}, Interval: metav1.Duration{Duration: time.Minute},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },
@ -261,7 +261,7 @@ data:
Interval: metav1.Duration{Duration: time.Minute}, Interval: metav1.Duration{Duration: time.Minute},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: secretName, Name: secretName,
Key: secretKey, Key: secretKey,
}, },

View File

@ -20,14 +20,14 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/fluxcd/pkg/runtime/conditions"
ctrl "sigs.k8s.io/controller-runtime" ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/runtime/conditions"
"github.com/fluxcd/pkg/runtime/dependency" "github.com/fluxcd/pkg/runtime/dependency"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
) )
@ -36,7 +36,7 @@ func (r *KustomizationReconciler) requestsForRevisionChangeOf(indexKey string) h
return func(ctx context.Context, obj client.Object) []reconcile.Request { return func(ctx context.Context, obj client.Object) []reconcile.Request {
log := ctrl.LoggerFrom(ctx) log := ctrl.LoggerFrom(ctx)
repo, ok := obj.(interface { repo, ok := obj.(interface {
GetArtifact() *sourcev1.Artifact GetArtifact() *meta.Artifact
}) })
if !ok { if !ok {
log.Error(fmt.Errorf("expected an object conformed with GetArtifact() method, but got a %T", obj), log.Error(fmt.Errorf("expected an object conformed with GetArtifact() method, but got a %T", obj),
@ -64,16 +64,11 @@ func (r *KustomizationReconciler) requestsForRevisionChangeOf(indexKey string) h
} }
dd = append(dd, d.DeepCopy()) dd = append(dd, d.DeepCopy())
} }
sorted, err := dependency.Sort(dd) reqs, err := sortAndEnqueue(dd)
if err != nil { if err != nil {
log.Error(err, "failed to sort dependencies for revision change") log.Error(err, "failed to sort dependencies for revision change")
return nil return nil
} }
reqs := make([]reconcile.Request, len(sorted))
for i := range sorted {
reqs[i].NamespacedName.Name = sorted[i].Name
reqs[i].NamespacedName.Namespace = sorted[i].Namespace
}
return reqs return reqs
} }
} }
@ -96,3 +91,54 @@ func (r *KustomizationReconciler) indexBy(kind string) func(o client.Object) []s
return nil return nil
} }
} }
// requestsForConfigDependency enqueues requests for watched ConfigMaps or Secrets
// according to the specified index.
func (r *KustomizationReconciler) requestsForConfigDependency(
index string) func(ctx context.Context, o client.Object) []reconcile.Request {
return func(ctx context.Context, o client.Object) []reconcile.Request {
log := ctrl.LoggerFrom(ctx).WithValues("index", index, "objectRef", map[string]string{
"name": o.GetName(),
"namespace": o.GetNamespace(),
})
// List Kustomizations that have a dependency on the ConfigMap or Secret.
var list kustomizev1.KustomizationList
if err := r.List(ctx, &list, client.MatchingFields{
index: client.ObjectKeyFromObject(o).String(),
}); err != nil {
log.Error(err, "failed to list Kustomizations for config dependency change")
return nil
}
// Sort the Kustomizations by their dependencies to ensure
// that dependent Kustomizations are reconciled after their dependencies.
dd := make([]dependency.Dependent, 0, len(list.Items))
for i := range list.Items {
dd = append(dd, &list.Items[i])
}
// Enqueue requests for each Kustomization in the list.
reqs, err := sortAndEnqueue(dd)
if err != nil {
log.Error(err, "failed to sort dependencies for config dependency change")
return nil
}
return reqs
}
}
// sortAndEnqueue sorts the dependencies and returns a slice of reconcile.Requests.
func sortAndEnqueue(dd []dependency.Dependent) ([]reconcile.Request, error) {
sorted, err := dependency.Sort(dd)
if err != nil {
return nil, err
}
reqs := make([]reconcile.Request, len(sorted))
for i := range sorted {
reqs[i].NamespacedName.Name = sorted[i].Name
reqs[i].NamespacedName.Namespace = sorted[i].Namespace
}
return reqs, nil
}

View File

@ -97,7 +97,7 @@ stringData:
Interval: metav1.Duration{Duration: 2 * time.Minute}, Interval: metav1.Duration{Duration: 2 * time.Minute},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },

View File

@ -0,0 +1,171 @@
/*
Copyright 2025 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
import (
"context"
"fmt"
corev1 "k8s.io/api/core/v1"
"k8s.io/client-go/util/workqueue"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/fluxcd/pkg/runtime/predicates"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
)
// KustomizationReconcilerOptions contains options for the KustomizationReconciler.
type KustomizationReconcilerOptions struct {
RateLimiter workqueue.TypedRateLimiter[reconcile.Request]
WatchConfigsPredicate predicate.Predicate
WatchExternalArtifacts bool
}
// SetupWithManager sets up the controller with the Manager.
// It indexes the Kustomizations by the source references, and sets up watches for
// changes in those sources, as well as for ConfigMaps and Secrets that the Kustomizations depend on.
func (r *KustomizationReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, opts KustomizationReconcilerOptions) error {
const (
indexExternalArtifact = ".metadata.externalArtifact"
indexOCIRepository = ".metadata.ociRepository"
indexGitRepository = ".metadata.gitRepository"
indexBucket = ".metadata.bucket"
indexConfigMap = ".metadata.configMap"
indexSecret = ".metadata.secret"
)
// Index the Kustomizations by the OCIRepository references they (may) point at.
if err := mgr.GetCache().IndexField(ctx, &kustomizev1.Kustomization{}, indexOCIRepository,
r.indexBy(sourcev1.OCIRepositoryKind)); err != nil {
return fmt.Errorf("failed creating index %s: %w", indexOCIRepository, err)
}
// Index the Kustomizations by the GitRepository references they (may) point at.
if err := mgr.GetCache().IndexField(ctx, &kustomizev1.Kustomization{}, indexGitRepository,
r.indexBy(sourcev1.GitRepositoryKind)); err != nil {
return fmt.Errorf("failed creating index %s: %w", indexGitRepository, err)
}
// Index the Kustomizations by the Bucket references they (may) point at.
if err := mgr.GetCache().IndexField(ctx, &kustomizev1.Kustomization{}, indexBucket,
r.indexBy(sourcev1.BucketKind)); err != nil {
return fmt.Errorf("failed creating index %s: %w", indexBucket, err)
}
// Index the Kustomizations by the ExternalArtifact references they (may) point at (if enabled).
if opts.WatchExternalArtifacts {
if err := mgr.GetCache().IndexField(ctx, &kustomizev1.Kustomization{}, indexExternalArtifact,
r.indexBy(sourcev1.ExternalArtifactKind)); err != nil {
return fmt.Errorf("failed creating index %s: %w", indexExternalArtifact, err)
}
}
// Index the Kustomization by the ConfigMap references they point to.
if err := mgr.GetFieldIndexer().IndexField(ctx, &kustomizev1.Kustomization{}, indexConfigMap,
func(o client.Object) []string {
obj := o.(*kustomizev1.Kustomization)
namespace := obj.GetNamespace()
var keys []string
if kc := obj.Spec.KubeConfig; kc != nil && kc.ConfigMapRef != nil {
keys = append(keys, fmt.Sprintf("%s/%s", namespace, kc.ConfigMapRef.Name))
}
if pb := obj.Spec.PostBuild; pb != nil {
for _, ref := range pb.SubstituteFrom {
if ref.Kind == "ConfigMap" {
keys = append(keys, fmt.Sprintf("%s/%s", namespace, ref.Name))
}
}
}
return keys
},
); err != nil {
return fmt.Errorf("failed creating index %s: %w", indexConfigMap, err)
}
// Index the Kustomization by the Secret references they point to.
if err := mgr.GetFieldIndexer().IndexField(ctx, &kustomizev1.Kustomization{}, indexSecret,
func(o client.Object) []string {
obj := o.(*kustomizev1.Kustomization)
namespace := obj.GetNamespace()
var keys []string
if dec := obj.Spec.Decryption; dec != nil && dec.SecretRef != nil {
keys = append(keys, fmt.Sprintf("%s/%s", namespace, dec.SecretRef.Name))
}
if kc := obj.Spec.KubeConfig; kc != nil && kc.SecretRef != nil {
keys = append(keys, fmt.Sprintf("%s/%s", namespace, kc.SecretRef.Name))
}
if pb := obj.Spec.PostBuild; pb != nil {
for _, ref := range pb.SubstituteFrom {
if ref.Kind == "Secret" {
keys = append(keys, fmt.Sprintf("%s/%s", namespace, ref.Name))
}
}
}
return keys
},
); err != nil {
return fmt.Errorf("failed creating index %s: %w", indexSecret, err)
}
ctrlBuilder := ctrl.NewControllerManagedBy(mgr).
For(&kustomizev1.Kustomization{}, builder.WithPredicates(
predicate.Or(predicate.GenerationChangedPredicate{}, predicates.ReconcileRequestedPredicate{}),
)).
Watches(
&sourcev1.OCIRepository{},
handler.EnqueueRequestsFromMapFunc(r.requestsForRevisionChangeOf(indexOCIRepository)),
builder.WithPredicates(SourceRevisionChangePredicate{}),
).
Watches(
&sourcev1.GitRepository{},
handler.EnqueueRequestsFromMapFunc(r.requestsForRevisionChangeOf(indexGitRepository)),
builder.WithPredicates(SourceRevisionChangePredicate{}),
).
Watches(
&sourcev1.Bucket{},
handler.EnqueueRequestsFromMapFunc(r.requestsForRevisionChangeOf(indexBucket)),
builder.WithPredicates(SourceRevisionChangePredicate{}),
).
WatchesMetadata(
&corev1.ConfigMap{},
handler.EnqueueRequestsFromMapFunc(r.requestsForConfigDependency(indexConfigMap)),
builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}, opts.WatchConfigsPredicate),
).
WatchesMetadata(
&corev1.Secret{},
handler.EnqueueRequestsFromMapFunc(r.requestsForConfigDependency(indexSecret)),
builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}, opts.WatchConfigsPredicate),
)
if opts.WatchExternalArtifacts {
ctrlBuilder = ctrlBuilder.Watches(
&sourcev1.ExternalArtifact{},
handler.EnqueueRequestsFromMapFunc(r.requestsForRevisionChangeOf(indexExternalArtifact)),
builder.WithPredicates(SourceRevisionChangePredicate{}),
)
}
return ctrlBuilder.WithOptions(controller.Options{RateLimiter: opts.RateLimiter}).Complete(r)
}

View File

@ -87,7 +87,7 @@ stringData:
Interval: metav1.Duration{Duration: reconciliationInterval}, Interval: metav1.Duration{Duration: reconciliationInterval},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },
@ -115,6 +115,15 @@ stringData:
g.Expect(resultK.Status.LastAppliedOriginRevision).To(Equal("orev")) g.Expect(resultK.Status.LastAppliedOriginRevision).To(Equal("orev"))
g.Expect(resultK.Status.History).To(HaveLen(1))
g.Expect(resultK.Status.History[0].Digest).ToNot(BeEmpty())
g.Expect(resultK.Status.History[0].TotalReconciliations).To(BeEquivalentTo(1))
g.Expect(resultK.Status.History[0].FirstReconciled.Time).ToNot(BeZero())
g.Expect(resultK.Status.History[0].LastReconciled.Time).ToNot(BeZero())
g.Expect(resultK.Status.History[0].LastReconciledStatus).To(Equal(meta.ReconciliationSucceededReason))
g.Expect(resultK.Status.History[0].LastReconciledDuration.Duration).To(BeNumerically(">", 0))
g.Expect(resultK.Status.History[0].Metadata).To(ContainElements(revision, "orev"))
events := getEvents(kustomizationKey.Name, nil) events := getEvents(kustomizationKey.Name, nil)
g.Expect(events).To(Not(BeEmpty())) g.Expect(events).To(Not(BeEmpty()))

View File

@ -0,0 +1,243 @@
/*
Copyright 2025 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package controller
import (
"context"
"testing"
"time"
"github.com/fluxcd/pkg/apis/kustomize"
"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/testserver"
sourcev1 "github.com/fluxcd/source-controller/api/v1"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
)
// TestKustomizationReconciler_MultiplePatchDelete tests the handling of multiple
// $patch: delete directives in strategic merge patches.
// This test ensures that the controller properly handles scenarios where multiple
// resources are deleted using a single patch specification.
func TestKustomizationReconciler_MultiplePatchDelete(t *testing.T) {
g := NewWithT(t)
id := "multi-patch-delete-" + randStringRunes(5)
revision := "v1.0.0"
err := createNamespace(id)
g.Expect(err).NotTo(HaveOccurred(), "failed to create test namespace")
err = createKubeConfigSecret(id)
g.Expect(err).NotTo(HaveOccurred(), "failed to create kubeconfig secret")
// Create test files with multiple ConfigMaps
manifests := func(name string, data string) []testserver.File {
return []testserver.File{
{
Name: "configmaps.yaml",
Body: `---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm1
namespace: ` + name + `
data:
key: ` + data + `1
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm2
namespace: ` + name + `
data:
key: ` + data + `2
---
apiVersion: v1
kind: ConfigMap
metadata:
name: cm3
namespace: ` + name + `
data:
key: ` + data + `3
`,
},
}
}
artifact, err := testServer.ArtifactFromFiles(manifests(id, randStringRunes(5)))
g.Expect(err).NotTo(HaveOccurred())
repositoryName := types.NamespacedName{
Name: randStringRunes(5),
Namespace: id,
}
err = applyGitRepository(repositoryName, artifact, revision)
g.Expect(err).NotTo(HaveOccurred())
kustomizationKey := types.NamespacedName{
Name: "patch-delete-" + randStringRunes(5),
Namespace: id,
}
t.Run("multiple patch delete in single patch should work", func(t *testing.T) {
// This test verifies that multiple $patch: delete directives in a single patch work correctly
// Ref: https://github.com/fluxcd/kustomize-controller/issues/1306
kustomization := &kustomizev1.Kustomization{
ObjectMeta: metav1.ObjectMeta{
Name: kustomizationKey.Name,
Namespace: kustomizationKey.Namespace,
},
Spec: kustomizev1.KustomizationSpec{
Interval: metav1.Duration{Duration: reconciliationInterval},
Path: "./",
KubeConfig: &meta.KubeConfigReference{
SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig",
},
},
SourceRef: kustomizev1.CrossNamespaceSourceReference{
Name: repositoryName.Name,
Namespace: repositoryName.Namespace,
Kind: sourcev1.GitRepositoryKind,
},
Prune: true,
Patches: []kustomize.Patch{
{
// Multiple $patch: delete in a single patch
Patch: `$patch: delete
apiVersion: v1
kind: ConfigMap
metadata:
name: cm1
namespace: ` + id + `
---
$patch: delete
apiVersion: v1
kind: ConfigMap
metadata:
name: cm2
namespace: ` + id + ``,
},
},
},
}
g.Expect(k8sClient.Create(context.Background(), kustomization)).To(Succeed())
// Wait for reconciliation and check that it succeeds without panic
g.Eventually(func() bool {
var obj kustomizev1.Kustomization
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), &obj)
return obj.Status.LastAppliedRevision == revision
}, timeout, time.Second).Should(BeTrue())
// Verify that only cm3 ConfigMap exists (cm1 and cm2 should be deleted)
var cm corev1.ConfigMap
err := k8sClient.Get(context.Background(), client.ObjectKey{Name: "cm1", Namespace: id}, &cm)
g.Expect(err).To(HaveOccurred(), "cm1 should have been deleted")
err = k8sClient.Get(context.Background(), client.ObjectKey{Name: "cm2", Namespace: id}, &cm)
g.Expect(err).To(HaveOccurred(), "cm2 should have been deleted")
err = k8sClient.Get(context.Background(), client.ObjectKey{Name: "cm3", Namespace: id}, &cm)
g.Expect(err).NotTo(HaveOccurred(), "cm3 should still exist")
// Cleanup
g.Expect(k8sClient.Delete(context.Background(), kustomization)).To(Succeed())
g.Eventually(func() bool {
err := k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomization), kustomization)
return apierrors.IsNotFound(err)
}, timeout, time.Second).Should(BeTrue())
})
t.Run("multiple patch delete in separate patches should work", func(t *testing.T) {
// This test verifies that separate patches (which was previously a workaround) still work correctly
kustomizationSeparate := &kustomizev1.Kustomization{
ObjectMeta: metav1.ObjectMeta{
Name: kustomizationKey.Name + "-separate",
Namespace: kustomizationKey.Namespace,
},
Spec: kustomizev1.KustomizationSpec{
Interval: metav1.Duration{Duration: reconciliationInterval},
Path: "./",
KubeConfig: &meta.KubeConfigReference{
SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig",
},
},
SourceRef: kustomizev1.CrossNamespaceSourceReference{
Name: repositoryName.Name,
Namespace: repositoryName.Namespace,
Kind: sourcev1.GitRepositoryKind,
},
Prune: true,
Patches: []kustomize.Patch{
{
Patch: `$patch: delete
apiVersion: v1
kind: ConfigMap
metadata:
name: cm1
namespace: ` + id + ``,
},
{
Patch: `$patch: delete
apiVersion: v1
kind: ConfigMap
metadata:
name: cm2
namespace: ` + id + ``,
},
},
},
}
g.Expect(k8sClient.Create(context.Background(), kustomizationSeparate)).To(Succeed())
// Wait for successful reconciliation
g.Eventually(func() bool {
var obj kustomizev1.Kustomization
_ = k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomizationSeparate), &obj)
return obj.Status.LastAppliedRevision == revision
}, timeout, time.Second).Should(BeTrue())
// Verify that only cm3 ConfigMap exists
var cm corev1.ConfigMap
err := k8sClient.Get(context.Background(), client.ObjectKey{Name: "cm1", Namespace: id}, &cm)
g.Expect(err).To(HaveOccurred(), "cm1 should have been deleted")
err = k8sClient.Get(context.Background(), client.ObjectKey{Name: "cm2", Namespace: id}, &cm)
g.Expect(err).To(HaveOccurred(), "cm2 should have been deleted")
err = k8sClient.Get(context.Background(), client.ObjectKey{Name: "cm3", Namespace: id}, &cm)
g.Expect(err).NotTo(HaveOccurred(), "cm3 should still exist")
// Cleanup
g.Expect(k8sClient.Delete(context.Background(), kustomizationSeparate)).To(Succeed())
g.Eventually(func() bool {
err := k8sClient.Get(context.Background(), client.ObjectKeyFromObject(kustomizationSeparate), kustomizationSeparate)
return apierrors.IsNotFound(err)
}, timeout, time.Second).Should(BeTrue())
})
}

View File

@ -99,7 +99,7 @@ data:
Interval: metav1.Duration{Duration: reconciliationInterval}, Interval: metav1.Duration{Duration: reconciliationInterval},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },
@ -227,7 +227,7 @@ data:
Interval: metav1.Duration{Duration: reconciliationInterval}, Interval: metav1.Duration{Duration: reconciliationInterval},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },
@ -385,7 +385,7 @@ data:
Interval: metav1.Duration{Duration: reconciliationInterval}, Interval: metav1.Duration{Duration: reconciliationInterval},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },

View File

@ -91,7 +91,7 @@ data:
Interval: metav1.Duration{Duration: 2 * time.Minute}, Interval: metav1.Duration{Duration: 2 * time.Minute},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },
@ -200,7 +200,7 @@ data:
Interval: metav1.Duration{Duration: 2 * time.Minute}, Interval: metav1.Duration{Duration: 2 * time.Minute},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },
@ -270,7 +270,7 @@ func TestKustomizationReconciler_KustomizeTransformer(t *testing.T) {
Interval: metav1.Duration{Duration: reconciliationInterval}, Interval: metav1.Duration{Duration: reconciliationInterval},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },
@ -393,7 +393,7 @@ func TestKustomizationReconciler_KustomizeTransformerFiles(t *testing.T) {
Interval: metav1.Duration{Duration: reconciliationInterval}, Interval: metav1.Duration{Duration: reconciliationInterval},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },
@ -512,7 +512,7 @@ func TestKustomizationReconciler_FluxTransformers(t *testing.T) {
Interval: metav1.Duration{Duration: reconciliationInterval}, Interval: metav1.Duration{Duration: reconciliationInterval},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },

View File

@ -80,7 +80,7 @@ func TestKustomizationReconciler_Validation(t *testing.T) {
Interval: metav1.Duration{Duration: 2 * time.Minute}, Interval: metav1.Duration{Duration: 2 * time.Minute},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },

View File

@ -120,7 +120,7 @@ stringData:
}, },
Spec: kustomizev1.KustomizationSpec{ Spec: kustomizev1.KustomizationSpec{
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },
@ -269,7 +269,7 @@ metadata:
}, },
Spec: kustomizev1.KustomizationSpec{ Spec: kustomizev1.KustomizationSpec{
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },
@ -426,7 +426,7 @@ data:
}, },
Spec: kustomizev1.KustomizationSpec{ Spec: kustomizev1.KustomizationSpec{
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },
@ -532,7 +532,7 @@ metadata:
}, },
Spec: kustomizev1.KustomizationSpec{ Spec: kustomizev1.KustomizationSpec{
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },

View File

@ -105,7 +105,7 @@ parameters:
Interval: metav1.Duration{Duration: 2 * time.Minute}, Interval: metav1.Duration{Duration: 2 * time.Minute},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },
@ -136,6 +136,11 @@ parameters:
g.Expect(resultK.Status.ObservedGeneration).To(BeIdenticalTo(resultK.Generation)) g.Expect(resultK.Status.ObservedGeneration).To(BeIdenticalTo(resultK.Generation))
kstatusCheck.CheckErr(ctx, resultK) kstatusCheck.CheckErr(ctx, resultK)
g.Expect(resultK.Status.History).To(HaveLen(1))
g.Expect(resultK.Status.History[0].TotalReconciliations).To(BeEquivalentTo(1))
g.Expect(resultK.Status.History[0].LastReconciledStatus).To(Equal(meta.ReconciliationSucceededReason))
g.Expect(resultK.Status.History[0].Metadata).To(ContainElements(revision))
}) })
t.Run("reports progressing status", func(t *testing.T) { t.Run("reports progressing status", func(t *testing.T) {
@ -192,6 +197,11 @@ parameters:
g.Expect(resultK.Status.ObservedGeneration).To(BeIdenticalTo(resultK.Generation - 1)) g.Expect(resultK.Status.ObservedGeneration).To(BeIdenticalTo(resultK.Generation - 1))
kstatusCheck.CheckErr(ctx, resultK) kstatusCheck.CheckErr(ctx, resultK)
g.Expect(resultK.Status.History).To(HaveLen(2))
g.Expect(resultK.Status.History[0].TotalReconciliations).To(BeEquivalentTo(1))
g.Expect(resultK.Status.History[0].LastReconciledStatus).To(Equal(meta.HealthCheckFailedReason))
g.Expect(resultK.Status.History[0].Metadata).To(ContainElements(revision))
}) })
t.Run("emits unhealthy event", func(t *testing.T) { t.Run("emits unhealthy event", func(t *testing.T) {
@ -228,6 +238,11 @@ parameters:
g.Expect(resultK.Status.ObservedGeneration).To(BeIdenticalTo(resultK.Generation)) g.Expect(resultK.Status.ObservedGeneration).To(BeIdenticalTo(resultK.Generation))
kstatusCheck.CheckErr(ctx, resultK) kstatusCheck.CheckErr(ctx, resultK)
g.Expect(resultK.Status.History).To(HaveLen(2))
g.Expect(resultK.Status.History[0].TotalReconciliations).To(BeEquivalentTo(2))
g.Expect(resultK.Status.History[0].LastReconciledStatus).To(Equal(meta.ReconciliationSucceededReason))
g.Expect(resultK.Status.History[0].Metadata).To(ContainElements(revision))
}) })
t.Run("emits recovery event", func(t *testing.T) { t.Run("emits recovery event", func(t *testing.T) {
@ -258,6 +273,11 @@ parameters:
g.Expect(resultK.Status.LastAttemptedRevision).To(BeIdenticalTo(resultK.Status.LastAppliedRevision)) g.Expect(resultK.Status.LastAttemptedRevision).To(BeIdenticalTo(resultK.Status.LastAppliedRevision))
kstatusCheck.CheckErr(ctx, resultK) kstatusCheck.CheckErr(ctx, resultK)
g.Expect(resultK.Status.History).To(HaveLen(3))
g.Expect(resultK.Status.History[0].TotalReconciliations).To(BeEquivalentTo(1))
g.Expect(resultK.Status.History[0].LastReconciledStatus).To(Equal(meta.ReconciliationSucceededReason))
g.Expect(resultK.Status.History[0].Metadata).To(ContainElements(revision))
}) })
t.Run("emits event for the new revision", func(t *testing.T) { t.Run("emits event for the new revision", func(t *testing.T) {
@ -331,7 +351,7 @@ data: {}
Interval: metav1.Duration{Duration: 2 * time.Minute}, Interval: metav1.Duration{Duration: 2 * time.Minute},
Path: "./", Path: "./",
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },
@ -411,7 +431,7 @@ func TestKustomizationReconciler_RESTMapper(t *testing.T) {
Kind: sourcev1.GitRepositoryKind, Kind: sourcev1.GitRepositoryKind,
}, },
KubeConfig: &meta.KubeConfigReference{ KubeConfig: &meta.KubeConfigReference{
SecretRef: meta.SecretKeyReference{ SecretRef: &meta.SecretKeyReference{
Name: "kubeconfig", Name: "kubeconfig",
}, },
}, },

View File

@ -37,6 +37,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/envtest" "sigs.k8s.io/controller-runtime/pkg/envtest"
controllerLog "sigs.k8s.io/controller-runtime/pkg/log" controllerLog "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/yaml" "sigs.k8s.io/yaml"
"github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/apis/meta"
@ -57,6 +58,7 @@ const (
reconciliationInterval = time.Second * 5 reconciliationInterval = time.Second * 5
vaultVersion = "1.13.2" vaultVersion = "1.13.2"
overrideManagerName = "node-fetch" overrideManagerName = "node-fetch"
sopsAgeSecret = "sops-age-secret"
) )
var ( var (
@ -173,16 +175,20 @@ func TestMain(m *testing.M) {
kstatusInProgressCheck.DisableFetch = true kstatusInProgressCheck.DisableFetch = true
reconciler = &KustomizationReconciler{ reconciler = &KustomizationReconciler{
ControllerName: controllerName, ControllerName: controllerName,
StatusManager: fmt.Sprintf("gotk-%s", controllerName),
Client: testEnv, Client: testEnv,
Mapper: testEnv.GetRESTMapper(), Mapper: testEnv.GetRESTMapper(),
APIReader: testEnv, APIReader: testEnv,
EventRecorder: testEnv.GetEventRecorderFor(controllerName), EventRecorder: testEnv.GetEventRecorderFor(controllerName),
Metrics: testMetricsH, Metrics: testMetricsH,
DependencyRequeueInterval: 2 * time.Second,
ConcurrentSSA: 4, ConcurrentSSA: 4,
DisallowedFieldManagers: []string{overrideManagerName}, DisallowedFieldManagers: []string{overrideManagerName},
SOPSAgeSecret: sopsAgeSecret,
} }
if err := (reconciler).SetupWithManager(ctx, testEnv, KustomizationReconcilerOptions{ if err := (reconciler).SetupWithManager(ctx, testEnv, KustomizationReconcilerOptions{
DependencyRequeueInterval: 2 * time.Second, WatchConfigsPredicate: predicate.Not(predicate.Funcs{}),
WatchExternalArtifacts: true,
}); err != nil { }); err != nil {
panic(fmt.Sprintf("Failed to start KustomizationReconciler: %v", err)) panic(fmt.Sprintf("Failed to start KustomizationReconciler: %v", err))
} }
@ -327,7 +333,7 @@ func applyGitRepository(objKey client.ObjectKey, artifactName string,
Reason: sourcev1.GitOperationSucceedReason, Reason: sourcev1.GitOperationSucceedReason,
}, },
}, },
Artifact: &sourcev1.Artifact{ Artifact: &meta.Artifact{
Path: url, Path: url,
URL: url, URL: url,
Revision: revision, Revision: revision,

View File

@ -0,0 +1,4 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- secret.yaml

View File

@ -0,0 +1,23 @@
apiVersion: v1
kind: Secret
metadata:
name: global-age-secret
namespace: default
type: Opaque
stringData:
key: ENC[AES256_GCM,data:5d9WYyV8eyrOhF/m,iv:wuxE+q4pB+2cXBLcOy4/eZFSteLXJJDEI001UFAPd3A=,tag:EyM2zosBOv5/DnZMUqyNJg==,type:str]
sops:
age:
- recipient: age1henwn645drkd3fa34t27t2w2x0nsgw3w4h62gmg5x5xflyjpy38s5cncph
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBhSEJIVUQrZXpkZmJ6ek1U
enNmL2cycGJVTUp0bGJBQmhzUms5eENWd1MwCjZtUjMvT28zWU8wVHArNDdyNG1x
RjJCK29jNEJ5bDc5M2pNVG0rcHhFQ2cKLS0tIDkzRk5zdTZ5ek4rNzNvT2l5VDJ1
Sk01a05sV094UjFVbkhsVUhaOFhWa3MKwVILC0MO4CPlK0kniPTxOgMxej+E1NJ3
d1vA+h/DFoHDV6fU63n/v0BLmi7q0x10pYj95u1Ef1cP24JC/G+M4g==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2025-07-06T17:19:51Z"
mac: ENC[AES256_GCM,data:5vkroTHj/9sLCEb+LHZEWwbrq9amLfU9QsDE6ZmpSDa/r7T2RGzX9cAhe73s2L7E5EZblsWOGLPdQCRXP/Llp19QzEU6dYs+n0IyFprjtYpL8VEB3S2aCPzdhOrN0o/pcHGmfvp4S9gXvwki1EIYhRL7ozBHNppxQGLOdVDigjg=,iv:dCzK0olxFn4QwknnYvO4LNo7SjuX04zF6saVRFQv7wY=,tag:GwL7WDw2IBxNUdGUBpBXEQ==,type:str]
encrypted_regex: ^(data|stringData)$
version: 3.10.2

View File

@ -0,0 +1,3 @@
# created: 2025-07-06T17:30:37+01:00
# public key: age1henwn645drkd3fa34t27t2w2x0nsgw3w4h62gmg5x5xflyjpy38s5cncph
AGE-SECRET-KEY-10U7MAELH09KZU63F65P2TYLN7TQM5X6GSDDCQ4YMUGDS3DS6XZMSTYY2PN

View File

@ -162,32 +162,30 @@ type Decryptor struct {
// decryptor. // decryptor.
keyServices []keyservice.KeyServiceClient keyServices []keyservice.KeyServiceClient
localServiceOnce sync.Once localServiceOnce sync.Once
// sopsAgeSecret is the NamespacedName of the Secret containing
// a fallback SOPS age decryption key.
sopsAgeSecret *types.NamespacedName
} }
// NewDecryptor creates a new Decryptor for the given kustomization. // New creates a new Decryptor, with a temporary GnuPG
// gnuPGHome can be empty, in which case the systems' keyring is used.
func NewDecryptor(root string, client client.Client, kustomization *kustomizev1.Kustomization,
maxFileSize int64, gnuPGHome string, tokenCache *cache.TokenCache) *Decryptor {
return &Decryptor{
root: root,
client: client,
kustomization: kustomization,
maxFileSize: maxFileSize,
gnuPGHome: pgp.GnuPGHome(gnuPGHome),
tokenCache: tokenCache,
}
}
// NewTempDecryptor creates a new Decryptor, with a temporary GnuPG
// home directory to Decryptor.ImportKeys() into. // home directory to Decryptor.ImportKeys() into.
func NewTempDecryptor(root string, client client.Client, kustomization *kustomizev1.Kustomization, func New(client client.Client, kustomization *kustomizev1.Kustomization, opts ...Option) (*Decryptor, func(), error) {
tokenCache *cache.TokenCache) (*Decryptor, func(), error) {
gnuPGHome, err := pgp.NewGnuPGHome() gnuPGHome, err := pgp.NewGnuPGHome()
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("cannot create decryptor: %w", err) return nil, nil, fmt.Errorf("cannot create decryptor: %w", err)
} }
cleanup := func() { _ = os.RemoveAll(gnuPGHome.String()) } cleanup := func() { _ = os.RemoveAll(gnuPGHome.String()) }
return NewDecryptor(root, client, kustomization, maxEncryptedFileSize, gnuPGHome.String(), tokenCache), cleanup, nil d := &Decryptor{
client: client,
kustomization: kustomization,
maxFileSize: maxEncryptedFileSize,
gnuPGHome: gnuPGHome,
}
for _, opt := range opts {
opt(d)
}
return d, cleanup, nil
} }
// IsEncryptedSecret checks if the given object is a Kubernetes Secret encrypted // IsEncryptedSecret checks if the given object is a Kubernetes Secret encrypted
@ -210,16 +208,43 @@ func IsEncryptedSecret(object *unstructured.Unstructured) bool {
// For the import of PGP keys, the Decryptor must be configured with // For the import of PGP keys, the Decryptor must be configured with
// an absolute GnuPG home directory path. // an absolute GnuPG home directory path.
func (d *Decryptor) ImportKeys(ctx context.Context) error { func (d *Decryptor) ImportKeys(ctx context.Context) error {
if d.kustomization.Spec.Decryption == nil || d.kustomization.Spec.Decryption.SecretRef == nil { if d.kustomization.Spec.Decryption == nil ||
(d.kustomization.Spec.Decryption.SecretRef == nil && d.sopsAgeSecret == nil) {
return nil return nil
} }
provider := d.kustomization.Spec.Decryption.Provider provider := d.kustomization.Spec.Decryption.Provider
switch provider { switch provider {
case DecryptionProviderSOPS: case DecryptionProviderSOPS:
secretRef := d.kustomization.Spec.Decryption.SecretRef
// We handle the SOPS age global decryption separately, as most of the other
// decryption providers already support global decryption in other ways, and
// we don't want to introduce duplicate methods of achieving the same.
// Furthermore, allowing e.g. cloud provider credentials to be fetched
// from this global secret would prevent workload identity from working.
if secretRef == nil && d.sopsAgeSecret != nil {
var secret corev1.Secret
if err := d.client.Get(ctx, *d.sopsAgeSecret, &secret); err != nil {
if apierrors.IsNotFound(err) {
return err
}
return fmt.Errorf("cannot get %s SOPS age decryption Secret '%s': %w", provider, *d.sopsAgeSecret, err)
}
for name, value := range secret.Data {
if filepath.Ext(name) == DecryptionAgeExt {
if err := d.ageIdentities.Import(string(value)); err != nil {
return fmt.Errorf("failed to import '%s' data from %s SOPS age decryption Secret '%s': %w",
name, provider, *d.sopsAgeSecret, err)
}
}
}
return nil
}
secretName := types.NamespacedName{ secretName := types.NamespacedName{
Namespace: d.kustomization.GetNamespace(), Namespace: d.kustomization.GetNamespace(),
Name: d.kustomization.Spec.Decryption.SecretRef.Name, Name: secretRef.Name,
} }
var secret corev1.Secret var secret corev1.Secret
@ -291,14 +316,17 @@ func (d *Decryptor) SetAuthOptions(ctx context.Context) {
switch d.kustomization.Spec.Decryption.Provider { switch d.kustomization.Spec.Decryption.Provider {
case DecryptionProviderSOPS: case DecryptionProviderSOPS:
var opts []auth.Option opts := []auth.Option{
auth.WithClient(d.client),
if d.kustomization.Spec.Decryption.ServiceAccountName != "" {
serviceAccount := types.NamespacedName{
Name: d.kustomization.Spec.Decryption.ServiceAccountName,
Namespace: d.kustomization.GetNamespace(),
} }
opts = append(opts, auth.WithServiceAccount(serviceAccount, d.client))
saName := d.kustomization.Spec.Decryption.ServiceAccountName
if saName == "" {
saName = auth.GetDefaultDecryptionServiceAccount()
}
if saName != "" {
opts = append(opts, auth.WithServiceAccountName(saName))
opts = append(opts, auth.WithServiceAccountNamespace(d.kustomization.GetNamespace()))
} }
involvedObject := cache.InvolvedObject{ involvedObject := cache.InvolvedObject{

View File

@ -376,7 +376,7 @@ clientSecret: some-client-secret`),
}, },
} }
d, cleanup, err := NewTempDecryptor("", cb.Build(), &kustomization, nil) d, cleanup, err := New(cb.Build(), &kustomization)
g.Expect(err).ToNot(HaveOccurred()) g.Expect(err).ToNot(HaveOccurred())
t.Cleanup(cleanup) t.Cleanup(cleanup)
@ -605,7 +605,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
Provider: DecryptionProviderSOPS, Provider: DecryptionProviderSOPS,
} }
d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kus, nil) d, cleanup, err := New(fake.NewClientBuilder().Build(), kus)
g.Expect(err).ToNot(HaveOccurred()) g.Expect(err).ToNot(HaveOccurred())
t.Cleanup(cleanup) t.Cleanup(cleanup)
@ -646,7 +646,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
Provider: DecryptionProviderSOPS, Provider: DecryptionProviderSOPS,
} }
d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kus, nil) d, cleanup, err := New(fake.NewClientBuilder().Build(), kus)
g.Expect(err).ToNot(HaveOccurred()) g.Expect(err).ToNot(HaveOccurred())
t.Cleanup(cleanup) t.Cleanup(cleanup)
@ -681,7 +681,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
Provider: DecryptionProviderSOPS, Provider: DecryptionProviderSOPS,
} }
d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kus, nil) d, cleanup, err := New(fake.NewClientBuilder().Build(), kus)
g.Expect(err).ToNot(HaveOccurred()) g.Expect(err).ToNot(HaveOccurred())
t.Cleanup(cleanup) t.Cleanup(cleanup)
@ -716,7 +716,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
Provider: DecryptionProviderSOPS, Provider: DecryptionProviderSOPS,
} }
d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kus, nil) d, cleanup, err := New(fake.NewClientBuilder().Build(), kus)
g.Expect(err).ToNot(HaveOccurred()) g.Expect(err).ToNot(HaveOccurred())
t.Cleanup(cleanup) t.Cleanup(cleanup)
@ -765,7 +765,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
t.Run("nil resource", func(t *testing.T) { t.Run("nil resource", func(t *testing.T) {
g := NewWithT(t) g := NewWithT(t)
d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kustomization.DeepCopy(), nil) d, cleanup, err := New(fake.NewClientBuilder().Build(), kustomization.DeepCopy())
g.Expect(err).ToNot(HaveOccurred()) g.Expect(err).ToNot(HaveOccurred())
t.Cleanup(cleanup) t.Cleanup(cleanup)
@ -777,7 +777,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
t.Run("no decryption spec", func(t *testing.T) { t.Run("no decryption spec", func(t *testing.T) {
g := NewWithT(t) g := NewWithT(t)
d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kustomization.DeepCopy(), nil) d, cleanup, err := New(fake.NewClientBuilder().Build(), kustomization.DeepCopy())
g.Expect(err).ToNot(HaveOccurred()) g.Expect(err).ToNot(HaveOccurred())
t.Cleanup(cleanup) t.Cleanup(cleanup)
@ -793,7 +793,7 @@ func TestDecryptor_DecryptResource(t *testing.T) {
kus.Spec.Decryption = &kustomizev1.Decryption{ kus.Spec.Decryption = &kustomizev1.Decryption{
Provider: "not-supported", Provider: "not-supported",
} }
d, cleanup, err := NewTempDecryptor("", fake.NewClientBuilder().Build(), kus, nil) d, cleanup, err := New(fake.NewClientBuilder().Build(), kus)
g.Expect(err).ToNot(HaveOccurred()) g.Expect(err).ToNot(HaveOccurred())
t.Cleanup(cleanup) t.Cleanup(cleanup)

View File

@ -0,0 +1,50 @@
/*
Copyright 2025 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package decryptor
import (
"k8s.io/apimachinery/pkg/types"
"github.com/fluxcd/pkg/cache"
)
// Option is a functional option for configuring the Decryptor.
type Option func(o *Decryptor)
// WithRoot sets the root directory for the Decryptor.
func WithRoot(root string) Option {
return func(o *Decryptor) {
o.root = root
}
}
// WithTokenCache sets the token cache for the Decryptor.
func WithTokenCache(tokenCache cache.TokenCache) Option {
return func(o *Decryptor) {
o.tokenCache = &tokenCache
}
}
// WithSOPSAgeSecret sets the SOPSAgeSecret for the Decryptor.
func WithSOPSAgeSecret(name, namespace string) Option {
return func(o *Decryptor) {
o.sopsAgeSecret = &types.NamespacedName{
Name: name,
Namespace: namespace,
}
}
}

View File

@ -48,9 +48,17 @@ const (
// but is missing from the input vars. // but is missing from the input vars.
StrictPostBuildSubstitutions = "StrictPostBuildSubstitutions" StrictPostBuildSubstitutions = "StrictPostBuildSubstitutions"
// GroupChangelog controls groups kubernetes objects names on log output // GroupChangeLog controls whether to group Kubernetes objects names in log output
// reduces cardinality of logs when logging to elasticsearch // to reduce cardinality of logs.
GroupChangeLog = "GroupChangeLog" GroupChangeLog = "GroupChangeLog"
// AdditiveCELDependencyCheck controls whether the CEL dependency check
// should be additive, meaning that the built-in readiness check will
// be added to the user-defined CEL expressions.
AdditiveCELDependencyCheck = "AdditiveCELDependencyCheck"
// ExternalArtifact controls whether the ExternalArtifact source type is enabled.
ExternalArtifact = "ExternalArtifact"
) )
var features = map[string]bool{ var features = map[string]bool{
@ -69,6 +77,12 @@ var features = map[string]bool{
// GroupChangeLog // GroupChangeLog
// opt-in from v1.5 // opt-in from v1.5
GroupChangeLog: false, GroupChangeLog: false,
// AdditiveCELDependencyCheck
// opt-in from v1.7
AdditiveCELDependencyCheck: false,
// ExternalArtifact
// opt-in from v1.7
ExternalArtifact: false,
} }
func init() { func init() {

View File

@ -0,0 +1,24 @@
/*
Copyright 2025 The Flux authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package runtime
import "os"
// Namespace returns the namespace where the controller is running.
func Namespace() string {
return os.Getenv("RUNTIME_NAMESPACE")
}

View File

@ -17,11 +17,25 @@ limitations under the License.
package awskms package awskms
import ( import (
"strings" "regexp"
) )
// arnRegex matches an AWS ARN, for example:
// "arn:aws:kms:us-west-2:107501996527:key/612d5f0p-p1l3-45e6-aca6-a5b005693a48".
// The regex matches both KMS keys and aliases, and supports different AWS partition names (aws, aws-cn, aws-us-gov).
//
// Copied from SOPS:
// https://github.com/getsops/sops/blob/b2edaade23453c8774fc28ec491ddbe2b9a4c994/kms/keysource.go#L30-L32
//
// ref:
// https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html
var arnRegex = regexp.MustCompile(`^arn:aws[\w-]*:kms:(.+):[0-9]+:(key|alias)/.+$`)
// GetRegionFromKMSARN extracts the region from a KMS ARN. // GetRegionFromKMSARN extracts the region from a KMS ARN.
func GetRegionFromKMSARN(arn string) string { func GetRegionFromKMSARN(arn string) string {
arn = strings.TrimPrefix(arn, "arn:aws:kms:") m := arnRegex.FindStringSubmatch(arn)
return strings.SplitN(arn, ":", 2)[0] if m == nil {
return ""
}
return m[1]
} }

View File

@ -25,10 +25,70 @@ import (
) )
func TestGetRegionFromKMSARN(t *testing.T) { func TestGetRegionFromKMSARN(t *testing.T) {
for _, tt := range []struct {
arn string
expected string
}{
{
arn: "arn:aws:kms:us-west-2:107501996527:key/612d5f0p-p1l3-45e6-aca6-a5b005693a48",
expected: "us-west-2",
},
{
arn: "arn:aws-cn:kms:cn-north-1:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab",
expected: "cn-north-1",
},
{
arn: "arn:aws-us-gov:kms:us-gov-west-1:123456789012:key/1234abcd-12ab-34cd-56ef-1234567890ab",
expected: "us-gov-west-1",
},
{
arn: "arn:aws:kms:us-west-2:107501996527:alias/my-key-alias",
expected: "us-west-2",
},
{
arn: "arn:aws:kms:us-west-2:107501996527:key/",
expected: "",
},
{
arn: "arn:aws:kms:us-west-2:107501996527:alias/",
expected: "",
},
{
arn: "not-an-arn",
expected: "",
},
{
arn: "arn:aws:s3:::my-bucket",
expected: "",
},
{
arn: "arn:aws:ec2:us-west-2:123456789012:instance/i-1234567890abcdef0",
expected: "",
},
{
arn: "arn:aws:iam::123456789012:user/David",
expected: "",
},
{
arn: "arn:aws:lambda:us-west-2:123456789012:function:my-function",
expected: "",
},
{
arn: "arn:aws:dynamodb:us-west-2:123456789012:table/my-table",
expected: "",
},
{
arn: "arn:aws:rds:us-west-2:123456789012:db:my-database",
expected: "",
},
{
arn: "arn:aws:sns:us-west-2:123456789012:my-topic",
expected: "",
},
} {
t.Run(tt.arn, func(t *testing.T) {
g := NewWithT(t) g := NewWithT(t)
g.Expect(awskms.GetRegionFromKMSARN(tt.arn)).To(Equal(tt.expected))
arn := "arn:aws:kms:us-east-1:211125720409:key/mrk-3179bb7e88bc42ffb1a27d5038ceea25" })
}
region := awskms.GetRegionFromKMSARN(arn)
g.Expect(region).To(Equal("us-east-1"))
} }

73
main.go
View File

@ -55,6 +55,7 @@ import (
kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1" kustomizev1 "github.com/fluxcd/kustomize-controller/api/v1"
"github.com/fluxcd/kustomize-controller/internal/controller" "github.com/fluxcd/kustomize-controller/internal/controller"
"github.com/fluxcd/kustomize-controller/internal/features" "github.com/fluxcd/kustomize-controller/internal/features"
intruntime "github.com/fluxcd/kustomize-controller/internal/runtime"
// +kubebuilder:scaffold:imports // +kubebuilder:scaffold:imports
) )
@ -96,6 +97,9 @@ func main() {
noRemoteBases bool noRemoteBases bool
httpRetry int httpRetry int
defaultServiceAccount string defaultServiceAccount string
defaultDecryptionServiceAccount string
defaultKubeConfigServiceAccount string
sopsAgeSecret string
featureGates feathelper.FeatureGates featureGates feathelper.FeatureGates
disallowedFieldManagers []string disallowedFieldManagers []string
tokenCacheOptions pkgcache.TokenFlags tokenCacheOptions pkgcache.TokenFlags
@ -110,7 +114,10 @@ func main() {
flag.BoolVar(&noRemoteBases, "no-remote-bases", false, flag.BoolVar(&noRemoteBases, "no-remote-bases", false,
"Disallow remote bases usage in Kustomize overlays. When this flag is enabled, all resources must refer to local files included in the source artifact.") "Disallow remote bases usage in Kustomize overlays. When this flag is enabled, all resources must refer to local files included in the source artifact.")
flag.IntVar(&httpRetry, "http-retry", 9, "The maximum number of retries when failing to fetch artifacts over HTTP.") flag.IntVar(&httpRetry, "http-retry", 9, "The maximum number of retries when failing to fetch artifacts over HTTP.")
flag.StringVar(&defaultServiceAccount, "default-service-account", "", "Default service account used for impersonation.") flag.StringVar(&defaultServiceAccount, auth.ControllerFlagDefaultServiceAccount, "", "Default service account used for impersonation.")
flag.StringVar(&defaultDecryptionServiceAccount, auth.ControllerFlagDefaultDecryptionServiceAccount, "", "Default service account used for decryption.")
flag.StringVar(&defaultKubeConfigServiceAccount, auth.ControllerFlagDefaultKubeConfigServiceAccount, "", "Default service account used for kubeconfig.")
flag.StringVar(&sopsAgeSecret, "sops-age-secret", "", "The name of a Kubernetes secret in the RUNTIME_NAMESPACE containing a SOPS age decryption key for fallback usage.")
flag.StringArrayVar(&disallowedFieldManagers, "override-manager", []string{}, "Field manager disallowed to perform changes on managed resources.") flag.StringArrayVar(&disallowedFieldManagers, "override-manager", []string{}, "Field manager disallowed to perform changes on managed resources.")
clientOptions.BindFlags(flag.CommandLine) clientOptions.BindFlags(flag.CommandLine)
@ -143,6 +150,20 @@ func main() {
auth.EnableObjectLevelWorkloadIdentity() auth.EnableObjectLevelWorkloadIdentity()
} }
// NOTE: defaultServiceAccount is used for regular impersonation, not workload identity lockdown
if defaultDecryptionServiceAccount != "" {
auth.SetDefaultDecryptionServiceAccount(defaultDecryptionServiceAccount)
}
if defaultKubeConfigServiceAccount != "" {
auth.SetDefaultKubeConfigServiceAccount(defaultKubeConfigServiceAccount)
}
if auth.InconsistentObjectLevelConfiguration() {
setupLog.Error(auth.ErrInconsistentObjectLevelConfiguration, "invalid configuration")
os.Exit(1)
}
if err := intervalJitterOptions.SetGlobalJitter(nil); err != nil { if err := intervalJitterOptions.SetGlobalJitter(nil); err != nil {
setupLog.Error(err, "unable to set global jitter") setupLog.Error(err, "unable to set global jitter")
os.Exit(1) os.Exit(1)
@ -150,7 +171,7 @@ func main() {
watchNamespace := "" watchNamespace := ""
if !watchOptions.AllNamespaces { if !watchOptions.AllNamespaces {
watchNamespace = os.Getenv("RUNTIME_NAMESPACE") watchNamespace = intruntime.Namespace()
} }
watchSelector, err := runtimeCtrl.GetWatchSelector(watchOptions) watchSelector, err := runtimeCtrl.GetWatchSelector(watchOptions)
@ -159,6 +180,12 @@ func main() {
os.Exit(1) os.Exit(1)
} }
watchConfigsPredicate, err := runtimeCtrl.GetWatchConfigsPredicate(watchOptions)
if err != nil {
setupLog.Error(err, "unable to configure watch configs label selector for controller")
os.Exit(1)
}
var disableCacheFor []ctrlclient.Object var disableCacheFor []ctrlclient.Object
shouldCache, err := features.Enabled(features.CacheSecretsAndConfigMaps) shouldCache, err := features.Enabled(features.CacheSecretsAndConfigMaps)
if err != nil { if err != nil {
@ -255,6 +282,18 @@ func main() {
os.Exit(1) os.Exit(1)
} }
additiveCELDependencyCheck, err := features.Enabled(features.AdditiveCELDependencyCheck)
if err != nil {
setupLog.Error(err, "unable to check feature gate "+features.AdditiveCELDependencyCheck)
os.Exit(1)
}
allowExternalArtifact, err := features.Enabled(features.ExternalArtifact)
if err != nil {
setupLog.Error(err, "unable to check feature gate "+features.ExternalArtifact)
os.Exit(1)
}
var tokenCache *pkgcache.TokenCache var tokenCache *pkgcache.TokenCache
if tokenCacheOptions.MaxSize > 0 { if tokenCacheOptions.MaxSize > 0 {
var err error var err error
@ -269,27 +308,33 @@ func main() {
} }
if err = (&controller.KustomizationReconciler{ if err = (&controller.KustomizationReconciler{
AdditiveCELDependencyCheck: additiveCELDependencyCheck,
AllowExternalArtifact: allowExternalArtifact,
APIReader: mgr.GetAPIReader(),
ArtifactFetchRetries: httpRetry,
Client: mgr.GetClient(),
ClusterReader: clusterReader,
ConcurrentSSA: concurrentSSA,
ControllerName: controllerName, ControllerName: controllerName,
DefaultServiceAccount: defaultServiceAccount, DefaultServiceAccount: defaultServiceAccount,
Client: mgr.GetClient(), DependencyRequeueInterval: requeueDependency,
Mapper: restMapper, DisallowedFieldManagers: disallowedFieldManagers,
APIReader: mgr.GetAPIReader(),
Metrics: metricsH,
EventRecorder: eventRecorder, EventRecorder: eventRecorder,
FailFast: failFast,
GroupChangeLog: groupChangeLog,
KubeConfigOpts: kubeConfigOpts,
Mapper: restMapper,
Metrics: metricsH,
NoCrossNamespaceRefs: aclOptions.NoCrossNamespaceRefs, NoCrossNamespaceRefs: aclOptions.NoCrossNamespaceRefs,
NoRemoteBases: noRemoteBases, NoRemoteBases: noRemoteBases,
FailFast: failFast, SOPSAgeSecret: sopsAgeSecret,
ConcurrentSSA: concurrentSSA, StatusManager: fmt.Sprintf("gotk-%s", controllerName),
KubeConfigOpts: kubeConfigOpts,
ClusterReader: clusterReader,
DisallowedFieldManagers: disallowedFieldManagers,
StrictSubstitutions: strictSubstitutions, StrictSubstitutions: strictSubstitutions,
GroupChangeLog: groupChangeLog,
TokenCache: tokenCache, TokenCache: tokenCache,
}).SetupWithManager(ctx, mgr, controller.KustomizationReconcilerOptions{ }).SetupWithManager(ctx, mgr, controller.KustomizationReconcilerOptions{
DependencyRequeueInterval: requeueDependency,
HTTPRetry: httpRetry,
RateLimiter: runtimeCtrl.GetRateLimiter(rateLimiterOptions), RateLimiter: runtimeCtrl.GetRateLimiter(rateLimiterOptions),
WatchConfigsPredicate: watchConfigsPredicate,
WatchExternalArtifacts: allowExternalArtifact,
}); err != nil { }); err != nil {
setupLog.Error(err, "unable to create controller", "controller", controllerName) setupLog.Error(err, "unable to create controller", "controller", controllerName)
os.Exit(1) os.Exit(1)

View File

@ -1,9 +1,9 @@
FROM gcr.io/oss-fuzz-base/base-builder-go FROM gcr.io/oss-fuzz-base/base-builder-go
RUN wget https://go.dev/dl/go1.24.0.linux-amd64.tar.gz \ RUN wget https://go.dev/dl/go1.25.0.linux-amd64.tar.gz \
&& mkdir temp-go \ && mkdir temp-go \
&& rm -rf /root/.go/* \ && rm -rf /root/.go/* \
&& tar -C temp-go/ -xzf go1.24.0.linux-amd64.tar.gz \ && tar -C temp-go/ -xzf go1.25.0.linux-amd64.tar.gz \
&& mv temp-go/go/* /root/.go/ && mv temp-go/go/* /root/.go/
ENV SRC=$GOPATH/src/github.com/fluxcd/kustomize-controller ENV SRC=$GOPATH/src/github.com/fluxcd/kustomize-controller