From f511382983bf0db6fc6a55a373f3404ca368ff5c Mon Sep 17 00:00:00 2001 From: Andre Arcaina Date: Mon, 26 Oct 2026 13:41:36 -0400 Subject: [PATCH] Run go fix after v2.10.0 release Signed-off-by: Andre Arcaina --- .github/labels.yaml | 3 + .github/workflows/backport.yaml | 2 +- .github/workflows/cifuzz.yaml | 2 +- .github/workflows/e2e.yaml | 26 +- .github/workflows/release.yaml | 2 +- .github/workflows/scan.yaml | 2 +- .github/workflows/sync-labels.yaml | 2 +- .github/workflows/test.yaml | 2 +- .github/workflows/upgrade-fluxcd-pkg.yaml | 2 +- .goreleaser.yaml | 7 +- AGENTS.md | 1 - CHANGELOG.md | 121 +++ Makefile | 8 +- README.md | 1 - api/go.mod | 2 +- api/go.sum | 12 +- api/v1/bucket_types.go | 8 +- api/v1/ocirepository_types.go | 14 +- .../source.toolkit.fluxcd.io_buckets.yaml | 4 +- ...rce.toolkit.fluxcd.io_ocirepositories.yaml | 6 +- config/manager/deployment.yaml | 4 +- config/manager/kustomization.yaml | 2 +- docs/README.md | 50 +- docs/api/v1/source.md | 20 +- docs/spec/v1/buckets.md | 83 +- docs/spec/v1/gitrepositories.md | 10 +- docs/spec/v1/ocirepositories.md | 77 +- go.mod | 318 +++--- go.sum | 779 +++++++------- hack/ci/oci-zot-workload-identity-e2e.sh | 993 ++++++++++++++++++ internal/bucket/azure/blob.go | 12 +- internal/bucket/azure/blob_test.go | 111 ++ internal/bucket/gcp/gcp.go | 6 + internal/bucket/gcp/gcp_test.go | 36 +- internal/bucket/minio/minio_test.go | 37 +- internal/cache/cache.go | 10 +- internal/controller/artifact_matchers_test.go | 6 +- internal/controller/bucket_controller.go | 99 +- .../bucket_controller_fetch_test.go | 2 +- internal/controller/bucket_controller_test.go | 151 +-- .../controller/gitrepository_controller.go | 119 ++- .../gitrepository_controller_test.go | 346 ++---- .../controller/helmchart_controller_test.go | 278 ++--- .../controller/helmrepository_controller.go | 26 +- .../helmrepository_controller_test.go | 294 ++++-- .../controller/ocirepository_controller.go | 122 ++- .../ocirepository_controller_test.go | 290 ++--- .../controller/ocirepository_verify_test.go | 13 +- internal/controller/suite_test.go | 12 +- internal/error/error.go | 28 +- internal/features/features.go | 9 +- internal/helm/chart/builder_local.go | 8 +- internal/helm/chart/builder_local_test.go | 8 +- internal/helm/chart/builder_remote.go | 6 +- internal/helm/chart/builder_remote_test.go | 16 +- .../helm/chart/secureloader/ignore/rules.go | 4 +- internal/helm/getter/client_opts_test.go | 40 +- internal/helm/registry/auth_test.go | 8 +- internal/helm/repository/chart_repository.go | 17 +- .../helm/repository/chart_repository_test.go | 3 - .../repository/oci_chart_repository_test.go | 1 - internal/index/digest.go | 9 +- internal/mock/s3/server.go | 8 +- internal/oci/notation/notation_test.go | 8 +- internal/reconcile/event.go | 2 +- internal/reconcile/reconcile_test.go | 4 +- internal/reconcile/summarize/matchers_test.go | 12 +- internal/reconcile/summarize/processor.go | 2 +- .../reconcile/summarize/processor_test.go | 5 +- internal/reconcile/summarize/summary_test.go | 10 +- internal/util/temp_root.go | 113 ++ internal/util/temp_root_test.go | 126 +++ internal/util/temp_test.go | 11 +- main.go | 214 +++- tests/registry/registry.go | 2 +- 75 files changed, 3378 insertions(+), 1829 deletions(-) create mode 100755 hack/ci/oci-zot-workload-identity-e2e.sh create mode 100644 internal/util/temp_root.go create mode 100644 internal/util/temp_root_test.go diff --git a/.github/labels.yaml b/.github/labels.yaml index 36332680e..c2e5febb4 100644 --- a/.github/labels.yaml +++ b/.github/labels.yaml @@ -46,3 +46,6 @@ - name: backport:release/v1.9.x description: To be backported to release/v1.9.x color: '#ffd700' +- name: backport:release/v1.10.x + description: To be backported to release/v1.10.x + color: '#ffd700' diff --git a/.github/workflows/backport.yaml b/.github/workflows/backport.yaml index f35ff5e9a..92aac065e 100644 --- a/.github/workflows/backport.yaml +++ b/.github/workflows/backport.yaml @@ -7,6 +7,6 @@ jobs: permissions: contents: write # for reading and creating branches. pull-requests: write # for creating pull requests against release branches. - uses: fluxcd/gha-workflows/.github/workflows/backport.yaml@v0.16.0 + uses: fluxcd/gha-workflows/.github/workflows/backport.yaml@v0.17.0 secrets: github-token: ${{ secrets.BOT_GITHUB_TOKEN }} diff --git a/.github/workflows/cifuzz.yaml b/.github/workflows/cifuzz.yaml index 8e80fab19..83281156a 100644 --- a/.github/workflows/cifuzz.yaml +++ b/.github/workflows/cifuzz.yaml @@ -11,7 +11,7 @@ jobs: contents: read # for reading the repository code. steps: - name: Test suite setup - uses: fluxcd/gha-workflows/.github/actions/setup-kubernetes@v0.16.0 + uses: fluxcd/gha-workflows/.github/actions/setup-kubernetes@v0.17.0 with: go-version: 1.27.x - name: Smoke test Fuzzers diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index d1563601c..f24d4f5e8 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -13,7 +13,7 @@ jobs: contents: read # for reading the repository code. steps: - name: Test suite setup - uses: fluxcd/gha-workflows/.github/actions/setup-kubernetes@v0.16.0 + uses: fluxcd/gha-workflows/.github/actions/setup-kubernetes@v0.17.0 with: go-version: 1.27.x - name: Verify @@ -33,3 +33,27 @@ jobs: continue-on-error: true run: | kubectl -n source-system logs -l app=source-controller + zot-oci-workload-identity: + runs-on: ubuntu-latest + permissions: + contents: read # for reading the repository code. + steps: + - name: Test suite setup + uses: fluxcd/gha-workflows/.github/actions/setup-kubernetes@v0.17.0 + with: + go-version: 1.27.x + - name: Setup Flux CLI + uses: fluxcd/flux2/action@main + - name: Setup Helm + uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1 + with: + version: v4.3.1 + - name: Build container image + run: | + make docker-build IMG=test/source-controller TAG=latest \ + BUILD_PLATFORMS=linux/amd64 \ + BUILD_ARGS="--load" + - name: Run Zot OCI workload identity e2e + run: hack/ci/oci-zot-workload-identity-e2e.sh + env: + IMG: test/source-controller:latest diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 039d46774..6a359d74f 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -15,7 +15,7 @@ jobs: contents: write # for creating the GitHub release. id-token: write # for creating OIDC tokens for signing. packages: write # for pushing and signing container images. - uses: fluxcd/gha-workflows/.github/workflows/controller-release.yaml@v0.16.0 + uses: fluxcd/gha-workflows/.github/workflows/controller-release.yaml@v0.17.0 with: controller: ${{ github.event.repository.name }} release-candidate-prefix: ${{ github.event.inputs.tag }} diff --git a/.github/workflows/scan.yaml b/.github/workflows/scan.yaml index da139cf90..e4c5bddaa 100644 --- a/.github/workflows/scan.yaml +++ b/.github/workflows/scan.yaml @@ -11,7 +11,7 @@ jobs: permissions: contents: read # for reading the repository code. security-events: write # for uploading the CodeQL analysis results. - uses: fluxcd/gha-workflows/.github/workflows/code-scan.yaml@v0.16.0 + uses: fluxcd/gha-workflows/.github/workflows/code-scan.yaml@v0.17.0 secrets: github-token: ${{ secrets.GITHUB_TOKEN }} fossa-token: ${{ secrets.FOSSA_TOKEN }} diff --git a/.github/workflows/sync-labels.yaml b/.github/workflows/sync-labels.yaml index 92bfacf01..445fdbfd9 100644 --- a/.github/workflows/sync-labels.yaml +++ b/.github/workflows/sync-labels.yaml @@ -11,6 +11,6 @@ jobs: permissions: contents: read # for reading the labels file. issues: write # for creating and updating labels. - uses: fluxcd/gha-workflows/.github/workflows/labels-sync.yaml@v0.16.0 + uses: fluxcd/gha-workflows/.github/workflows/labels-sync.yaml@v0.17.0 secrets: github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 52379ed2b..f6da63fe1 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Test suite setup - uses: fluxcd/gha-workflows/.github/actions/setup-kubernetes@v0.16.0 + uses: fluxcd/gha-workflows/.github/actions/setup-kubernetes@v0.17.0 with: go-version: 1.27.x - name: Run tests diff --git a/.github/workflows/upgrade-fluxcd-pkg.yaml b/.github/workflows/upgrade-fluxcd-pkg.yaml index 08508fb0e..58b0d2366 100644 --- a/.github/workflows/upgrade-fluxcd-pkg.yaml +++ b/.github/workflows/upgrade-fluxcd-pkg.yaml @@ -5,6 +5,6 @@ on: jobs: upgrade-fluxcd-pkg: - uses: fluxcd/gha-workflows/.github/workflows/upgrade-fluxcd-pkg.yaml@v0.16.0 + uses: fluxcd/gha-workflows/.github/workflows/upgrade-fluxcd-pkg.yaml@v0.17.0 secrets: github-token: ${{ secrets.BOT_GITHUB_TOKEN }} diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 7b61ce0c1..5df799481 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -44,14 +44,11 @@ sboms: # https://goreleaser.com/customization/sign signs: - cmd: cosign - env: - - COSIGN_EXPERIMENTAL=1 - certificate: "${artifact}.pem" + signature: "${artifact}.sigstore.json" args: - sign-blob - "--yes" - - "--output-certificate=${certificate}" - - "--output-signature=${signature}" + - "--bundle=${signature}" - "${artifact}" artifacts: checksum output: true diff --git a/AGENTS.md b/AGENTS.md index 211ac7010..8abbd4c22 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -135,7 +135,6 @@ Bump `fluxcd/pkg/*` modules as a set — version skew breaks `go.sum`. Run `make - `make verify` is the CI gate — a dirty diff means you forgot to run codegen or tidy. - The `replace` directives in `go.mod` (semver and go-digest) exist for correctness. Leave them alone. - `Storage` serializes writes per object via `fluxcd/pkg/lockedfile` and expects `--storage-path` to be a real local directory. Never write artifacts outside `Storage.BasePath` — the file server exposes that path verbatim at `--storage-addr`. -- Workload Identity is feature-gated by `auth.FeatureGateObjectLevelWorkloadIdentity` (from `fluxcd/pkg/auth`). Token caching is opt-in via `--token-cache-max-size`. - `CacheSecretsAndConfigMaps` (in `internal/features`) is off by default; `Secret`/`ConfigMap` lookups bypass the cache and hit the API server directly. Mind that before adding new secret reads to a hot path. - The controller watches a label-selected subset of its CRs — see `Cache.ByObject` in `mustSetupManager`. Adding a new kind requires updating both `main.go` and the scheme. - `pkg/azure`, `pkg/gcp`, `pkg/minio` are importable by external consumers. Treat their exported surface as semi-public API. diff --git a/CHANGELOG.md b/CHANGELOG.md index 3843579c1..f54cb26c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,127 @@ All notable changes to this project are documented in this file. +## 1.10.1 + +**Release date:** 2026-10-26 + +This patch release hardens the controller's artifact server by setting a read +header timeout, which also bounds the TLS handshake, and an idle timeout. As a +result, connections that never send a request, or keep-alive connections from +clients without an idle timeout, no longer stay open until the server shuts +down. No timeout is set for reading or writing the whole request, so large +artifacts can still be downloaded by slow clients. + +Improvements: +- Update fluxcd/pkg dependencies + [#2217](https://github.com/fluxcd/source-controller/pull/2217) + +## 1.10.0 + +**Release date:** 2026-10-24 + +This minor release graduates object-level workload identity, now renamed to +object identity, introduces SPIFFE mTLS between the controllers, and adds +GitHub App Client ID support for Git repositories. + +### Object Identity + +Object-level workload identity is now generally available and renamed to object +identity. Workload identity refers exclusively to identities assigned to Flux +controllers, while object identity refers to identities assigned to Flux +objects. The `ObjectLevelWorkloadIdentity` feature gate is retired and the +feature is always enabled. + +⚠️ **Breaking change:** the default Flux RBAC no longer grants the `create` +verb on the `serviceaccounts/token` resource. If your installation relies on +the default RBAC, bind a ClusterRole with this permission to the controller +ServiceAccount before upgrading. See +[fluxcd/flux2#6139](https://github.com/fluxcd/flux2/pull/6139) for the required +ClusterRole and ClusterRoleBinding, and the +[object identity documentation](https://fluxcd.io/flux/integrations/object-identity/) +for the API reference. + +Two credential providers can now supply the credential: `kubernetes`, which +uses the Kubernetes TokenRequest API and was already available, and `spiffe`, +which fetches a JWT-SVID from the SPIFFE Broker API. The provider is configured +on the ServiceAccount with `credential.auth.fluxcd.io/provider`, independently +of the service provider in `.spec.provider`. + +### Inter-Controller mTLS + +This release introduces SPIFFE mTLS for inter-controller communication. Flux +controllers authenticate each other with X.509-SVIDs, and each traffic type is +enabled independently by configuring the SPIFFE Workload API socket and the +SPIFFE IDs that the controller authorizes. See the +[inter-controller mTLS documentation](https://fluxcd.io/flux/installation/configuration/intercontroller-mtls/) +for the controller flags and an example configuration. + +### GitRepository + +GitHub App authentication accepts a Client ID as an alternative to the numeric +App ID (`githubAppClientID`). Signature verification is re-run when the trusted +keys rotate, so a revoked or replaced key no longer leaves a stale +`SourceVerified=True` condition. + +### OCIRepository + +Generic OCI repositories now support object identity. The credential is sent as +a bearer token, or as the password in HTTP basic authentication when +`credential.auth.fluxcd.io/username` is set. Audiences default to the registry +host and can be overridden with `credential.auth.fluxcd.io/audiences`. + +Signature verification is re-run when the verification keys rotate, and the +origin revision from OCI image metadata is forwarded in events. + +### ExternalArtifact + +A new `.status.exportedInputs` field exposes structured data extracted from the +source resource, for consumers such as ResourceSet to implement templating. + +### General updates + +The controller is built with Kubernetes 1.37 and Go 1.27. + +Fixes: +- Close GCP object streams on error paths + [#2053](https://github.com/fluxcd/source-controller/pull/2053) +- Close Azure blob streams on error paths + [#2200](https://github.com/fluxcd/source-controller/pull/2200) +- Remove leaked Helm index cache on reconcile errors + [#2201](https://github.com/fluxcd/source-controller/pull/2201) + [#2202](https://github.com/fluxcd/source-controller/pull/2202) +- Purge stale temp dirs at startup + [#2176](https://github.com/fluxcd/source-controller/pull/2176) +- Include the repository URL in Helm index parse errors + [#2063](https://github.com/fluxcd/source-controller/pull/2063) +- Support the AWS `ap-southeast-6` region for Bucket sources + [#2183](https://github.com/fluxcd/source-controller/pull/2183) + +Improvements: +- Support Kubernetes ServiceAccount tokens for `generic` OCI repositories + [#2192](https://github.com/fluxcd/source-controller/pull/2192) +- Add the `spiffe` credential provider and SPIFFE Broker API + [#2197](https://github.com/fluxcd/source-controller/pull/2197) +- Retire the `ObjectLevelWorkloadIdentity` feature gate + [#2197](https://github.com/fluxcd/source-controller/pull/2197) +- Add SPIFFE mTLS between the controllers + [#2194](https://github.com/fluxcd/source-controller/pull/2194) +- Add GitHub App Client ID support + [#2168](https://github.com/fluxcd/source-controller/pull/2168) +- Reverify sources when verification keys rotate + [#2166](https://github.com/fluxcd/source-controller/pull/2166) + [#2179](https://github.com/fluxcd/source-controller/pull/2179) +- Expose the OCI origin revision in events + [#2127](https://github.com/fluxcd/source-controller/pull/2127) +- Add ExternalArtifact exported inputs + [#2182](https://github.com/fluxcd/source-controller/pull/2182) +- Update to Kubernetes 1.37 and Go 1.27 + [#2162](https://github.com/fluxcd/source-controller/pull/2162) + [#2186](https://github.com/fluxcd/source-controller/pull/2186) +- Various dependency updates + [#2100](https://github.com/fluxcd/source-controller/pull/2100) + [#2203](https://github.com/fluxcd/source-controller/pull/2203) + ## 1.9.6 **Release date:** 2026-10-01 diff --git a/Makefile b/Makefile index eff4be3f3..5feba911e 100644 --- a/Makefile +++ b/Makefile @@ -58,7 +58,7 @@ manager: generate fmt vet go build $(GO_STATIC_FLAGS) -o $(BUILD_DIR)/bin/manager main.go KUBEBUILDER_ASSETS?="$(shell $(ENVTEST) --arch=$(ENVTEST_ARCH) use -i $(ENVTEST_KUBERNETES_VERSION) --bin-dir=$(ENVTEST_ASSETS_DIR) -p path)" -test: install-envtest test-api ## Run all tests +test: fix install-envtest test-api ## Run all tests HTTPS_PROXY="" HTTP_PROXY="" \ KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) \ GIT_CONFIG_GLOBAL=/dev/null \ @@ -115,6 +115,10 @@ fmt: ## Run go fmt against code go fmt ./... cd api; go fmt ./... +fix: ## Run go fix against code + go fix ./... + cd api ; go fix ./... + vet: ## Run go vet against code go vet ./... cd api; go vet ./... @@ -204,7 +208,7 @@ fuzz-smoketest: fuzz-build bash -c "/runner.sh" # Run fuzz tests for the duration set in FUZZ_TIME. -fuzz-native: +fuzz-native: KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) \ FUZZ_TIME=$(FUZZ_TIME) \ ./tests/fuzz/native_go_run.sh diff --git a/README.md b/README.md index 5d6bccb90..7f84471e7 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/4786/badge)](https://bestpractices.coreinfrastructure.org/projects/4786) [![e2e](https://github.com/fluxcd/source-controller/workflows/e2e/badge.svg)](https://github.com/fluxcd/source-controller/actions) -[![report](https://goreportcard.com/badge/github.com/fluxcd/source-controller)](https://goreportcard.com/report/github.com/fluxcd/source-controller) [![license](https://img.shields.io/github/license/fluxcd/source-controller.svg)](https://github.com/fluxcd/source-controller/blob/main/LICENSE) [![release](https://img.shields.io/github/release/fluxcd/source-controller/all.svg)](https://github.com/fluxcd/source-controller/releases) diff --git a/api/go.mod b/api/go.mod index 9dd52303e..5b5f12c8e 100644 --- a/api/go.mod +++ b/api/go.mod @@ -4,7 +4,7 @@ go 1.27.0 require ( github.com/fluxcd/pkg/apis/acl v0.12.0 - github.com/fluxcd/pkg/apis/meta v1.35.0 + github.com/fluxcd/pkg/apis/meta v1.38.0 k8s.io/apiextensions-apiserver v0.37.1 k8s.io/apimachinery v0.37.1 ) diff --git a/api/go.sum b/api/go.sum index b19ce7f9e..27bbc7f83 100644 --- a/api/go.sum +++ b/api/go.sum @@ -4,8 +4,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fluxcd/pkg/apis/acl v0.12.0 h1:UWpZDqKxpKDW38wdXIMHV8Uv3POPu2vUrra0JH1frH0= github.com/fluxcd/pkg/apis/acl v0.12.0/go.mod h1:PJrGIu5Bw/6/siO95uhS+pi5oKSU+gVJqHqsPHb8Kok= -github.com/fluxcd/pkg/apis/meta v1.35.0 h1:/k5d7uEFrxkz/hf6JTrdsKBr3UQWVHW2J4G6VSSae/k= -github.com/fluxcd/pkg/apis/meta v1.35.0/go.mod h1:1MLvK2Xu3UdTKBzg+Q1Oj7H9PHeiKsUB1UW3uwxAwiM= +github.com/fluxcd/pkg/apis/meta v1.38.0 h1:7t6/DuGFAPx2LE4MsRh1jskjnVE6qELKfr/LrfG7KE8= +github.com/fluxcd/pkg/apis/meta v1.38.0/go.mod h1:lmdugeiZyt6w+DRm8ypkB+nwrzm+saMbqSfZKGBbTOE= github.com/fxamacker/cbor/v2 v2.9.2 h1:X4Ksno9+x3cz0TZv69ec1hxP/+tymuR8PXQJyDwfh78= github.com/fxamacker/cbor/v2 v2.9.2/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8= @@ -21,8 +21,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ 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/gomega v1.43.0 h1:VlG/1FxqNxhSO+lq/OHBNaaqwiBK/mO8JbVkX9Y+FeU= -github.com/onsi/gomega v1.43.0/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg= +github.com/onsi/gomega v1.44.0 h1:eAiGl3Pw5jz5GQdDff0BcxYpAX1JxW8xD7mFUuwNfZQ= +github.com/onsi/gomega v1.44.0/go.mod h1:e/C2HwaZ1DhvjzXXuFhcR7hY7Sh9pl7MmoWKEjzwcdA= 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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -36,8 +36,8 @@ github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= +go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= golang.org/x/net v0.61.0 h1:RR+6j/BTBCrggOLugxa+66R8FvoN6MHlzDlZIbN9YFw= golang.org/x/net v0.61.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI= golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc= diff --git a/api/v1/bucket_types.go b/api/v1/bucket_types.go index 398a453f9..cfe8e849f 100644 --- a/api/v1/bucket_types.go +++ b/api/v1/bucket_types.go @@ -34,10 +34,10 @@ const ( BucketProviderGeneric string = "generic" // BucketProviderAmazon for an AWS S3 object storage Bucket. // Provides support for retrieving credentials from the AWS EC2 service - // and workload identity authentication. + // and for object identity. BucketProviderAmazon string = "aws" // BucketProviderGoogle for a Google Cloud Storage Bucket. - // Provides support for authentication using a workload identity. + // Provides support for authentication using object identity. BucketProviderGoogle string = "gcp" // BucketProviderAzure for an Azure Blob Storage Bucket. // Provides support for authentication using a Service Principal, @@ -98,8 +98,8 @@ type BucketSpec struct { // ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate // the bucket. This field is only supported for the 'gcp' and 'aws' providers. - // For more information about workload identity: - // https://fluxcd.io/flux/components/source/buckets/#workload-identity + // For more information about object identity: + // https://fluxcd.io/flux/integrations/object-identity/ // +optional ServiceAccountName string `json:"serviceAccountName,omitempty"` diff --git a/api/v1/ocirepository_types.go b/api/v1/ocirepository_types.go index 05d099fb9..10ec5e44e 100644 --- a/api/v1/ocirepository_types.go +++ b/api/v1/ocirepository_types.go @@ -90,7 +90,11 @@ type OCIRepositorySpec struct { Verify *OCIRepositoryVerification `json:"verify,omitempty"` // ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate - // the image pull if the service account has attached pull secrets. For more information: + // the image pull, either with the pull secrets attached to the service account or with + // an object identity. + // For more information about object identity: + // https://fluxcd.io/flux/integrations/object-identity/ + // For more information about image pull secrets: // https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account // +optional ServiceAccountName string `json:"serviceAccountName,omitempty"` @@ -253,6 +257,14 @@ func (in *OCIRepository) GetArtifact() *meta.Artifact { return in.Status.Artifact } +// GetProvider returns the OCI authentication provider, defaulting to generic. +func (in *OCIRepository) GetProvider() string { + if in.Spec.Provider == "" { + return GenericOCIProvider + } + return in.Spec.Provider +} + // GetLayerMediaType returns the media type layer selector if found in spec. func (in *OCIRepository) GetLayerMediaType() string { if in.Spec.LayerSelector == nil { diff --git a/config/crd/bases/source.toolkit.fluxcd.io_buckets.yaml b/config/crd/bases/source.toolkit.fluxcd.io_buckets.yaml index 8b85220f9..032a877ba 100644 --- a/config/crd/bases/source.toolkit.fluxcd.io_buckets.yaml +++ b/config/crd/bases/source.toolkit.fluxcd.io_buckets.yaml @@ -151,8 +151,8 @@ spec: description: |- ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate the bucket. This field is only supported for the 'gcp' and 'aws' providers. - For more information about workload identity: - https://fluxcd.io/flux/components/source/buckets/#workload-identity + For more information about object identity: + https://fluxcd.io/flux/integrations/object-identity/ type: string sts: description: |- diff --git a/config/crd/bases/source.toolkit.fluxcd.io_ocirepositories.yaml b/config/crd/bases/source.toolkit.fluxcd.io_ocirepositories.yaml index b152ec384..5254d3732 100644 --- a/config/crd/bases/source.toolkit.fluxcd.io_ocirepositories.yaml +++ b/config/crd/bases/source.toolkit.fluxcd.io_ocirepositories.yaml @@ -178,7 +178,11 @@ spec: serviceAccountName: description: |- ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate - the image pull if the service account has attached pull secrets. For more information: + the image pull, either with the pull secrets attached to the service account or with + an object identity. + For more information about object identity: + https://fluxcd.io/flux/integrations/object-identity/ + For more information about image pull secrets: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account type: string suspend: diff --git a/config/manager/deployment.yaml b/config/manager/deployment.yaml index e354b00e3..24495671c 100644 --- a/config/manager/deployment.yaml +++ b/config/manager/deployment.yaml @@ -66,8 +66,8 @@ spec: path: /healthz readinessProbe: httpGet: - port: http - path: / + path: /readyz + port: healthz resources: limits: cpu: 1000m diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index c204df0eb..661bf294a 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -6,4 +6,4 @@ resources: images: - name: fluxcd/source-controller newName: fluxcd/source-controller - newTag: v1.9.0 + newTag: v1.10.0 diff --git a/docs/README.md b/docs/README.md index eb02d5ee5..a7eb8d330 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,6 +6,8 @@ |---------------------------------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `--artifact-retention-records` | int | The maximum number of artifacts to be kept in storage after a garbage collection. (default 2) | | `--artifact-retention-ttl` | duration | The duration of time that artifacts from previous reconciliations will be kept in storage before being garbage collected. (default 1m0s) | +| `--authorized-artifact-client-spiffe-id` | strings | SPIFFE ID authorized to fetch artifacts from the artifact server. Can be repeated. When set, the artifact server uses SPIFFE mTLS. | +| `--authorized-event-server-spiffe-id` | strings | SPIFFE ID of an event server authorized to receive events from source-controller. Can be repeated. When set, the event recorder uses SPIFFE mTLS. | | `--concurrent` | int | The number of concurrent reconciles per controller. (default 2) | | `--default-service-account` | string | Default service account to use for workload identity when not specified in resources. | | `--enable-leader-election` | boolean | Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager. | @@ -34,15 +36,61 @@ | `--storage-adv-addr` | string | The advertised address of the static file server. | | `--storage-path` | string | The local storage path. | | `--token-cache-max-size` | int | The maximum amount of entries in the LRU cache used for tokens. (default 100, enabled) | +| `--spiffe-broker-endpoint` | string | The gRPC URL of the SPIFFE Broker endpoint. | +| `--spiffe-broker-id` | strings | SPIFFE ID of the SPIFFE Broker endpoint. Can be repeated. | | `--token-cache-max-duration` | duration | The maximum duration for which a token would be considered unexpired. This is capped at 1h. (default 1h) | | `--watch-all-namespaces` | boolean | Watch for custom resources in all namespaces, if set to false it will only watch the runtime namespace. (default true) | | `--watch-label-selector` | string | Watch for resources with matching labels e.g. 'sharding.fluxcd.io/key=shard1'. | | `--feature-gates` | mapStringBool | A comma separated list of key=value pairs defining the state of experimental features. | +### SPIFFE mTLS + +When at least one of the repeatable flags `--authorized-artifact-client-spiffe-id` +or `--authorized-event-server-spiffe-id` is set, source-controller acquires an +X.509-SVID from the SPIFFE Workload API. The `SPIFFE_ENDPOINT_SOCKET` environment +variable must point to the Workload API socket. + +- `--authorized-artifact-client-spiffe-id` authorizes the given SPIFFE IDs to + fetch artifacts from the artifact server. When set, the artifact server uses + mTLS. +- `--authorized-event-server-spiffe-id` authorizes the given SPIFFE IDs of + event servers to which source-controller sends events. When set, the event + recorder uses mTLS. + +Each traffic type is protected independently. SPIFFE mTLS is transparent to the +Flux API: the scheme of `.status.artifact.url` does not change. + +Each flag value is interpreted as follows: + +- `spiffe:///` authorizes exactly that SPIFFE ID. +- `spiffe:////` (with a trailing slash) authorizes any + SPIFFE ID with that path prefix. +- `spiffe://` (without a path) authorizes any SPIFFE ID in that + trust domain. + +Whenever `` equals the special value `self`, source-controller +understands it as the trust domain of the controller's own X509-SVID. + +Invalid values cause source-controller to fail at startup. + +### SPIFFE Broker API + +When `--spiffe-broker-endpoint` and at least one `--spiffe-broker-id` are set, +source-controller acquires an X.509-SVID from the SPIFFE Workload API and uses it to +authenticate to the SPIFFE Broker API. The `SPIFFE_ENDPOINT_SOCKET` environment +variable must point to the Workload API socket. + +The Broker is used to acquire JWT-SVIDs for object identity when a +ServiceAccount sets `credential.auth.fluxcd.io/provider` to `spiffe`. The +Broker endpoint is a TCP gRPC target, e.g. +`dns:///spire-server.spire.svc.cluster.local:8443`. + +Each `--spiffe-broker-id` value is interpreted like the SPIFFE IDs used by the +mTLS flags. Invalid values cause source-controller to fail at startup. + ### Feature Gates | Name | Default Value | Description | |-------------------------------|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `CacheSecretsAndConfigMaps` | `false` | Configures the caching of Secrets and ConfigMaps by the controller-runtime client. When enabled, it will cache both object types, resulting in increased memory usage and cluster-wide RBAC permissions (list and watch). | -| `ObjectLevelWorkloadIdentity` | `false` | Enables the use of object-level workload identity for the controller. | | `OptimizedGitClones` | `true` | Optimises Git resource usage by only cloning repositories when the HEAD commit changed since last reconciliation. | diff --git a/docs/api/v1/source.md b/docs/api/v1/source.md index 3fe0791b0..0c75239fd 100644 --- a/docs/api/v1/source.md +++ b/docs/api/v1/source.md @@ -191,8 +191,8 @@ string (Optional)

ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate the bucket. This field is only supported for the ‘gcp’ and ‘aws’ providers. -For more information about workload identity: -https://fluxcd.io/flux/components/source/buckets/#workload-identity

+For more information about object identity: +https://fluxcd.io/flux/integrations/object-identity/

@@ -1198,7 +1198,11 @@ string (Optional)

ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate -the image pull if the service account has attached pull secrets. For more information: +the image pull, either with the pull secrets attached to the service account or with +an object identity. +For more information about object identity: +https://fluxcd.io/flux/integrations/object-identity/ +For more information about image pull secrets: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account

@@ -1555,8 +1559,8 @@ string (Optional)

ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate the bucket. This field is only supported for the ‘gcp’ and ‘aws’ providers. -For more information about workload identity: -https://fluxcd.io/flux/components/source/buckets/#workload-identity

+For more information about object identity: +https://fluxcd.io/flux/integrations/object-identity/

@@ -3471,7 +3475,11 @@ string (Optional)

ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate -the image pull if the service account has attached pull secrets. For more information: +the image pull, either with the pull secrets attached to the service account or with +an object identity. +For more information about object identity: +https://fluxcd.io/flux/integrations/object-identity/ +For more information about image pull secrets: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account

diff --git a/docs/spec/v1/buckets.md b/docs/spec/v1/buckets.md index eb02f5114..8caadbe58 100644 --- a/docs/spec/v1/buckets.md +++ b/docs/spec/v1/buckets.md @@ -275,14 +275,14 @@ data: secretkey: ``` -##### AWS Controller-Level Workload Identity example +##### AWS Workload Identity example ```yaml --- apiVersion: source.toolkit.fluxcd.io/v1 kind: Bucket metadata: - name: aws-controller-level-workload-identity + name: aws-workload-identity namespace: default spec: interval: 5m0s @@ -293,18 +293,14 @@ spec: timeout: 30s ``` -##### AWS Object-Level Workload Identity example - -**Note:** To use Object-Level Workload Identity (`.spec.serviceAccountName` with -cloud providers), the controller feature gate `ObjectLevelWorkloadIdentity` must -be enabled. +##### AWS Object Identity example ```yaml --- apiVersion: source.toolkit.fluxcd.io/v1 kind: Bucket metadata: - name: aws-object-level-workload-identity + name: aws-object-identity namespace: default spec: interval: 5m0s @@ -312,13 +308,13 @@ spec: bucketName: podinfo endpoint: s3.amazonaws.com region: us-east-1 - serviceAccountName: aws-workload-identity-sa + serviceAccountName: aws-object-identity-sa timeout: 30s --- apiVersion: v1 kind: ServiceAccount metadata: - name: aws-workload-identity-sa + name: aws-object-identity-sa namespace: default annotations: eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/flux-bucket-role @@ -339,8 +335,7 @@ with: with the `AZURE_CLIENT_ID` - Managed Identity with a system-assigned identity -is attempted by default. If no chain can be established, the bucket -is assumed to be publicly reachable. +is attempted by default. When a reference is specified, it expects a Secret with one of the following sets of `.data` fields: @@ -359,23 +354,6 @@ For any Managed Identity and/or Microsoft Entra ID (Formerly Azure Active Direct the base URL can be configured using `.data.authorityHost`. If not supplied, [`AzurePublicCloud` is assumed](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#AuthorityHost). -##### Azure example - -```yaml ---- -apiVersion: source.toolkit.fluxcd.io/v1 -kind: Bucket -metadata: - name: azure-public - namespace: default -spec: - interval: 5m0s - provider: azure - bucketName: podinfo - endpoint: https://podinfoaccount.blob.core.windows.net - timeout: 30s -``` - ##### Azure Service Principal Secret example ```yaml @@ -571,31 +549,27 @@ spec: endpoint: https://testfluxwi.blob.core.windows.net ``` -##### Azure Object-Level Workload Identity example - -**Note:** To use Object-Level Workload Identity (`.spec.serviceAccountName` with -cloud providers), the controller feature gate `ObjectLevelWorkloadIdentity` must -be enabled. +##### Azure Object Identity example ```yaml --- apiVersion: source.toolkit.fluxcd.io/v1 kind: Bucket metadata: - name: azure-object-level-workload-identity + name: azure-object-identity namespace: default spec: interval: 5m0s provider: azure bucketName: testwi endpoint: https://testfluxwi.blob.core.windows.net - serviceAccountName: azure-workload-identity-sa + serviceAccountName: azure-object-identity-sa timeout: 30s --- apiVersion: v1 kind: ServiceAccount metadata: - name: azure-workload-identity-sa + name: azure-object-identity-sa namespace: default annotations: azure.workload.identity/client-id: @@ -656,14 +630,14 @@ Refer to the [Azure documentation](https://learn.microsoft.com/en-us/rest/api/st For detailed setup instructions, see: https://fluxcd.io/flux/integrations/gcp/#for-google-cloud-storage -##### GCP Controller-Level Workload Identity example +##### GCP Workload Identity example ```yaml --- apiVersion: source.toolkit.fluxcd.io/v1 kind: Bucket metadata: - name: gcp-controller-level-workload-identity + name: gcp-workload-identity namespace: default spec: interval: 5m0s @@ -674,18 +648,14 @@ spec: timeout: 30s ``` -##### GCP Object-Level Workload Identity example - -**Note:** To use Object-Level Workload Identity (`.spec.serviceAccountName` with -cloud providers), the controller feature gate `ObjectLevelWorkloadIdentity` must -be enabled. +##### GCP Object Identity example ```yaml --- apiVersion: source.toolkit.fluxcd.io/v1 kind: Bucket metadata: - name: gcp-object-level-workload-identity + name: gcp-object-identity namespace: default spec: interval: 5m0s @@ -693,13 +663,13 @@ spec: bucketName: podinfo endpoint: storage.googleapis.com region: us-east-1 - serviceAccountName: gcp-workload-identity-sa + serviceAccountName: gcp-object-identity-sa timeout: 30s --- apiVersion: v1 kind: ServiceAccount metadata: - name: gcp-workload-identity-sa + name: gcp-object-identity-sa namespace: default annotations: iam.gke.io/gcp-service-account: @@ -753,9 +723,9 @@ Where the (base64 decoded) value of `.data.serviceaccount` looks like this: Only service account keys are accepted, i.e. the `type` field must be set to `service_account`. Other Google credential configurations, such as `external_account` (workload identity federation), are rejected. To authenticate -without a static key, use Workload Identity as shown in the -[GCP Controller-Level](#gcp-controller-level-workload-identity-example) and -[GCP Object-Level](#gcp-object-level-workload-identity-example) examples. +without a static key, use workload identity or object identity as shown in the +[GCP Workload Identity](#gcp-workload-identity-example) and +[GCP Object Identity](#gcp-object-identity-example) examples. ### Interval @@ -1000,12 +970,15 @@ the `.spec.provider` field: - When `.spec.provider` is set to `generic`, the controller will fetch the image pull secrets attached to the Service Account and use them for authentication. - When `.spec.provider` is set to `aws`, `azure`, or `gcp`, the Service Account - will be used for Workload Identity authentication. In this case, the controller - feature gate `ObjectLevelWorkloadIdentity` must be enabled, otherwise the - controller will error out. + will be used to acquire an object identity. + +For a complete guide to the ServiceAccount annotations used for object +identity, including the `spiffe` credential provider, see the +[Object Identity](/flux/integrations/object-identity/) docs. -**Note:** that for a publicly accessible object storage, you don't need to -provide a `secretRef` nor `serviceAccountName`. +**Note:** when `.spec.provider` is set to `generic` and the object storage +allows anonymous access, you don't need to provide a `secretRef` nor a +`serviceAccountName`. The cloud providers all require authentication. **Important:** `.spec.secretRef` and `.spec.serviceAccountName` are mutually exclusive and cannot be set at the same time. This constraint is enforced diff --git a/docs/spec/v1/gitrepositories.md b/docs/spec/v1/gitrepositories.md index a700c460a..a20ed9a20 100644 --- a/docs/spec/v1/gitrepositories.md +++ b/docs/spec/v1/gitrepositories.md @@ -308,7 +308,7 @@ For a full list of available regions and their endpoints, see the spec: interval: 5m0s provider: aws - serviceAccountName: my-tenant # optional (used for object-level workload identity) + serviceAccountName: my-tenant # optional (used for object identity) url: https://git-codecommit..amazonaws.com/v1/repos/ ref: branch: main @@ -465,11 +465,13 @@ in the same namespace as GitRepository with purpose depending on the value of the `.spec.provider` field: - When `.spec.provider` is set to `azure`, the Service Account - will be used for Workload Identity authentication. In this case, the controller - feature gate `ObjectLevelWorkloadIdentity` must be enabled, otherwise the - controller will error out. For Azure DevOps specific setup, see the + will be used to acquire an object identity. For Azure DevOps specific setup, see the [Azure DevOps integration guide](https://fluxcd.io/flux/integrations/azure/#for-azure-devops). +For a complete guide to the ServiceAccount annotations used for object +identity, including the `spiffe` credential provider, see the +[Object Identity](/flux/integrations/object-identity/) docs. + **Note:** that for a publicly accessible git repository, you don't need to provide a `secretRef` nor `serviceAccountName`. diff --git a/docs/spec/v1/ocirepositories.md b/docs/spec/v1/ocirepositories.md index bb742be39..67fa8c584 100644 --- a/docs/spec/v1/ocirepositories.md +++ b/docs/spec/v1/ocirepositories.md @@ -116,9 +116,11 @@ Supported options are: - `azure` - `gcp` -The `generic` provider can be used for public repositories or when +The `generic` provider can be used for public repositories, when static credentials are used for authentication, either with -`spec.secretRef` or `spec.serviceAccountName`. +`spec.secretRef` or `spec.serviceAccountName`, or when a Kubernetes +ServiceAccount token is used for object identity, as described +in the [Service Account reference](#service-account-reference) section. If you do not specify `.spec.provider`, it defaults to `generic`. For a complete guide on how to set up authentication for cloud providers, @@ -274,16 +276,77 @@ kubectl create secret docker-registry ... in the same namespace as OCIRepository with purpose depending on the value of the `.spec.provider` field: -- When `.spec.provider` is set to `generic`, the controller will fetch the image - pull secrets attached to the Service Account and use them for authentication. +- When `.spec.provider` is set to `generic`, the controller will use the image + pull secrets attached to the Service Account for static authentication, or + use the Service Account token for object identity when the + Service Account opts in with the credential annotations described below. - When `.spec.provider` is set to `aws`, `azure`, or `gcp`, the Service Account - will be used for Workload Identity authentication. In this case, the controller - feature gate `ObjectLevelWorkloadIdentity` must be enabled, otherwise the - controller will error out. + will be used to acquire an object identity. + +For a complete guide to the ServiceAccount annotations used for object +identity, including the `spiffe` credential provider, see the +[Object Identity](/flux/integrations/object-identity/) docs. **Note:** that for a publicly accessible image repository, you don't need to provide a `secretRef` nor `serviceAccountName`. +#### Generic provider ServiceAccount tokens + +For the `generic` provider, the controller can authenticate to a registry +using a short-lived Kubernetes ServiceAccount token. The token is presented +to the registry as a bearer token by default, or as the password in basic +authentication when `credential.auth.fluxcd.io/username` is set. + +The ServiceAccount must opt in with the following required annotations: + +- `credential.auth.fluxcd.io/provider`: must be set to `kubernetes`. +- `credential.auth.fluxcd.io/type`: must be set to `jwt`. + +The following annotations are optional: + +- `credential.auth.fluxcd.io/audiences`: a comma or newline separated list of + token audiences. Defaults to the registry host derived from `.spec.url`. +- `credential.auth.fluxcd.io/username`: the username to use when the registry + expects basic authentication. When unset, the token is sent as a bearer token. +- `credential.auth.fluxcd.io/expiration`: the requested token expiration as a + Go duration, e.g. `10m`. + +A ServiceAccount that does not set both required annotations does not opt in +and continues to use its `imagePullSecrets`, or anonymous access if it has none. +Setting only one of the two required annotations is a configuration error. The +credential annotations cannot be combined with `imagePullSecrets`. + +Example: + +```yaml +--- +apiVersion: source.toolkit.fluxcd.io/v1 +kind: OCIRepository +metadata: + name: app + namespace: apps +spec: + interval: 5m + url: oci://registry.example.com/app + provider: generic + serviceAccountName: registry-puller + ref: + tag: latest +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: registry-puller + namespace: apps + annotations: + credential.auth.fluxcd.io/provider: kubernetes + credential.auth.fluxcd.io/type: jwt + # Optional. Defaults to the registry host from .spec.url. + credential.auth.fluxcd.io/audiences: registry.example.com + # Optional. Use when the registry expects basic authentication. + credential.auth.fluxcd.io/username: flux +``` + For a complete guide on how to set up authentication for cloud providers, see the integration [docs](/flux/integrations/). diff --git a/go.mod b/go.mod index 630ece7bb..7551d2918 100644 --- a/go.mod +++ b/go.mod @@ -9,85 +9,92 @@ replace github.com/fluxcd/source-controller/api => ./api replace github.com/opencontainers/go-digest => github.com/opencontainers/go-digest v1.0.1-0.20220411205349-bde1400a84be require ( - cloud.google.com/go/auth v0.23.2 - cloud.google.com/go/compute/metadata v0.9.0 - cloud.google.com/go/storage v1.62.3 + cloud.google.com/go/auth v0.24.1 + cloud.google.com/go/compute/metadata v0.10.0 + cloud.google.com/go/storage v1.70.0 github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.1 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.1 - github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.7.0 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.3 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.15.0 + github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.8.2 github.com/Masterminds/semver/v3 v3.5.0 github.com/cyphar/filepath-securejoin v0.7.0 github.com/distribution/distribution/v3 v3.1.2 - github.com/docker/cli v29.7.2+incompatible + github.com/docker/cli v29.9.2+incompatible github.com/docker/go-units v0.5.0 - github.com/elazarl/goproxy v1.8.4 + github.com/elazarl/goproxy v1.9.2 github.com/fluxcd/cli-utils v1.4.0 + github.com/fluxcd/pkg/apis/crypto v0.5.0 github.com/fluxcd/pkg/apis/event v0.32.0 - github.com/fluxcd/pkg/apis/meta v1.35.0 - github.com/fluxcd/pkg/artifact v0.24.0 - github.com/fluxcd/pkg/auth v0.62.0 - github.com/fluxcd/pkg/cache v0.17.0 - github.com/fluxcd/pkg/git v0.55.0 - github.com/fluxcd/pkg/gittestserver v0.32.0 - github.com/fluxcd/pkg/helmtestserver v0.44.0 + github.com/fluxcd/pkg/apis/meta v1.38.0 + github.com/fluxcd/pkg/artifact v0.28.0 + github.com/fluxcd/pkg/auth v0.66.0 + github.com/fluxcd/pkg/cache v0.19.0 + github.com/fluxcd/pkg/git v0.56.0 + github.com/fluxcd/pkg/gittestserver v0.33.0 + github.com/fluxcd/pkg/helmtestserver v0.45.0 github.com/fluxcd/pkg/http/transport v0.9.0 github.com/fluxcd/pkg/masktoken v0.10.0 - github.com/fluxcd/pkg/oci v0.72.0 - github.com/fluxcd/pkg/runtime v0.119.0 - github.com/fluxcd/pkg/sourceignore v0.21.0 - github.com/fluxcd/pkg/ssh v0.28.0 - github.com/fluxcd/pkg/tar v1.3.0 + github.com/fluxcd/pkg/oci v0.73.0 + github.com/fluxcd/pkg/runtime v0.122.0 + github.com/fluxcd/pkg/sourceignore v0.22.0 + github.com/fluxcd/pkg/spiffe v0.2.0 + github.com/fluxcd/pkg/ssh v0.29.0 + github.com/fluxcd/pkg/tar v1.4.0 github.com/fluxcd/pkg/testserver v0.15.0 - github.com/fluxcd/pkg/version v0.19.0 - github.com/fluxcd/source-controller/api v1.9.0 + github.com/fluxcd/pkg/version v0.20.0 + github.com/fluxcd/source-controller/api v1.10.0 github.com/go-git/go-billy/v5 v5.9.2 github.com/go-git/go-git/v5 v5.19.3 github.com/go-logr/logr v1.4.4 github.com/google/go-containerregistry v0.22.1 - github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20260602205845-4b7672c7705a + github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20261013193525-92aac6186bc4 github.com/google/uuid v1.6.0 - github.com/miekg/dns v1.1.72 - github.com/minio/minio-go/v7 v7.2.1 + github.com/miekg/dns v1.1.73 + github.com/minio/minio-go/v7 v7.4.1 github.com/notaryproject/notation-core-go v1.3.0 github.com/notaryproject/notation-go v1.3.2 - github.com/onsi/gomega v1.43.0 + github.com/onsi/gomega v1.44.0 github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.1 github.com/otiai10/copy v1.14.1 github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 - github.com/prometheus/client_golang v1.24.1 - github.com/sigstore/cosign/v3 v3.0.6 - github.com/sigstore/rekor v1.5.2 - github.com/sigstore/sigstore v1.10.8 - github.com/sigstore/sigstore-go v1.2.1 + github.com/prometheus/client_golang v1.25.0 + github.com/sigstore/cosign/v3 v3.1.4 + github.com/sigstore/rekor v1.5.5 + github.com/sigstore/sigstore v1.11.1 + github.com/sigstore/sigstore-go v1.3.0 github.com/sirupsen/logrus v1.10.2 github.com/spf13/pflag v1.0.10 + github.com/spiffe/go-spiffe/v2 v2.9.0 golang.org/x/crypto v0.58.0 golang.org/x/sync v0.24.0 - google.golang.org/api v0.297.0 - helm.sh/helm/v4 v4.3.0 + google.golang.org/api v0.308.0 + helm.sh/helm/v4 v4.3.1 k8s.io/api v0.37.1 k8s.io/apimachinery v0.37.1 k8s.io/client-go v0.37.1 k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 - oras.land/oras-go/v2 v2.6.2 + oras.land/oras-go/v2 v2.6.4 sigs.k8s.io/controller-runtime v0.25.2 sigs.k8s.io/yaml v1.6.0 ) require ( + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260709200747-435963d16310.1 // indirect cel.dev/expr v0.25.2 // indirect cloud.google.com/go v0.123.0 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect - cloud.google.com/go/iam v1.12.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.3.0 // indirect + cloud.google.com/go/iam v1.14.0 // indirect cloud.google.com/go/monitoring v1.30.0 // indirect + connectrpc.com/connect v1.20.0 // indirect dario.cat/mergo v1.0.2 // indirect - github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/provider v0.14.0 // indirect + github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/provider v0.18.1 // indirect + github.com/AliyunContainerService/ack-ram-tool/pkg/ecsmetadata v0.0.9 // indirect github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // 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.12.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.13.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/storage/internal v0.1.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect github.com/Azure/go-autorest/autorest v0.11.30 // indirect @@ -98,54 +105,53 @@ require ( github.com/Azure/go-autorest/logger v0.2.2 // indirect github.com/Azure/go-autorest/tracing v0.6.1 // indirect github.com/Azure/go-ntlmssp v0.1.1 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.8.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.10.1 // indirect github.com/BurntSushi/toml v1.6.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.35.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.57.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/sprig/v3 v3.3.0 // indirect github.com/Masterminds/squirrel v1.5.4 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/ProtonMail/go-crypto v1.4.1 // indirect - github.com/ThalesIgnite/crypto11 v1.2.5 // indirect + github.com/ProtonMail/go-crypto v1.5.2 // indirect github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect github.com/alibabacloud-go/cr-20160607 v1.0.1 // indirect github.com/alibabacloud-go/cr-20181201 v1.0.10 // indirect github.com/alibabacloud-go/darabonba-openapi v0.2.1 // indirect - github.com/alibabacloud-go/debug v1.0.0 // indirect + github.com/alibabacloud-go/debug v1.0.1 // indirect github.com/alibabacloud-go/endpoint-util v1.1.1 // indirect github.com/alibabacloud-go/openapi-util v0.1.0 // indirect - github.com/alibabacloud-go/tea v1.2.1 // indirect + github.com/alibabacloud-go/tea v1.3.12 // indirect github.com/alibabacloud-go/tea-utils v1.4.5 // indirect github.com/alibabacloud-go/tea-xml v1.1.3 // indirect - github.com/aliyun/credentials-go v1.3.2 // indirect + github.com/aliyun/credentials-go v1.4.7 // indirect + github.com/apache/arrow-go/v18 v18.7.0 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/aws/aws-sdk-go-v2 v1.46.0 // indirect - github.com/aws/aws-sdk-go-v2/config v1.33.3 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.20.3 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.19.2 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.2 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.2 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.2 // indirect - github.com/aws/aws-sdk-go-v2/service/ecr v1.64.0 // indirect - github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.46.0 // indirect - github.com/aws/aws-sdk-go-v2/service/eks v1.98.0 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.19 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.2 // indirect - github.com/aws/aws-sdk-go-v2/service/signin v1.9.0 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.37.0 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.42.0 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.49.0 // indirect - github.com/aws/smithy-go v1.28.1 // indirect + github.com/aws/aws-sdk-go-v2 v1.47.4 // indirect + github.com/aws/aws-sdk-go-v2/config v1.33.10 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.20.10 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.4 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.7 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.7 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.7 // indirect + github.com/aws/aws-sdk-go-v2/service/ecr v1.66.6 // indirect + github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.47.6 // indirect + github.com/aws/aws-sdk-go-v2/service/eks v1.104.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.22 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.7 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.10.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.38.5 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.43.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.51.5 // indirect + github.com/aws/smithy-go v1.28.6 // indirect github.com/aws/smithy-go/aws-http-auth v1.2.1 // indirect github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/blang/semver v3.5.1+incompatible // indirect github.com/blang/semver/v4 v4.0.0 // indirect - github.com/buildkite/agent/v3 v3.118.0 // indirect - github.com/buildkite/go-pipeline v0.16.0 // indirect + github.com/buildkite/agent/v3 v3.130.0 // indirect + github.com/buildkite/go-pipeline v0.17.1 // indirect github.com/buildkite/interpolate v0.1.5 // indirect github.com/buildkite/roko v1.4.0 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect @@ -159,10 +165,8 @@ require ( github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect github.com/coreos/go-oidc/v3 v3.21.0 // indirect github.com/coreos/go-systemd/v22 v22.7.0 // indirect - github.com/creack/pty v1.1.24 // indirect github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect github.com/digitorus/timestamp v0.0.0-20231217203849-220c5c2851b7 // indirect github.com/dimchansky/utfbom v1.1.1 // indirect @@ -172,6 +176,7 @@ require ( github.com/docker/go-metrics v0.1.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a // indirect + github.com/eclipse-keypont/crypto11 v1.6.8 // indirect github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/envoyproxy/go-control-plane/envoy v1.37.0 // indirect @@ -187,62 +192,66 @@ require ( github.com/fluxcd/pkg/lockedfile v0.9.0 // indirect github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/fxamacker/cbor/v2 v2.9.4 // indirect - github.com/gaganhr94/docker-credential-acr v1.0.2 // indirect + github.com/gaganhr94/docker-credential-acr v1.0.3 // indirect github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect - github.com/go-chi/chi/v5 v5.3.0 // indirect + github.com/go-chi/chi/v5 v5.5.0 // 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-gorp/gorp/v3 v3.1.0 // indirect - github.com/go-jose/go-jose/v4 v4.1.5 // indirect + github.com/go-jose/go-jose/v4 v4.1.6 // indirect github.com/go-ldap/ldap/v3 v3.4.10 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect - github.com/go-openapi/analysis v0.25.5 // indirect - github.com/go-openapi/errors v0.22.8 // indirect - github.com/go-openapi/jsonpointer v1.0.0 // indirect - github.com/go-openapi/jsonreference v1.0.1 // indirect - github.com/go-openapi/loads v0.25.0 // indirect - github.com/go-openapi/runtime v0.33.0 // indirect - github.com/go-openapi/runtime/server-middleware v0.30.0 // indirect - github.com/go-openapi/spec v0.22.9 // indirect - github.com/go-openapi/strfmt v0.27.0 // indirect - github.com/go-openapi/swag v0.29.1 // indirect - github.com/go-openapi/swag/cmdutils v0.29.1 // indirect - github.com/go-openapi/swag/conv v0.29.1 // indirect - github.com/go-openapi/swag/fileutils v0.29.1 // indirect - github.com/go-openapi/swag/jsonutils v0.29.1 // indirect - github.com/go-openapi/swag/loading v0.29.1 // indirect - github.com/go-openapi/swag/mangling v0.29.1 // indirect - github.com/go-openapi/swag/netutils v0.29.1 // indirect - github.com/go-openapi/swag/pools v0.29.1 // indirect - github.com/go-openapi/swag/stringutils v0.29.1 // indirect - github.com/go-openapi/swag/typeutils v0.29.1 // indirect - github.com/go-openapi/swag/yamlutils v0.29.1 // indirect - github.com/go-openapi/validate v0.26.1 // indirect + github.com/go-openapi/analysis v1.0.0 // indirect + github.com/go-openapi/errors v0.22.9 // indirect + github.com/go-openapi/jsonpointer v1.0.1 // indirect + github.com/go-openapi/jsonreference v1.0.2 // indirect + github.com/go-openapi/loads v0.25.3 // indirect + github.com/go-openapi/runtime v0.33.3 // indirect + github.com/go-openapi/runtime/server-middleware v0.33.3 // indirect + github.com/go-openapi/spec v1.0.1 // indirect + github.com/go-openapi/strfmt v0.27.3 // indirect + github.com/go-openapi/swag v0.29.2 // indirect + github.com/go-openapi/swag/cmdutils v0.29.2 // indirect + github.com/go-openapi/swag/conv v0.29.2 // indirect + github.com/go-openapi/swag/fileutils v0.29.2 // indirect + github.com/go-openapi/swag/jsonutils v0.29.2 // indirect + github.com/go-openapi/swag/loading v0.29.2 // indirect + github.com/go-openapi/swag/mangling v0.29.2 // indirect + github.com/go-openapi/swag/netutils v0.29.2 // indirect + github.com/go-openapi/swag/pools v0.29.2 // indirect + github.com/go-openapi/swag/stringutils v0.29.2 // indirect + github.com/go-openapi/swag/typeutils v0.29.2 // indirect + github.com/go-openapi/swag/yamlutils v0.29.2 // indirect + github.com/go-openapi/validate v1.0.0 // indirect github.com/go-piv/piv-go/v2 v2.6.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect + github.com/gobuffalo/flect v1.0.3 // indirect github.com/gobwas/glob v0.2.3 // indirect - github.com/gofrs/flock v0.13.0 // indirect + github.com/goccy/go-json v0.10.6 // indirect + github.com/godbus/dbus/v5 v5.2.2 // indirect + github.com/gofrs/flock v0.13.1 // indirect github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.1 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect - github.com/golang/snappy v0.0.4 // indirect + github.com/golang/snappy v1.0.0 // indirect github.com/google/btree v1.1.3 // indirect github.com/google/certificate-transparency-go v1.3.3 // indirect + github.com/google/flatbuffers v25.12.19+incompatible // indirect github.com/google/gnostic-models v0.7.1 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20250225234217-098045d5e61f // indirect - github.com/google/go-github/v73 v73.0.0 // indirect github.com/google/go-github/v87 v87.0.0 // indirect + github.com/google/go-github/v88 v88.0.0 // indirect github.com/google/go-querystring v1.2.0 // indirect - github.com/google/s2a-go v0.1.9 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.20 // indirect - github.com/googleapis/gax-go/v2 v2.24.0 // indirect + github.com/google/s2a-go v0.1.11 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.23 // indirect + github.com/googleapis/gax-go/v2 v2.26.2 // indirect github.com/gorilla/handlers v1.5.2 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gosuri/uitable v0.0.4 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.31.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.8 // indirect github.com/hashicorp/golang-lru/arc/v2 v2.0.5 // indirect @@ -258,18 +267,17 @@ require ( github.com/jmoiron/sqlx v1.4.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kevinburke/ssh_config v1.6.0 // indirect - github.com/klauspost/compress v1.19.2 // indirect - github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/klauspost/compress v1.20.1 // indirect + github.com/klauspost/cpuid/v2 v2.4.0 // indirect github.com/klauspost/crc32 v1.3.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect - github.com/letsencrypt/boulder v0.20260309.0 // indirect github.com/lib/pq v1.12.3 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.23 // indirect + github.com/mattn/go-colorable v0.1.15 // indirect + github.com/mattn/go-isatty v0.0.22 // indirect + github.com/mattn/go-runewidth v0.0.24 // indirect github.com/miekg/pkcs11 v1.1.2 // indirect github.com/minio/crc64nvme v1.1.1 // indirect github.com/minio/md5-simd v1.1.2 // indirect @@ -281,19 +289,22 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // 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/mozillazg/docker-credential-acr-helper v0.4.0 // indirect + github.com/mozillazg/docker-credential-acr-helper v0.4.1 // indirect + github.com/mozillazg/docker-credential-acr-helper/pkg/acr v0.2.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/notaryproject/notation-plugin-framework-go v1.0.0 // indirect github.com/notaryproject/tspclient-go v1.0.0 // indirect github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect - github.com/oklog/ulid/v2 v2.1.1 // indirect + github.com/oklog/ulid/v2 v2.1.2 // indirect github.com/oleiade/reflections v1.1.0 // indirect - github.com/opencontainers/go-digest/blake3 v0.0.0-20250813155314-89707e38ad1a // indirect + github.com/opencontainers/go-digest/blake3 v0.0.0-20261015205711-63db54280247 // indirect github.com/otiai10/mint v1.6.3 // indirect github.com/pborman/uuid v1.2.1 // indirect - github.com/pelletier/go-toml/v2 v2.3.1 // indirect + github.com/pelletier/go-toml/v2 v2.4.3 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/philhofer/fwd v1.2.0 // indirect + github.com/phuslu/lru v1.0.18 // indirect + github.com/pierrec/lz4/v4 v4.1.28 // indirect github.com/pjbgf/sha1cd v0.7.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect @@ -305,37 +316,34 @@ require ( github.com/prometheus/procfs v0.22.0 // indirect github.com/redis/go-redis/extra/rediscmd/v9 v9.5.3 // indirect github.com/redis/go-redis/extra/redisotel/v9 v9.5.3 // indirect - github.com/redis/go-redis/v9 v9.18.0 // indirect + github.com/redis/go-redis/v9 v9.22.0 // indirect github.com/rs/xid v1.6.0 // indirect github.com/rubenv/sql-migrate v1.8.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 // indirect - github.com/sassoftware/relic v7.2.1+incompatible // indirect - github.com/secure-systems-lab/go-securesystemslib v0.11.0 // indirect + github.com/sassoftware/relic/v8 v8.2.0 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.11.1 // indirect github.com/sergi/go-diff v1.4.0 // indirect github.com/shibumi/go-pathspec v1.3.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect - github.com/sigstore/fulcio v1.8.6 // indirect - github.com/sigstore/protobuf-specs v0.5.1 // indirect - github.com/sigstore/rekor-tiles/v2 v2.2.2-0.20260601073857-5d098a2b6443 // indirect - github.com/sigstore/timestamp-authority/v2 v2.1.2 // indirect + github.com/sigstore/protobuf-specs v0.5.2 // indirect + github.com/sigstore/rekor-tiles/v2 v2.3.0 // indirect + github.com/sigstore/timestamp-authority/v2 v2.1.5 // indirect github.com/skeema/knownhosts v1.3.2 // indirect github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect github.com/spf13/cobra v1.10.2 // indirect github.com/spf13/viper v1.21.0 // indirect - github.com/spiffe/go-spiffe/v2 v2.7.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect github.com/tetratelabs/wabin v0.0.0-20230304001439-f6f874872834 // indirect github.com/tetratelabs/wazero v1.12.0 // indirect github.com/thales-e-security/pool v0.0.2 // indirect github.com/theupdateframework/go-tuf v0.7.0 // indirect - github.com/theupdateframework/go-tuf/v2 v2.4.2-0.20260407074541-7e8f69f906ef // indirect - github.com/tinylib/msgp v1.6.1 // indirect - github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect + github.com/theupdateframework/go-tuf/v2 v2.4.2 // indirect + github.com/tinylib/msgp v1.6.4 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect github.com/transparency-dev/formats v0.1.1 // indirect github.com/transparency-dev/merkle v0.0.2 // indirect @@ -346,54 +354,54 @@ require ( github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect github.com/zeebo/blake3 v0.2.3 // indirect github.com/zeebo/xxh3 v1.1.0 // indirect - gitlab.com/gitlab-org/api/client-go v1.46.0 // indirect + gitlab.com/gitlab-org/api/client-go/v3 v3.13.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/bridges/prometheus v0.70.0 // indirect - go.opentelemetry.io/contrib/detectors/gcp v1.44.0 // indirect - go.opentelemetry.io/contrib/exporters/autoexport v0.70.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.70.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0 // indirect - go.opentelemetry.io/otel v1.45.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.21.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.21.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.45.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.45.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.45.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0 // indirect - go.opentelemetry.io/otel/exporters/prometheus v0.67.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.21.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.45.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.45.0 // indirect - go.opentelemetry.io/otel/log v0.21.0 // indirect - go.opentelemetry.io/otel/metric v1.45.0 // indirect - go.opentelemetry.io/otel/sdk v1.45.0 // indirect - go.opentelemetry.io/otel/sdk/log v0.21.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.45.0 // indirect - go.opentelemetry.io/otel/trace v1.45.0 // indirect - go.opentelemetry.io/proto/otlp v1.11.0 // indirect + go.opentelemetry.io/contrib/bridges/prometheus v0.72.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.45.0 // indirect + go.opentelemetry.io/contrib/exporters/autoexport v0.72.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.71.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.72.0 // indirect + go.opentelemetry.io/otel v1.47.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.23.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.23.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.47.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.47.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.47.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.47.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.47.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.69.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.23.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.47.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.47.0 // indirect + go.opentelemetry.io/otel/log v1.47.0 // indirect + go.opentelemetry.io/otel/metric v1.47.0 // indirect + go.opentelemetry.io/otel/sdk v1.47.0 // indirect + go.opentelemetry.io/otel/sdk/log v1.47.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.47.0 // indirect + go.opentelemetry.io/otel/trace v1.47.0 // indirect + go.opentelemetry.io/proto/otlp v1.11.1 // indirect go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.5 // indirect - golang.org/x/mod v0.41.0 // indirect + golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba // indirect + golang.org/x/mod v0.42.0 // indirect golang.org/x/net v0.61.0 // indirect golang.org/x/oauth2 v0.37.0 // indirect golang.org/x/sys v0.49.0 // indirect golang.org/x/term v0.47.0 // indirect golang.org/x/text v0.43.0 // indirect - golang.org/x/time v0.15.0 // indirect - golang.org/x/tools v0.51.0 // indirect + golang.org/x/time v0.16.0 // indirect gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect google.golang.org/genproto v0.0.0-20260715232425-e75dac1f907d // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688 // indirect - google.golang.org/grpc v1.83.2 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260928230214-8a89bd6388cc // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20261019180522-ce908d29299b // indirect + google.golang.org/grpc v1.84.0 // indirect google.golang.org/protobuf v1.36.12 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.67.2 // indirect + gopkg.in/ini.v1 v1.67.3 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.37.1 // indirect @@ -407,7 +415,7 @@ require ( sigs.k8s.io/kustomize/api v0.21.2 // indirect sigs.k8s.io/kustomize/kyaml v0.21.2 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/release-utils v0.12.4 // indirect + sigs.k8s.io/release-utils v0.12.5 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.4.2 // indirect ) diff --git a/go.sum b/go.sum index d4cceb29c..c54d75d7d 100644 --- a/go.sum +++ b/go.sum @@ -1,30 +1,32 @@ -al.essio.dev/pkg/shellescape v1.6.0 h1:NxFcEqzFSEVCGN2yq7Huv/9hyCEGVa/TncnOOBBeXHA= -al.essio.dev/pkg/shellescape v1.6.0/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260709200747-435963d16310.1 h1:fXh8CsdNpjRr8R5vFdqtIxPt/Lno2IIJlYOdZBIZn0w= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.11-20260709200747-435963d16310.1/go.mod h1:tvtbpgaVXZX4g6Pn+AnzFycuRK3MOz5HJfEGeEllXYM= cel.dev/expr v0.25.2 h1:K6j46C81hXtZQfuX60cVWQFBJahKSE2gfRbNuvr5bFs= cel.dev/expr v0.25.2/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= -cloud.google.com/go/auth v0.23.2 h1:pxSCpfiji41hpzpPdMCftEUCezpgpqmmDdYiAjCKXxo= -cloud.google.com/go/auth v0.23.2/go.mod h1:4DhBRcqvtljQN3dJ57qtqbib5ZGCYE5f2crfiiC2EM0= -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/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= -cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= -cloud.google.com/go/iam v1.12.0 h1:Aki3bX9aHUDKPHfnRJfDcTdVedvy6quGBQcTqx3DRXk= -cloud.google.com/go/iam v1.12.0/go.mod h1:FEZ4lXpADAC2AIpQY7LANNjjwyQ2jK439CI2VaD+sLY= -cloud.google.com/go/kms v1.32.0 h1:s+rEluaaZKhLVjrIWG7uNBsnWbiitElzNzFGyp6+nIg= -cloud.google.com/go/kms v1.32.0/go.mod h1:CSGvW6GnMQbY+1nOHcIzhMtHSbExXlOmCKjWtYVjcpA= +cloud.google.com/go/auth v0.24.1 h1:NTmd2iJb4DsGIav0KiqFMYI88nMshA2wnFMBatRmoCs= +cloud.google.com/go/auth v0.24.1/go.mod h1:1dmBXa9xfBvBr39ZnDUDkaaA91ccq9CcqENDgJA4d7c= +cloud.google.com/go/auth/oauth2adapt v0.3.0 h1:FY8oSZpCYoUNv6QxVODuMjQz4IlSOVeiQtZ08vLPz88= +cloud.google.com/go/auth/oauth2adapt v0.3.0/go.mod h1:7+2uCm7++XFO+/lN06c2HXpDXb/NMNn2/UwyBPbTnkk= +cloud.google.com/go/compute/metadata v0.10.0 h1:pyKMUQSwchgkIBBJGdILqQbs/BNJXqwSA7Ej6LAvvtY= +cloud.google.com/go/compute/metadata v0.10.0/go.mod h1:rGFHRrIif570kSibjFTMbt6/4/tzgJWFGI/HVol4GIk= +cloud.google.com/go/iam v1.14.0 h1:YRIB1/4xxwJ+NfI6RQr1WaOoHHeV0MizJEj940nBXJA= +cloud.google.com/go/iam v1.14.0/go.mod h1:JTPZfbHdGXJrOYREtkRoFzCDGaBdbA7ILLKO/DDuav0= +cloud.google.com/go/kms v1.33.0 h1:pG0X78m212b2pv9N4fdMoUO69LuZGQ9kSvn8sHBOFAo= +cloud.google.com/go/kms v1.33.0/go.mod h1:CSGvW6GnMQbY+1nOHcIzhMtHSbExXlOmCKjWtYVjcpA= cloud.google.com/go/logging v1.19.0 h1:NCqhdVUg3wQ8Cobdf16FDSuTGi3+6+hdSBHrY5TsR6Q= cloud.google.com/go/logging v1.19.0/go.mod h1:i40NZCHC9Gqvod4yE+yQfDWwlgwW/SrshkkGibCHxcA= cloud.google.com/go/longrunning v1.2.0 h1:WjYH3YHBGCxGJP9M4dWGHBfXr/cFIjMkNgWcJj7/iMM= cloud.google.com/go/longrunning v1.2.0/go.mod h1:5KMQALFGOCtFoi2xSOA1u3H7WKlhmckgiyFw7+LGQp0= cloud.google.com/go/monitoring v1.30.0 h1:r/d+JUbyKmJ8b07iznuKfzVzrIXTWxHQ3lBRm3x2LlY= cloud.google.com/go/monitoring v1.30.0/go.mod h1:htlUR0QWVMrjFzZmN4LGnMAve9xB/eduwjmINxVZ8RM= -cloud.google.com/go/storage v1.62.3 h1:SZq1t23NCI+e96dH77Dg3PEfsNNEjqO8zE5AnD8gVD0= -cloud.google.com/go/storage v1.62.3/go.mod h1:cpYz/kRVZ+UQAF1uHeea10/9ewcRbxGoGNKsS9daSXA= +cloud.google.com/go/storage v1.70.0 h1:LQNHn0rRF94jVFfF3e416IzdQqIufa5GfF6HilQxb80= +cloud.google.com/go/storage v1.70.0/go.mod h1:W84cNa070TZIegvTW67CBY+f5rxux8EJMb7YyiHdrls= cloud.google.com/go/trace v1.16.0 h1:GmQovzFc5F0CNfl0VLgL64aoTtu7xsM0YajW2GlG9+E= cloud.google.com/go/trace v1.16.0/go.mod h1:r+bdAn16dKLSV1G2D5v3e58IlQlizfxWrUfjx7kM7X0= +connectrpc.com/connect v1.20.0 h1:6TNDAB+WeNd2uolWNlYczB5E0KNNaVMNUEx8JEUsPmQ= +connectrpc.com/connect v1.20.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= @@ -36,20 +38,23 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8af github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjqpY4C7H15HjRPEenkS4SAn3Jy2eRRjkjZbGR30TOg= github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d/go.mod h1:XNqJ7hv2kY++g8XEHREpi+JqZo3+0l+CH2egBVN4yqM= -github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/provider v0.14.0 h1:kcnfY4vljxXliXDBrA9K9lwF8IoEZ4Up6Eg9kWTIm28= -github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/provider v0.14.0/go.mod h1:tlqp9mUGbsP+0z3Q+c0Q5MgSdq/OMwQhm5bffR3Q3ss= +github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/provider v0.18.1 h1:YWDC5K6uH/hUB9iBqNMzcbYw/mpySDyRiMv4auyB7xY= +github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/provider v0.18.1/go.mod h1:eoheXiZu1MD1WqNrjd6QCzH9eNc564OBZhwmPpIHWfw= +github.com/AliyunContainerService/ack-ram-tool/pkg/ecsmetadata v0.0.7/go.mod h1:QM3VKYNyD5thMEWqKef+uOfpNmZG7RjG7wOsCdavj9w= +github.com/AliyunContainerService/ack-ram-tool/pkg/ecsmetadata v0.0.9 h1:LW8G8Zf9R6+F0ZpV1FLimv2rVaBRRX+mR1b6N6vIpJc= +github.com/AliyunContainerService/ack-ram-tool/pkg/ecsmetadata v0.0.9/go.mod h1:QM3VKYNyD5thMEWqKef+uOfpNmZG7RjG7wOsCdavj9w= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.1 h1:zvXfGJCWvywnCA814d8ZiVyt+fm9nnTE8xSb99zRyfo= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.1/go.mod h1:iptorS+VYKFL2N6PnebpS91dubG35eAOEERnT4PJbQU= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.1 h1:u93s+zU2JD62im61Bm5CZIc1ZrOJaIAWEg0WOrMVkEo= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.1/go.mod h1:oXtinPO4OLj9d1DOTrqrL1oRwGhcqadvAmrl6wTeGlk= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.3 h1:qgnWTiWs1G7AqcKZegFvkJD1bmXJcaDU3uC0ELSgZ5U= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.3/go.mod h1:XtxD48QPInqoj+GMlcRlXHEYsoOxFnP6w2JgIZ9rDS4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.15.0 h1:w3gDUJlJ0BfBsHH02DCBDwbkmMSXJqBj6nBOCo9iocY= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.15.0/go.mod h1:7scl8mG/IFLVjJ9ef/ltdND2T1z0CkgVSaQoUxLvmeU= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.4.0 h1:xFaZZ+IubdftrDHnGGwZ6QvQ3KHTtWl2MCK+GMt2vxs= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.4.0/go.mod h1:mCBhUhlMjLLJKr5aqw2TNS/VqJOie8MzWq3DAMJeKso= 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/containers/azcontainerregistry v0.2.3/go.mod h1:MAm7bk0oDLmD8yIkvfbxPW04fxzphPyL+7GzwHxOp6Y= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 h1:fhqpLE3UEXi9lPaBRpQ6XuRW0nU7hgg4zlmZZa+a9q4= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0/go.mod h1:7dCRMLwisfRH3dBupKeNCioWYUZ4SS09Z14H+7i8ZoY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.13.0 h1:Terj1CMW4+NY5CpWjSRkqLoxh2DkYDdv/FH2z0897Uc= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.13.0/go.mod h1:7Ut2Qw9vcwpLW5Q4MBy43uSxXS7tK2yjs94OJ6j5thE= 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/resourcemanager/storage/armstorage v1.8.1 h1:/Zt+cDPnpC3OVDm/JKLOs7M2DKmLRIIp3XIx9pHHiig= @@ -58,8 +63,10 @@ github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.5.0 h1:MaKvxE6 github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.5.0/go.mod h1:i2h9fsTFKZorh8RdV2IcSUf/Qj98GlTkrTvUbX/s8as= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 h1:nCYfgcSyHZXJI8J0IWE5MsCGlb2xp9fJiXyxWgmOFg4= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0/go.mod h1:ucUjca2JtSZboY8IoUqyQyuuXvwbMBVwFOm0vdQPNhA= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.7.0 h1:BM85pSYlVYQHdq00nxyPoOkyLF5NArJG3bOsrmbwr4k= -github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.7.0/go.mod h1:QYjP2cB7ZYtS/8jAbE0VSBZde/tjExqGjp+8JY6/+ts= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.8.2 h1:tQJdgQjbKKffEiMOKJmcQPkY01eFHRqWIZVefqP44nI= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.8.2/go.mod h1:kTkpJchXAdO7LGkKZP97a+Ou8TgaF4NT6MHwOIy5uSQ= +github.com/Azure/azure-sdk-for-go/sdk/storage/internal v0.1.0 h1:X/bTwe0qpvmvPPutc+LWo7T4cKMqDP9JHP4k+Co9ZB4= +github.com/Azure/azure-sdk-for-go/sdk/storage/internal v0.1.0/go.mod h1:+kX7ahyFelQ42F3RScM2CFgHJljp0phc/FieKLYVpFI= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= @@ -93,21 +100,21 @@ github.com/Azure/go-ntlmssp v0.1.1 h1:l+FM/EEMb0U9QZE7mKNEDw5Mu3mFiaa2GKOoTSsNDP github.com/Azure/go-ntlmssp v0.1.1/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= -github.com/AzureAD/microsoft-authentication-library-for-go v1.8.0 h1:Nljr4q1GRA/5vCrMONS+g4u4LRHNgOXVSh3O43J2CnI= -github.com/AzureAD/microsoft-authentication-library-for-go v1.8.0/go.mod h1:Y33QHnf0FfdVewFFISOGe20mkZbxX4H839o955/PoeI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.10.1 h1:0O6j18nQDoIff6/mtosT5LNU2WehEwp+LqfX6699oqY= +github.com/AzureAD/microsoft-authentication-library-for-go v1.10.1/go.mod h1:xdYAf5bjkOpsd3auA8riiJW4vneBubt9caavL7626jQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0 h1:l7+6kwRMJNwdCvYdDl7Eax+wzEYHSnNY7zrrfbhDdTA= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0/go.mod h1:pJTkW8hEUIIi3Pf65lPZOnn4Y81yCllX6IWk2jNXdkM= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 h1:UnDZ/zFfG1JhH/DqxIZYU/1CUAlTUScoXD/LcM2Ykk8= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0/go.mod h1:IA1C1U7jO/ENqm/vhi7V9YYpBsp+IMyqNrEN94N7tVc= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0 h1:7t/qx5Ost0s0wbA/VDrByOooURhp+ikYwv20i9Y07TQ= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0/go.mod h1:vB2GH9GAYYJTO3mEn8oYwzEdhlayZIdQz6zdzgUIRvA= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 h1:0s6TxfCu2KHkkZPnBfsQ2y5qia0jl3MMrmBhu3nCOYk= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.35.0 h1:bN1gA3of5bXtbnLsRPrwfmbbe7A5UWFlcTHseujLnpc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.35.0/go.mod h1:Yj5vHEz/aAepZGliRJsA6uvHAVAQyEwajq9ORCHPxzM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.57.0 h1:jLdiS1vO+XJFyDSWRHBx56r4s/NNtcl5J6KyCcWUX/w= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.57.0/go.mod h1:8lmpHY+1VRoteiOwyrQMDt1YGXOrFKCz+1wJW7n3ODY= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.57.0 h1:cSjUzZ7KU8hicTgzaSv9NmSyM9fTVK3y5lsBUl3wOis= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.57.0/go.mod h1:dzcEjy1WJ0Q4u9twNR3LcLhNoYMRCrMCMafpxa0TjPQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0 h1:RoO5+d7uCmDqovLrHCr2/BuViUXvdcrNxyNM1pN9dDQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0/go.mod h1:YqwkQPrWSC7+byyc1VlKbWLBF5JsW5IoL6xUkemYSXk= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -121,10 +128,8 @@ github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA4 github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM= -github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo= -github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY+9ef8E= -github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= +github.com/ProtonMail/go-crypto v1.5.2 h1:cucYnvqcY7UOXVD//mSyjeaPY0SSN3v5cDkYPxumINk= +github.com/ProtonMail/go-crypto v1.5.2/go.mod h1:/RaSu30DaKO4RY+XdV/ACcCcZkGr7AhUIduq5sjzzCo= github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.2/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= @@ -140,8 +145,9 @@ github.com/alibabacloud-go/darabonba-openapi v0.2.1 h1:WyzxxKvhdVDlwpAMOHgAiCJ+N github.com/alibabacloud-go/darabonba-openapi v0.2.1/go.mod h1:zXOqLbpIqq543oioL9IuuZYOQgHQ5B8/n5OPrnko8aY= github.com/alibabacloud-go/darabonba-string v1.0.0/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA= github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY= -github.com/alibabacloud-go/debug v1.0.0 h1:3eIEQWfay1fB24PQIEzXAswlVJtdQok8f3EVN5VrBnA= github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= +github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg= +github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc= github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= github.com/alibabacloud-go/endpoint-util v1.1.1 h1:ZkBv2/jnghxtU0p+upSU0GGzW1VL9GQdZO3mcSUTUy8= github.com/alibabacloud-go/endpoint-util v1.1.1/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= @@ -156,86 +162,93 @@ github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeG github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= github.com/alibabacloud-go/tea v1.1.19/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= -github.com/alibabacloud-go/tea v1.2.1 h1:rFF1LnrAdhaiPmKwH5xwYOKlMh66CqRwPUTzIK74ask= -github.com/alibabacloud-go/tea v1.2.1/go.mod h1:qbzof29bM/IFhLMtJPrgTGK3eauV5J2wSyEUo4OEmnA= +github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk= +github.com/alibabacloud-go/tea v1.3.12 h1:ir2Io80UlBy1JHf7t+uCTxmaGQtiEta1WpV29NGJTkE= +github.com/alibabacloud-go/tea v1.3.12/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg= github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= github.com/alibabacloud-go/tea-utils v1.3.9/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= github.com/alibabacloud-go/tea-utils v1.4.3/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw= github.com/alibabacloud-go/tea-utils v1.4.5 h1:h0/6Xd2f3bPE4XHTvkpjwxowIwRCJAJOqY6Eq8f3zfA= github.com/alibabacloud-go/tea-utils v1.4.5/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw= +github.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I= github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0= github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI= github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM= github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= -github.com/aliyun/credentials-go v1.3.2 h1:L4WppI9rctC8PdlMgyTkF8bBsy9pyKQEzBD1bHMRl+g= -github.com/aliyun/credentials-go v1.3.2/go.mod h1:tlpz4uys4Rn7Ik4/piGRrTbXy2uLKvePgQJJduE+Y5c= +github.com/aliyun/credentials-go v1.4.5/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U= +github.com/aliyun/credentials-go v1.4.7 h1:T17dLqEtPUFvjDRRb5giVvLh6dFT8IcNFJJb7MeyCxw= +github.com/aliyun/credentials-go v1.4.7/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U= +github.com/andybalholm/brotli v1.2.2 h1:HzTuoo2ErYQqf5qvcJInB8uvqSVxRttzkFexPWtnceM= +github.com/andybalholm/brotli v1.2.2/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/apache/arrow-go/v18 v18.7.0 h1:Vw/i+cJyebUofT7JlqFpe65LrmwxULn166jjwStM4HY= +github.com/apache/arrow-go/v18 v18.7.0/go.mod h1:PM6IigLJkdMwIpeHXnymo+xZ52f42a9EYiLtRel4p/A= +github.com/apache/thrift v0.24.0 h1:zy31L1a49QTNB2bG1BBfMXol3yJrTH975G3pPubQVLQ= +github.com/apache/thrift v0.24.0/go.mod h1:zPt6WxgvTOM6hF92y8C+MkEM5LMxZuk4JcQOiU4Esvs= 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/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go-v2 v1.46.0 h1:1kt7m/EKcEHt5mlyyxx9cSlMddRPIKbjb6DIQsu4HPk= -github.com/aws/aws-sdk-go-v2 v1.46.0/go.mod h1:bttEH6JqnUL8LepvDVfdrds/fZ5bCIxzpe3abyUrhDU= -github.com/aws/aws-sdk-go-v2/config v1.33.3 h1:h090b3O5S17bF87/0ysHZuIT/7DCb4EBRFQX2PMVPCw= -github.com/aws/aws-sdk-go-v2/config v1.33.3/go.mod h1:YYDB1kTejxbfAbEVUqgCtkVp26xvNCHev9cLKABMGAk= -github.com/aws/aws-sdk-go-v2/credentials v1.20.3 h1:tToOYM/LXev4NpfWlIYGDvBvjHmJ3HXpRU9ppl+pM6k= -github.com/aws/aws-sdk-go-v2/credentials v1.20.3/go.mod h1:wfGneWyncO7p67wqXV2IQhPk14JqIc25woKlaArT3WI= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.19.2 h1:Ldv7RPHs7qwwTscRjAl3YBud32f3BvdAGRmSvAx5L38= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.19.2/go.mod h1:XyK6UV8xbo66ysVqLd2783C09pBYHOm8aKTRV5DVJ30= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.2 h1:q/PSLGuRWCChWg+dLnb9dWOnrCxJtnboXbBtFoqqRrI= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.2/go.mod h1:TD1jvU2LvXkJexct5vBqcd8QlNXh5EmRUeL/Z32p0n4= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.2 h1:6fl86IPqKEXoySqiOWdfgbEp9OVbn44zTfEICNEBDhY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.2/go.mod h1:63HDfhFkdzBpI8WGXTSKUHPKS6mqldj4u3LJW7RZtSU= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.2 h1:XMgIRS+uW9F3yFKnXGRrI9pkHi99CXTmoz2kz2/TGBA= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.2/go.mod h1:vorxDzK+n3jiv9a5ST/LG0Eu9cSv1CRdKTpG6pDMs+M= -github.com/aws/aws-sdk-go-v2/service/ecr v1.64.0 h1:iOYGE9bHGhMQYtbjEcgDJEobWIhKoUvE71m+Jm0vZgU= -github.com/aws/aws-sdk-go-v2/service/ecr v1.64.0/go.mod h1:5ccNgipT/aF9MWzTrKkyGJaCozPt+D6LOD4RFIdP22k= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.46.0 h1:15LYA7aTaqNWQzV0c7IRujt4mAUavcRckNz600RE6c8= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.46.0/go.mod h1:Kh1VyF4gwzKAvjGtG66pHyIhfSGxNRGA1wsbbd0NKSs= -github.com/aws/aws-sdk-go-v2/service/eks v1.98.0 h1:/VGWZHBd1+pGuLOzZCp+MXLY6O63HxeCHRk+Db6JPyc= -github.com/aws/aws-sdk-go-v2/service/eks v1.98.0/go.mod h1:e+m9ew4EU+j7X2GWWbB85Tg2WEnFRvWnwq/KnsG6RKI= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.19 h1:bAdDl/HkGCcGPoe25ToSHEw23VIxt6CT5fLcg111BKg= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.19/go.mod h1:KaUzbLxv4CeSxh6ZCl9B4m7CuFenS8kUEaDs+f/DQr4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.2 h1:ZtHYnumr6QyxhzEzNZwzQTFJEXOswrZqTTkRxthwvr4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.2/go.mod h1:a1NXrYpBd311gBzn1UI5UzJyyvXktM4xNh/ydPiPpqY= -github.com/aws/aws-sdk-go-v2/service/kms v1.52.0 h1:QNtg+Mtj1zmepk568+UKBD5DFfqh+ESTUUqQT27JkQc= -github.com/aws/aws-sdk-go-v2/service/kms v1.52.0/go.mod h1:Y0+uxvxz6ib4KktRdK0V4X45Vcs/JyYoz8H71pO8xeI= -github.com/aws/aws-sdk-go-v2/service/signin v1.9.0 h1:c3k+k/CS4L+sAIH6fxikL+g5g2LpeNczaoyjjw1iMKI= -github.com/aws/aws-sdk-go-v2/service/signin v1.9.0/go.mod h1:AGIoQg99fBrOIQnF78TLx4lj18mc4gZ0hJx1UaLIFM4= -github.com/aws/aws-sdk-go-v2/service/sso v1.37.0 h1:+rqBaOq7jzInjY8M12hr+zEe85JpRll9BjMx38r33Ok= -github.com/aws/aws-sdk-go-v2/service/sso v1.37.0/go.mod h1:XFlVwUsw3sYh8Hw37umYVJnrcWrwXWRxbNw/JY0bblw= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.42.0 h1:hzM3GslEAOBcLn3DHH6ENToFi+vXP+n02W+x6zejAIM= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.42.0/go.mod h1:588e7skMkYIYkSUseT8E3WKFfbAfrA1bj3Zf+qxJtJY= -github.com/aws/aws-sdk-go-v2/service/sts v1.49.0 h1:N7Ey8obY3uSui+cxl0OUzFlFmkxSucoJnrniFhw+cLc= -github.com/aws/aws-sdk-go-v2/service/sts v1.49.0/go.mod h1:zMBwjSf4Pt8a1OHYiZ5rPD0PJRK1kQrUaxhS/Dbld8E= -github.com/aws/smithy-go v1.28.1 h1:R/nXH00c8qcfCzQVELtRw+eLQWtzv+VAIEFJ1/xxXlQ= -github.com/aws/smithy-go v1.28.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/aws-sdk-go-v2 v1.47.4 h1:bTvk6UdSSSvaQsfLVj1JRDEEqj/PDRwGf1KanaRzau0= +github.com/aws/aws-sdk-go-v2 v1.47.4/go.mod h1:XkrjAZvr705Nytv6PkPUhoSB2W9+vVHBp5oAWpkh8Ak= +github.com/aws/aws-sdk-go-v2/config v1.33.10 h1:RRb0GghYqq2ikMXzfT4r9NO9lVhOld3FIhJJWDIdk+w= +github.com/aws/aws-sdk-go-v2/config v1.33.10/go.mod h1:BDSA7QGh6yrNQA5Z5aBnHWKMWdJDMD4IOlB4QjYSp5E= +github.com/aws/aws-sdk-go-v2/credentials v1.20.10 h1:ItSjcLk+2M15RnRxO105uVIqLiVQn8y3gaeHC7W0iqs= +github.com/aws/aws-sdk-go-v2/credentials v1.20.10/go.mod h1:eO4GlmGXB+gx+zDbhu3IENEFOmjrbVKJtOptJiIhngc= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.4 h1:7ksj/zMap9+831SmNQZkcpmfuATp3DKr4BfLNnBdYXM= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.20.4/go.mod h1:zhi8vDrD1dj76ysTWS6Ny1y6/EEW1Jrq2y8lzKeoqfs= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.7 h1:fSNAt6hs4qLjb9tMZc6vj3PHlOCYGXK2JjJZ7AW2tf4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.5.7/go.mod h1:TMav+EuXQu5kNpZZz/6rIC8xx4mCsW+6AKHrjvOSAsE= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.7 h1:7NS6kqBjkWIN8P/AEdmtyq6eQGAhN1oHTZdzXclpH6Y= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.8.7/go.mod h1:1PFqtAUsIgF1nEVojzRJSf/v88zjzqrGW+w+UAtkXyQ= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.7 h1:wYu55QUHARwu9YzeuY5yEJUTsIu8ahkmp1l3QmbzHn8= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.5.7/go.mod h1:OteEkhFAgFwQtZft/3myWo3tVWxdAo7cwV+7AbcpAtQ= +github.com/aws/aws-sdk-go-v2/service/ecr v1.66.6 h1:BLPDoTpiLhoMZd2QfAbSC6QWbLEd/KYcsps9rKrqlig= +github.com/aws/aws-sdk-go-v2/service/ecr v1.66.6/go.mod h1:hbHHpYZ/29gAnar+fh7C0BpL/ELXMwxh2q7ZqXSZePI= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.47.6 h1:3YzoAsrxJOZe0yn+fajdDO2Ja/3v9wVBWLQXLT6t88w= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.47.6/go.mod h1:m2T51VB7nSFOpDI8T3kNBwBVSwJrYfLlLm87AP+Wh08= +github.com/aws/aws-sdk-go-v2/service/eks v1.104.3 h1:WqkUjchX5c5Ya1l7I22po00NXfeImCe7M/FxTXk2bMs= +github.com/aws/aws-sdk-go-v2/service/eks v1.104.3/go.mod h1:yqmvzWK8FGGbWvjhW1wIpbjxpJNXgz0ciG2ISBXho+Q= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.22 h1:qHfXn1Tm2Rh8kic/DvdKaiSVlHQRSr5C+rMuBR29D44= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.22/go.mod h1:D4ytxJbGXu82l+2XF0q60bibysf+KkIbWfAOldAQwf8= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.7 h1:6MSlRo7aiUVBftQ8XmW7XIh3EZqCbkN9zI0YlGW8nl0= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.14.7/go.mod h1:OqapjZfp6VW4YJKKO4l68UdPvS4FIEYuEoq1JNkxCL0= +github.com/aws/aws-sdk-go-v2/service/kms v1.61.4 h1:vW7497x7W6ucLJAqDt96jIdO4+IWLMB3p98UubwjdqM= +github.com/aws/aws-sdk-go-v2/service/kms v1.61.4/go.mod h1:Vz59N7x42VGLXZ5dGL9b10QhrU4GQv7MqnO7wc8bl9U= +github.com/aws/aws-sdk-go-v2/service/signin v1.10.5 h1:oFsZ3u5PPFSO1YZDGKFgyDuwgpbv/sTmVShEZfRnyaE= +github.com/aws/aws-sdk-go-v2/service/signin v1.10.5/go.mod h1:P+IzYJeldxO0mHt8fX4cglzexP3akO94C/UuEA2ojP0= +github.com/aws/aws-sdk-go-v2/service/sso v1.38.5 h1:EYD6E6CsgRQPazaOfZ6suyTgfZjg07HoT4S3qB9Exkg= +github.com/aws/aws-sdk-go-v2/service/sso v1.38.5/go.mod h1:hHg7OZNN1/U62f3Q5mebJYKO4f97vZC+s0fNAsPLnMk= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.43.5 h1:v6V0AnJ0aDmivAIie/6iT7sGpjnNmzaAdxZwZ0dhwqI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.43.5/go.mod h1:P2Lqn27Kph9z400MFOYIebFnKjZEwyUIAva6fFFMiRY= +github.com/aws/aws-sdk-go-v2/service/sts v1.51.5 h1:OuyNaMncYP4ftrvcnOHJ9j3AxO3zsC5C/ewD0HoHdjQ= +github.com/aws/aws-sdk-go-v2/service/sts v1.51.5/go.mod h1:Bhu66tgvf94uunUSiAc0Ew0avhnpSm0J8Hl1MYocRbo= +github.com/aws/smithy-go v1.28.6 h1:Pj+/sUviNYjow8ELRtNwKHZ9ECeF+7pATX3+shjLIrw= +github.com/aws/smithy-go v1.28.6/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/aws/smithy-go/aws-http-auth v1.2.1 h1:uRP2q0BGnhpE1/8iriH3yHVMCg6nhFNaVCDvKYPQVmQ= github.com/aws/smithy-go/aws-http-auth v1.2.1/go.mod h1:KL46VTjVK9De3jurMqDLBkXCP9vrAvD03zQrmyzyrQ0= github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0 h1:JFWXO6QPihCknDdnL6VaQE57km4ZKheHIGd9YiOGcTo= github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0/go.mod h1:046/oLyFlYdAghYQE2yHXi/E//VM5Cf3/dFmA+3CZ0c= 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/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= -github.com/buildkite/agent/v3 v3.118.0 h1:wZiuvzku7oCzfzwePq7Zw8nPQxQkoEBQwLdI7b9r3CM= -github.com/buildkite/agent/v3 v3.118.0/go.mod h1:B4+s7ulBypyMmga4X8WrQmosF20UFpTIUt8au9seRGQ= -github.com/buildkite/go-pipeline v0.16.0 h1:wEgWUMRAgSg1ZnWOoA3AovtYYdTvN0dLY1zwUWmPP+4= -github.com/buildkite/go-pipeline v0.16.0/go.mod h1:VE37qY3X5pmAKKUMoDZvPsHOQuyakB9cmXj9Qn6QasA= +github.com/buildkite/agent/v3 v3.130.0 h1:fYo5iTrYAK6OTBWBoF7M627tnCKhZaRTD+Ex186dJZ0= +github.com/buildkite/agent/v3 v3.130.0/go.mod h1:vdR/SLbzWakWhK4wQO7SRW1YrMGQvP+5ARNXgVXw0LE= +github.com/buildkite/go-pipeline v0.17.1 h1:m7+a2SF9hLjqsbe6HShmzm9M4rDk4QtanKTF2LD6LG0= +github.com/buildkite/go-pipeline v0.17.1/go.mod h1:VE37qY3X5pmAKKUMoDZvPsHOQuyakB9cmXj9Qn6QasA= github.com/buildkite/interpolate v0.1.5 h1:v2Ji3voik69UZlbfoqzx+qfcsOKLA61nHdU79VV+tPU= github.com/buildkite/interpolate v0.1.5/go.mod h1:dHnrwHew5O8VNOAgMDpwRlFnhL5VSN6M1bHVmRZ9Ccc= github.com/buildkite/roko v1.4.0 h1:DxixoCdpNqxu4/1lXrXbfsKbJSd7r1qoxtef/TT2J80= github.com/buildkite/roko v1.4.0/go.mod h1:0vbODqUFEcVf4v2xVXRfZZRsqJVsCCHTG/TBRByGK4E= -github.com/cenkalti/backoff v1.1.1-0.20171020064038-309aa717adbf h1:yxlp0s+Sge9UsKEK0Bsvjiopb9XRk+vxylmZ9eGBfm8= +github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= @@ -286,8 +299,6 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/depcheck-test/depcheck-test v0.0.0-20220607135614-199033aaa936 h1:foGzavPWwtoyBvjWyKJYDYsyzy+23iBV7NKTwdk+LRY= github.com/depcheck-test/depcheck-test v0.0.0-20220607135614-199033aaa936/go.mod h1:ttKPnOepYt4LLzD+loXQ1rT6EmpyIYHro7TAJuIIlHo= -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/digitorus/pkcs7 v0.0.0-20230713084857-e76b763bdc49/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 h1:ge14PCmCvPjpMQMIAH7uKg0lrtNSOdpYsRXlwk3QbaE= github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= @@ -299,10 +310,10 @@ github.com/distribution/distribution/v3 v3.1.2 h1:/Bv2YIqqSR00HiO49FT93hovy6r9Il github.com/distribution/distribution/v3 v3.1.2/go.mod h1:Oz0Drs2Tx3iTXzK3DhUD4otfTfqlCxlWVC7QJeXWBP0= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz8= -github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= -github.com/docker/cli v29.7.2+incompatible h1:dlkwallR8XqfeVnA2ELEhdwvb4lsSwuB4IgsG8Q9cLY= -github.com/docker/cli v29.7.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/docker/cli v29.9.2+incompatible h1:trup/vUxx5uUo2t+87Cd9AmITR+DvKUe4H2tTtZN8FY= +github.com/docker/cli v29.9.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY= github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= github.com/docker/go-events v0.0.0-20250808211157-605354379745 h1:yOn6Ze6IbYI/KAw2lw/83ELYvZh6hvsygTVkD0dzMC4= @@ -315,8 +326,10 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a h1:UwSIFv5g5lIvbGgtf3tVwC7Ky9rmMFBp0RMs+6f6YqE= github.com/dylibso/observe-sdk/go v0.0.0-20240819160327-2d926c5d788a/go.mod h1:C8DzXehI4zAbrdlbtOByKX6pfivJTBiV9Jjqv56Yd9Q= -github.com/elazarl/goproxy v1.8.4 h1:tIHKhYHXf8gQracfoHl8Zy7PG/jhvmIMUR5j8OlPUIM= -github.com/elazarl/goproxy v1.8.4/go.mod h1:b5xm6W48AUHNpRTCvlnd0YVh+JafCCtsLsJZvvNTz+E= +github.com/eclipse-keypont/crypto11 v1.6.8 h1:4OE1GSUYFWbZb27S2+Glj6bQpiPND5BfnPIFxOCIkKU= +github.com/eclipse-keypont/crypto11 v1.6.8/go.mod h1:NIBgfiTuqvCyaktz22yE33PB4Aa0dU0M2Uf6QUTfDFk= +github.com/elazarl/goproxy v1.9.2 h1:+vXRRSWrznMtBrAb559qfqC+Cny1Q3rR0l51Yu/3WUw= +github.com/elazarl/goproxy v1.9.2/go.mod h1:THdE5ix2clxX9lZzcICPpZ67d6CdrPZxdOYsNgU5e30= github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -350,42 +363,46 @@ github.com/fluxcd/gitkit v0.6.0 h1:iNg5LTx6ePo+Pl0ZwqHTAkhbUHxGVSY3YCxCdw7VIFg= github.com/fluxcd/gitkit v0.6.0/go.mod h1:svOHuKi0fO9HoawdK4HfHAJJseZDHHjk7I3ihnCIqNo= github.com/fluxcd/pkg/apis/acl v0.12.0 h1:UWpZDqKxpKDW38wdXIMHV8Uv3POPu2vUrra0JH1frH0= github.com/fluxcd/pkg/apis/acl v0.12.0/go.mod h1:PJrGIu5Bw/6/siO95uhS+pi5oKSU+gVJqHqsPHb8Kok= +github.com/fluxcd/pkg/apis/crypto v0.5.0 h1:buy9YCvrD8IpKqOVCSV9k2wlF2iVPaETLOZBqsbyfVs= +github.com/fluxcd/pkg/apis/crypto v0.5.0/go.mod h1:bEwkDgBjLXMVJ2BwmVbbj32PkirmNSicabgl2Jvw7Dk= github.com/fluxcd/pkg/apis/event v0.32.0 h1:3zyjdUspwQN+EQJPlUXwCvCMbeqtB+azlNmeenAcbuE= github.com/fluxcd/pkg/apis/event v0.32.0/go.mod h1:ALma8dTqo+NbI+z4BawSzt0pAPjetCSNNUSSS/9p0VU= -github.com/fluxcd/pkg/apis/meta v1.35.0 h1:/k5d7uEFrxkz/hf6JTrdsKBr3UQWVHW2J4G6VSSae/k= -github.com/fluxcd/pkg/apis/meta v1.35.0/go.mod h1:1MLvK2Xu3UdTKBzg+Q1Oj7H9PHeiKsUB1UW3uwxAwiM= -github.com/fluxcd/pkg/artifact v0.24.0 h1:j/3JrNDThWRwtkkU9u2N7wOFkSVwZjnMYQWHNG51PEA= -github.com/fluxcd/pkg/artifact v0.24.0/go.mod h1:D1VCIh95degPgcfOOfVmRDHRqJqgRb2Z+PCCVODcHws= -github.com/fluxcd/pkg/auth v0.62.0 h1:ZhrkfoLTaH5Q095sNGLZ47zLYw/AHw4nVnPAspGwT/c= -github.com/fluxcd/pkg/auth v0.62.0/go.mod h1:46lKYjDvx5Q7fsq09ZC1BxXJcstTOfm2X/NFLLFn7wA= -github.com/fluxcd/pkg/cache v0.17.0 h1:197ST7bSJD2Nw4G01y5cMjtWQpwQsyjpl2ox4MaGeU8= -github.com/fluxcd/pkg/cache v0.17.0/go.mod h1:OW3fncHhPnPZFZav5FdEa//cyMHSndI0JS+IM69LXOU= -github.com/fluxcd/pkg/git v0.55.0 h1:jMdW0uAYuzcwIL5rJq5pmDd+jSrgOe6eIOZfdrcAvO8= -github.com/fluxcd/pkg/git v0.55.0/go.mod h1:s9AK9ZEG9CtAgkcX4QhQphLsaDBTje32tT5V3knpj1I= -github.com/fluxcd/pkg/gittestserver v0.32.0 h1:y1wbnEaOXpEYqAxGMxcsXZTH6W4QjFyTj6m1d1ytjY0= -github.com/fluxcd/pkg/gittestserver v0.32.0/go.mod h1:Cp4hpAePUXnSaeESHKitnvORCn+xT4fOKuZljkRBqVo= -github.com/fluxcd/pkg/helmtestserver v0.44.0 h1:VaJegpUI6KXxGQkiVoMAwzVkvWHNmqdEk77Y74S2vRE= -github.com/fluxcd/pkg/helmtestserver v0.44.0/go.mod h1:EttjvMWJWNSuQqdpLIlLDSXk1+1mocIJLVKioCk5qlU= +github.com/fluxcd/pkg/apis/meta v1.38.0 h1:7t6/DuGFAPx2LE4MsRh1jskjnVE6qELKfr/LrfG7KE8= +github.com/fluxcd/pkg/apis/meta v1.38.0/go.mod h1:lmdugeiZyt6w+DRm8ypkB+nwrzm+saMbqSfZKGBbTOE= +github.com/fluxcd/pkg/artifact v0.28.0 h1:k6/KjWQtHvLJ+KR2kciXJoL87vE6EuBZuZVzpaN4Uak= +github.com/fluxcd/pkg/artifact v0.28.0/go.mod h1:h4bURFRXHpl6ASHRgjqyPQJg4mh+8iQVMgM4Nyk1geM= +github.com/fluxcd/pkg/auth v0.66.0 h1:fNS6cf1WOVxVCdEKfCfDgbEgk2t3kzepaI1IKuiXuYo= +github.com/fluxcd/pkg/auth v0.66.0/go.mod h1:eD0iXDzt4BCHVNoBoDkYCsD3+LD39WN/r1qaqsgPJWk= +github.com/fluxcd/pkg/cache v0.19.0 h1:gcpASwfXLkaHTq8dv9AkXD+IvD54IfskRwGv3SDwC+E= +github.com/fluxcd/pkg/cache v0.19.0/go.mod h1:HYfYegfUsGBIttmTDZDFa0xmbeba4wPyND1YKitXm0w= +github.com/fluxcd/pkg/git v0.56.0 h1:DYsgOthnW0FtHAeosvJYQGCa/5PTbWwnDZdML7wt4YI= +github.com/fluxcd/pkg/git v0.56.0/go.mod h1:C3dgIpk8DXkgAE3tjxzkg/d4f+8DqY6GXmWbEpTBTT4= +github.com/fluxcd/pkg/gittestserver v0.33.0 h1:/MKs5Yf3w4BR91fJ4hH6W3E+saQYPDB3toViyDpQXpA= +github.com/fluxcd/pkg/gittestserver v0.33.0/go.mod h1:l6PPC/CuAbHn1YHniEulzbU/UWuHHQQegJmWzuz/sz0= +github.com/fluxcd/pkg/helmtestserver v0.45.0 h1:erSSj767Z3+LX1oq8Fuu1ybO1da0alg4PiIKW43yc3I= +github.com/fluxcd/pkg/helmtestserver v0.45.0/go.mod h1:Rc+VEXsTqfmcluj2IIVkujsOFaHxZ49leVBvr46/Rso= github.com/fluxcd/pkg/http/transport v0.9.0 h1:kDnqhfHNzSu5rfohC/saE97yS+Ubx1ASvUPMw97AhTI= github.com/fluxcd/pkg/http/transport v0.9.0/go.mod h1:6x2keN0cT/4s2DNpYjibEZTcBvRhE9B9ZXvmQCHmABE= github.com/fluxcd/pkg/lockedfile v0.9.0 h1:bjm/kUjYpdMBBz/5Hw0nTSLwsZwrithTrH8gMyZjbzI= github.com/fluxcd/pkg/lockedfile v0.9.0/go.mod h1:xtSyl0R6g01ItVGOTRqzjPZTXqFwCvkQTuWA52oudQg= github.com/fluxcd/pkg/masktoken v0.10.0 h1:jY9tuBIS1T7z9F8nJc9ozT0QrI391Se9vMu0eMd/wvE= github.com/fluxcd/pkg/masktoken v0.10.0/go.mod h1:zOCZp3trYmD5/csLOizvIGTi4gn76nnkTFu+vKOJufo= -github.com/fluxcd/pkg/oci v0.72.0 h1:nUTGHTd/2qq2yT66anHfXiwE19uJl6VL36zm74iFwbU= -github.com/fluxcd/pkg/oci v0.72.0/go.mod h1:5ngbB6SDvo6daD29HZtBbC6CcnHxuI+5JJR/YqtZhKM= -github.com/fluxcd/pkg/runtime v0.119.0 h1:cY2KpQ4WwpQKG+pFToslSIXUjKK8ckHQ7cvPUc/QHiY= -github.com/fluxcd/pkg/runtime v0.119.0/go.mod h1:7LJFbZPXwl++ATADx6bt63Jh9CverBoXQxbluxPwspw= -github.com/fluxcd/pkg/sourceignore v0.21.0 h1:p65j81RzXAeL4KJKgbkFRou+xiA0841CUdAsOCSAb5o= -github.com/fluxcd/pkg/sourceignore v0.21.0/go.mod h1:N9NFuxbEMm+UPtP9t+Oyx5LEs0uHu7W+WVPmdlIbCeY= -github.com/fluxcd/pkg/ssh v0.28.0 h1:DbqDtvJGKzrSfXfB8Hi9T2mbTnNBodijpRZRfxx59wE= -github.com/fluxcd/pkg/ssh v0.28.0/go.mod h1:FyaNc49GQcFIHdyZzwmG/9UApGaD5nKXT6CtzWAsaE0= -github.com/fluxcd/pkg/tar v1.3.0 h1:PbL5jNN5q/F6nXA8aCH+msM75BS6lSnPX2RNd48f8ls= -github.com/fluxcd/pkg/tar v1.3.0/go.mod h1:CbN3lQVWrYBZ4CwcK7ZzpxA++WiiM1RQHDOtNPJWuno= +github.com/fluxcd/pkg/oci v0.73.0 h1:3A3jJEpQdR+dgCXDGwmKFYRfx10lvDJPrwHSm4lvbsQ= +github.com/fluxcd/pkg/oci v0.73.0/go.mod h1:ckLyhq6xOi9kI2csdYwMPfqxU/xf+plJMs3IL598j6c= +github.com/fluxcd/pkg/runtime v0.122.0 h1:5nT+3PqbRvnzIMBNW6g/lIqsg6B715zaF12J3KnvjJY= +github.com/fluxcd/pkg/runtime v0.122.0/go.mod h1:46y8/GVMFotQkQxzzePecv7cn09DLbQhq6cl2VQ5l9A= +github.com/fluxcd/pkg/sourceignore v0.22.0 h1:GFvVgEA8HW4mZL26NFxhry3ZzCA8nR12MQovvZvbXm0= +github.com/fluxcd/pkg/sourceignore v0.22.0/go.mod h1:rEEyPqhc5u1t0fDkonqzGTKVuGOvp3v33boxg0Uw5VA= +github.com/fluxcd/pkg/spiffe v0.2.0 h1:UM5USXRkl92nZZov3R5gAfPxnRpudowsCprqPG/GNaA= +github.com/fluxcd/pkg/spiffe v0.2.0/go.mod h1:zLRMyp/vOwQEGWLhe04qKOBorUuTQvwBKBgpmWwkKQE= +github.com/fluxcd/pkg/ssh v0.29.0 h1:sw25lljJsA54Lcr1oiw7sBLU1c3vWroZf9uLy87rltg= +github.com/fluxcd/pkg/ssh v0.29.0/go.mod h1:193HoPaYQ/SJ9zsJZSMwVsQA/XL02ouOwAHj7jekHCw= +github.com/fluxcd/pkg/tar v1.4.0 h1:E6LKORT7ZBd/1ekX4N/gS9LeKfyLui1lBgne8e5PC8I= +github.com/fluxcd/pkg/tar v1.4.0/go.mod h1:20lDnHLNj/mgTUA494YWkxldX8zyyFexSKpuipX+6Tk= github.com/fluxcd/pkg/testserver v0.15.0 h1:6Pao9oK1Cu5ssh9c7QoDnnyfPUtxHbw7gJ+5tSOmkWc= github.com/fluxcd/pkg/testserver v0.15.0/go.mod h1:uSiItABsGd13YQDkYpIWeV1mww6KEYcH9wk/L4p2PKw= -github.com/fluxcd/pkg/version v0.19.0 h1:mTTsGs6V8OPlmh46FqsXdjQtPdURmtefnI5hxE6g9/E= -github.com/fluxcd/pkg/version v0.19.0/go.mod h1:zW3uCyPPNV5Gk3W2IVp5qG0dsBf7oAqIrehL3xnJUSg= +github.com/fluxcd/pkg/version v0.20.0 h1:uPT60sOPMPd0q3n1Go7dYC1fOjkq8wznwNwZ5vv7AJ0= +github.com/fluxcd/pkg/version v0.20.0/go.mod h1:n+JDY4+3Wb2NpFF3jWTSEEKvJC97ed+hN+o7QnBCHDA= github.com/foxcpp/go-mockdns v1.2.0 h1:omK3OrHRD1IWJz1FuFBCFquhXslXoF17OvBS6JPzZF0= github.com/foxcpp/go-mockdns v1.2.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -397,14 +414,14 @@ github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx5 github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo= github.com/fxamacker/cbor/v2 v2.9.4 h1:xwjVlxEMR3S605oUlgBjKLTTeGFciYPGYCtF/35LKGo= github.com/fxamacker/cbor/v2 v2.9.4/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/gaganhr94/docker-credential-acr v1.0.2 h1:0eMFjVqRUmwINbhFxb5xLTWLJpdNta34TGuInVGtEGk= -github.com/gaganhr94/docker-credential-acr v1.0.2/go.mod h1:8yd2V0GhCyd17MpMxfAJzcZqldu1ghFmrUV0GS7qcGc= +github.com/gaganhr94/docker-credential-acr v1.0.3 h1:zIXZrP1sm+Y7Qdk1wBID6u09LJH0sbmdqnOOS9pXItk= +github.com/gaganhr94/docker-credential-acr v1.0.3/go.mod h1:YsMgIr11zzK6lDE/w89aAfXszmWtmxp4J1V+1GegKOs= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU= github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk= github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= -github.com/go-chi/chi/v5 v5.3.0 h1:halUjDxhshgXHMrao5bB8eNBXo/rnzwr8m5m36glehM= -github.com/go-chi/chi/v5 v5.3.0/go.mod h1:R+tYY2hNuVUUjxoPtqUdgBqevM9s9njzkTLutVsOCto= +github.com/go-chi/chi/v5 v5.5.0 h1:xdKQ1dJmVJsmgahrXSKhUx8NyVPzpwdlyrrQFpMPJgg= +github.com/go-chi/chi/v5 v5.5.0/go.mod h1:d1IG7GtKPSjPyBP365PH8sAGVHk3qfB/IhjFNRH1wWM= github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= @@ -417,8 +434,8 @@ github.com/go-git/go-git/v5 v5.19.3 h1:qHttbZ+Am7wEjdTpR1XMQ4rl42FK9SIXzZ4o9x7s6 github.com/go-git/go-git/v5 v5.19.3/go.mod h1:Ye4C8dVigkqrv9UsB4NMdSLV4yAIkLiIhW61gcOyhl4= github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= -github.com/go-jose/go-jose/v4 v4.1.5 h1:RjgjO2LOtWOJKUC5wpwY9LR3B3vwVAz6JS2YHfYU6eA= -github.com/go-jose/go-jose/v4 v4.1.5/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= +github.com/go-jose/go-jose/v4 v4.1.6 h1:sNRkftxFJxwsILiQukqp5z83QBSmntVwmgRY7AqBDO4= +github.com/go-jose/go-jose/v4 v4.1.6/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-ldap/ldap/v3 v3.4.10 h1:ot/iwPOhfpNVgB1o+AVXljizWZ9JTp7YF5oeyONmcJU= github.com/go-ldap/ldap/v3 v3.4.10/go.mod h1:JXh4Uxgi40P6E9rdsYqpUtbW46D9UTjJ9QSwGRznplY= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -428,63 +445,63 @@ 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/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/analysis v0.25.5 h1:xPYEvTb90o1y0epuiOPAoG4QqahjP3cdp5xNlHeKJRI= -github.com/go-openapi/analysis v0.25.5/go.mod h1:d3UGtQC5uq5Kqqqis2VH09Km/v3vwsWrYkbp4gdm+Rc= -github.com/go-openapi/errors v0.22.8 h1:oP7sW7TWc3wFFjrzzj0nI83H2qMBkNjNfSd+XRejk/I= -github.com/go-openapi/errors v0.22.8/go.mod h1:BuUoHcYrU6E7V9gfj1I5wLQqgtIHnup/alXZ8KdgQ0w= -github.com/go-openapi/jsonpointer v1.0.0 h1:kR9tHqY0CtZaOPVFm622dPVNhrvYpwr4uCxgL3h1H8s= -github.com/go-openapi/jsonpointer v1.0.0/go.mod h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y= -github.com/go-openapi/jsonreference v1.0.1 h1:4zJ7AmYDKNmD3aSpfPnFNCFA5E80/xMHUNKgydaLh38= -github.com/go-openapi/jsonreference v1.0.1/go.mod h1:dYplQXa6p5lXprLcJ8LE2iU7vNpXsAHDQ5ZAgL+Qx3A= -github.com/go-openapi/loads v0.25.0 h1:74Bc2snfaVlsHzwdQj/3gsA9XJz3daXTJVs+4ZaK7jI= -github.com/go-openapi/loads v0.25.0/go.mod h1:JFBw4SIB9+PTIFHDfcXuSSy5h6aWzjtUCrPYyx3qWU8= -github.com/go-openapi/runtime v0.33.0 h1:Dd3Oj2ig+WH8ckK95l0Wn2V8a4bH/UqWPRZVT0vc8yU= -github.com/go-openapi/runtime v0.33.0/go.mod h1:+rsupH3+TFKqmFysqkmgBOTxpVJV8eV+j9myvvea2Xw= -github.com/go-openapi/runtime/server-middleware v0.30.0 h1:8rPoJ/xv7JL8BsovaqboKETlpWBArVh8n+0L/GyePog= -github.com/go-openapi/runtime/server-middleware v0.30.0/go.mod h1:OYNT/TxNvB/VK5oe4htM2jDTwlEXuejVJmu0DVZfAMs= -github.com/go-openapi/spec v0.22.9 h1:/vKIFDcGKp0ktZWGbym/tJEWbk6/XOEmAVU0kqKMH+w= -github.com/go-openapi/spec v0.22.9/go.mod h1:b/mNUYIOQOyIiUzUzXEE8xzyZqf93KvM9hQGP91yfl0= -github.com/go-openapi/strfmt v0.27.0 h1:kbcTeaD9TXuXD0hhMXzuYa1sdTo6+dWGvwjW93E80IM= -github.com/go-openapi/strfmt v0.27.0/go.mod h1:s/qhDqfY72irigXUGJmtgid2Rm+3tnz3k8hZaRmvWYc= -github.com/go-openapi/swag v0.29.1 h1:C6EeWzUwQtcWEhE9eqBdUubGXxhWY4PlzHMLD7kLaiQ= -github.com/go-openapi/swag v0.29.1/go.mod h1:BzxEXKiPlSXRsRTv1KSBF/BpGKHxA/YciCnr4tv9bvA= -github.com/go-openapi/swag/cmdutils v0.29.1 h1:3DorPGfUdE80BogKY22EzoHBcHMrkVomZMoV7kS4ANY= -github.com/go-openapi/swag/cmdutils v0.29.1/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM= -github.com/go-openapi/swag/conv v0.29.1 h1:AC4Eh/5c/eUDOUCzzsRC9ghmFgOSBHeRMGIngY0ZUGA= -github.com/go-openapi/swag/conv v0.29.1/go.mod h1:S1X7/ZrBEZOC0Wc8AGxjbcGS92l3WEjA7aPtpl+RaqM= -github.com/go-openapi/swag/fileutils v0.29.1 h1:ZcPzMceVhU1WPbK6N1G6sNQKdd1CWJlf3cA08UHuoM0= -github.com/go-openapi/swag/fileutils v0.29.1/go.mod h1:/wofKYckbtRl2p3+EwQsosie5CT1B38+dQ+PS579BzI= -github.com/go-openapi/swag/jsonutils v0.29.1 h1:AFCxs0eQZ24/QyfhVHM2t49rMz7Vv3XCsZQI6yrNy+c= -github.com/go-openapi/swag/jsonutils v0.29.1/go.mod h1:u3+sCfJpttDpcmS5kpm0yxL6GK0eWgODsx8Yw8fcqNM= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.29.1 h1:BiiXE31Bx9SfpsMmOQj5KYpUhTZBpLVriVhJDuLuY2o= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.29.1/go.mod h1:julgTUKZ9/D0j6O7GKajmRs+812FWxQg/mMpGunWSjg= -github.com/go-openapi/swag/loading v0.29.1 h1:FCv5fG8UhTdDJa2R7w+5O9Ekpcbw7tt0nFWvmDKGBjc= -github.com/go-openapi/swag/loading v0.29.1/go.mod h1:N0ESuem4p2oedKal8EJhciqnJ9Q9Wmt83L1CRB3Fouw= -github.com/go-openapi/swag/mangling v0.29.1 h1:lHALtvYCdxVnRl4GrHmFPwfBTZYIObqdGNSKyu/8D6I= -github.com/go-openapi/swag/mangling v0.29.1/go.mod h1:SAop9pB7PUjQ/CGCNf/JmCKTRK+GDO+RqE9UHqC/N6s= -github.com/go-openapi/swag/netutils v0.29.1 h1:IjIvdEP5duKcghFqJEPSUraRnkKYHoM65kTluTu+Jb4= -github.com/go-openapi/swag/netutils v0.29.1/go.mod h1:DUde7x4Bx00k5jYl2AdRpNAO0m7atUvD2x6X+bWkbno= -github.com/go-openapi/swag/pools v0.29.1 h1:NRogYxdEW9SjRM4mkAOji9iefO4MRXq3p/ZJcoQbUKg= -github.com/go-openapi/swag/pools v0.29.1/go.mod h1:leDcaghjkRAhCuCRv9NfJU5f0mjoU3cT/XZObhMk3pc= -github.com/go-openapi/swag/stringutils v0.29.1 h1:1ykunK7iJQk1uOO7+oUH1ukbsK85fFCOiCFMOVSY+F0= -github.com/go-openapi/swag/stringutils v0.29.1/go.mod h1:7fSqZ+z8Qc0tOfAAK0jVa5qFGrnIlRi6n7NeGGrr1vc= -github.com/go-openapi/swag/typeutils v0.29.1 h1:Nzv9nhnlLCRBPQqfOX+7lB6Guju370or8StT+lIOf6M= -github.com/go-openapi/swag/typeutils v0.29.1/go.mod h1:hxpgDZJVBkBsi/d3MIUosafoFdE5exaQRmVp0zwu3YE= -github.com/go-openapi/swag/yamlutils v0.29.1 h1:69w3tsBajm7MR/fejLy7HD/3J68Ys1SeeZMEzZ3w2sk= -github.com/go-openapi/swag/yamlutils v0.29.1/go.mod h1:rgsp3vT/QdWzKwn43CigDwjOGIenPyTZMKnxEM8jZOA= -github.com/go-openapi/testify/enable/yaml/v2 v2.6.1 h1:Jm+/ze2rMtbD98yen92AhATGLGREDYXG56Xr4gMjEtE= -github.com/go-openapi/testify/enable/yaml/v2 v2.6.1/go.mod h1:YDPnwCRDu38/oJBVMBVXOUDiJ9cIeBHWvfImHaXqnv4= -github.com/go-openapi/testify/v2 v2.6.1 h1:6CNJhTjMzgaeaH8WhshcsZNPIvRemiOcFpU7seO/y7Q= -github.com/go-openapi/testify/v2 v2.6.1/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= -github.com/go-openapi/validate v0.26.1 h1:pZSbvtRO8G2R2FpWTYRn3w8LrsNwbtaVhP2dWiBa0Us= -github.com/go-openapi/validate v0.26.1/go.mod h1:B8UMgXiQiwwQWIbmuROlwJZDPGlikPuh7iHV1vPX9Oo= +github.com/go-openapi/analysis v1.0.0 h1:sNvbAGCJqUTqIAodr9IVqJMmuZas3YS9ms1dGK9yiJ4= +github.com/go-openapi/analysis v1.0.0/go.mod h1:NhYjJ57fnE+bcE7UwrJyMkhWA3Dfz7TdiBfTVAnos4Y= +github.com/go-openapi/errors v0.22.9 h1:HI9+SyVYiRzyeBQGv0CbWbQa+u2S0nny7G7AhY+UMiM= +github.com/go-openapi/errors v0.22.9/go.mod h1:R73q66sXP2smzAGgFOI4nqtusRK2xN5pYCjsij+KX58= +github.com/go-openapi/jsonpointer v1.0.1 h1:2KxywRmNwJkT/FMBa3iRNHEaAxSJvjqoufQZy3au1Mg= +github.com/go-openapi/jsonpointer v1.0.1/go.mod h1:wI7ZYsFmbIi9nBXOZqgDaS/bqOchRGZjqxFli7FBYxY= +github.com/go-openapi/jsonreference v1.0.2 h1:oS4et8FOf3p3UQxEo4Xt0esijmBUM+F259Xl72OSZsc= +github.com/go-openapi/jsonreference v1.0.2/go.mod h1:TbUNSOo+fcorZjFaNoiSDSoaNnnZtqJtLGR1PuvE/Cs= +github.com/go-openapi/loads v0.25.3 h1:V+jKy/thXWdLJUuYC8sZX2dICyAa8M3DokF70jj34P0= +github.com/go-openapi/loads v0.25.3/go.mod h1:LgLyCSOLBL2Qnj0Ps1oo2YH8jZoEKsMeVZCc+ALIxFo= +github.com/go-openapi/runtime v0.33.3 h1:qVgTDaFH+pzO8UqlwYNVmx2DD+nwVK3rB65V29ednD0= +github.com/go-openapi/runtime v0.33.3/go.mod h1:57crQFXXnCtT68LuAd+NOELX5tSF4ZE1GFW3Z+r/KCk= +github.com/go-openapi/runtime/server-middleware v0.33.3 h1:Td5UBla/J/KCjlsD0tmZYWQWxnT4gHpfXbueLQv9Umw= +github.com/go-openapi/runtime/server-middleware v0.33.3/go.mod h1:dopwrwqIqXe4xWtovDZkkXJzYqKw+Ybmy7G/hIDUWKY= +github.com/go-openapi/spec v1.0.1 h1:lj2vdGpNDcVgwRc6qXdw6qt/KQpCtSa9tnUH6vpDPDk= +github.com/go-openapi/spec v1.0.1/go.mod h1:M//GWQGtDUAjnP37gE6fInLgaczB+FatoipV3H1fYw8= +github.com/go-openapi/strfmt v0.27.3 h1:cyf4J5Wjpd6bFpAs44MF8PsbWF/mWXhn82Ck/hgQZgw= +github.com/go-openapi/strfmt v0.27.3/go.mod h1:Bl+xadPLa3nhBiaCAxNND1vrQfzyEjF0vBrLx96n/4E= +github.com/go-openapi/swag v0.29.2 h1:9n8frkcsuQRA0INU31VbxNxt1zBaHxrHYGEf5BUjFi4= +github.com/go-openapi/swag v0.29.2/go.mod h1:RkCiX1gCVXWgLOavDXE3ALJrQ/VO2ziiIzOUs3z10xo= +github.com/go-openapi/swag/cmdutils v0.29.2 h1:cXEzX/nWCODon251f+1HgL75/R4XXML5IZxgLCWMoro= +github.com/go-openapi/swag/cmdutils v0.29.2/go.mod h1:XziaSVzYqkDvpWXzASfWajw2YvJrLscq610O2XiyqJw= +github.com/go-openapi/swag/conv v0.29.2 h1:8c9shoB8l0QRSR6ymq1llHdBWDqpIgJfjTGHx3Vuhm0= +github.com/go-openapi/swag/conv v0.29.2/go.mod h1:AZS0YigTNf8qNtD6WqJz/N4RUNmpr76SyjFGkq4+p6Q= +github.com/go-openapi/swag/fileutils v0.29.2 h1:mdUL+Vw5ah1fO1AvFCoHeIyv7YZsCz2MN3KNqd/lLVI= +github.com/go-openapi/swag/fileutils v0.29.2/go.mod h1:7QKmmodjAebaq61lGVJa8ldLdGeMF4DSgYv8tXOGGo0= +github.com/go-openapi/swag/jsonutils v0.29.2 h1:uZNSD2/rJDYAfvsLYkykTzXuyxM3QpDKV9jhRHrMu6k= +github.com/go-openapi/swag/jsonutils v0.29.2/go.mod h1:ONTdNvj3Y+IXRzfa17E+Rgt2ns/qiSOYjIo2K7v2yDs= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.29.2 h1:w+Fd6EBOMGlC74GYGHqCLGyb3Bpams8TtNrgM/SG4jo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.29.2/go.mod h1:HC2egPORFzbj/gf05Zrfgz8Mgplx3oW7hgGi7pjZVhM= +github.com/go-openapi/swag/loading v0.29.2 h1:QU1ry24e6r6Shoxe380Nx0X5iKkpRO+hJffsyJ2dVlY= +github.com/go-openapi/swag/loading v0.29.2/go.mod h1:DqilDjuiJKETecsS4TRopWG1acUDsfqN39ZmQhPu6uI= +github.com/go-openapi/swag/mangling v0.29.2 h1:ltdo3K0tTP4P6zWhVVWDU9D37/YYBuMJRdZxiS2Dtog= +github.com/go-openapi/swag/mangling v0.29.2/go.mod h1:RjDi4TnItAenS3cgCXSpIl1kKbjG7il6pnmQVl/8bp8= +github.com/go-openapi/swag/netutils v0.29.2 h1:dBli6jyUa93sDS/XYWShJzf4+nnVfFKfvY9SLUaUzB8= +github.com/go-openapi/swag/netutils v0.29.2/go.mod h1:1pM6Xg/Um8E1eps3umWl8Hme3ByWXOtHsvFC0CULHUE= +github.com/go-openapi/swag/pools v0.29.2 h1:PlGoDRF8WtSyXkedZZkpW3f9wDhFf3u8KtaBcmgmh8c= +github.com/go-openapi/swag/pools v0.29.2/go.mod h1:V3lhxVT4qDYRqc2MRSSbLsTYIYV/2HlHafhyEkmzLIc= +github.com/go-openapi/swag/stringutils v0.29.2 h1:lcnBxwAaysT3bMUVBfn9V/PkfVqkurpqufKU6rTUMGk= +github.com/go-openapi/swag/stringutils v0.29.2/go.mod h1:i9cdh7sGaa0nm3CqIvH5IM5h2QClk1wns2ktKeILvRI= +github.com/go-openapi/swag/typeutils v0.29.2 h1:O7aVvkTs3pXwikgtrPLenigEMdQFgONKRooQA9pgf2I= +github.com/go-openapi/swag/typeutils v0.29.2/go.mod h1:7+GDG+uz9Ke+eVt4rClZg7wklSDp8hT/mKNh/pC26TE= +github.com/go-openapi/swag/yamlutils v0.29.2 h1:IFKFFeDnuIwzfsuWRQU+rI8iL3g4XyAYjV/RlnlxPLM= +github.com/go-openapi/swag/yamlutils v0.29.2/go.mod h1:7MGqtcrK73sxQ4ceiyIf8qiXS/s09JLMdR5esK5euYQ= +github.com/go-openapi/testify/enable/yaml/v2 v2.8.0 h1:dvOQNZ2ovOdogn902NYq7i6jUI9Dma4qyjTXABY5YTY= +github.com/go-openapi/testify/enable/yaml/v2 v2.8.0/go.mod h1:3VwZZUT6nWcAjhIr2RypXxlsxd3mPPtXh3T8z65aRlQ= +github.com/go-openapi/testify/v2 v2.8.0 h1:19QDx5b57p8KjO2E4sgs/woL4Akp+FGLRlVm2xn0Rho= +github.com/go-openapi/testify/v2 v2.8.0/go.mod h1:4gUN8jC+RqOE2qjW/vDmfyTvl5BCht4qL7oca0odrak= +github.com/go-openapi/validate v1.0.0 h1:dFsYCLVUQUL6Vi2lQSexgwmCXDuHe7eWRDhQxkE+xYA= +github.com/go-openapi/validate v1.0.0/go.mod h1:wwXGRqMQzOZ7PCqBcgNk+DD9+Cacnxv7we5T0M/eA3Y= github.com/go-piv/piv-go/v2 v2.6.0 h1:/Z+uqlv5luC8aqLh/uO05egk23UH4KT/ldvPHXpnKls= github.com/go-piv/piv-go/v2 v2.6.0/go.mod h1:gcsS2WGbToZk5PwtsCIlCWzV/R+r/wN+Xi+6O6IlU3c= github.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA= github.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= -github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= -github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= +github.com/go-sql-driver/mysql v1.10.1 h1:arlSnNLq6a5yxGxV7qg9lF4j0C+KwD6NbQyKr9QL6ME= +github.com/go-sql-driver/mysql v1.10.1/go.mod h1:M+cqaI7+xxXGG9swrdeUIoPG3Y3KCkF0pZej+SK+nWk= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= @@ -493,12 +510,16 @@ github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4= +github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= +github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= -github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= -github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= +github.com/gofrs/flock v0.13.1 h1:jjREztyBeSKBZYAC+mgc1laB+xsgy4kYMf3FbKF2UBo= +github.com/gofrs/flock v0.13.1/go.mod h1:sf4BFiHwnvgxa25DlQoDqXQnwRMEOwqxRq37P6MzzmE= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= @@ -525,12 +546,15 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs= +github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 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/certificate-transparency-go v1.3.3 h1:hq/rSxztSkXN2tx/3jQqF6Xc0O565UQPdHrOWvZwybo= github.com/google/certificate-transparency-go v1.3.3/go.mod h1:iR17ZgSaXRzSa5qvjFl8TnVD5h8ky2JMVio+dzoKMgA= +github.com/google/flatbuffers v25.12.19+incompatible h1:haMV2JRRJCe1998HeW/p0X9UaMTK6SDo0ffLn2+DbLs= +github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -543,14 +567,14 @@ 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-containerregistry v0.22.1 h1:RZuuSYhTvlDvtsK+NkutoCZ//C0X2ebLK8X8l3ULs84= github.com/google/go-containerregistry v0.22.1/go.mod h1:bJR35SK8XgisYmhg/FMQ/5RK0S/XrOAqLBV5/LR2XE0= -github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20260602205845-4b7672c7705a h1:/Q1YXihxN5BQfLnzVr01Z6CARmIxe7B3PfcpZ0wcRk0= -github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20260602205845-4b7672c7705a/go.mod h1:SUZDppd0UtyRvoL0do5jUQqBUK+trwLQBg4EKF76WdI= +github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20261013193525-92aac6186bc4 h1:RaBbASFWNjSeFBUCKzbAmyP9sxxmTryISLgMTlBp4bE= +github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20261013193525-92aac6186bc4/go.mod h1:ADikmEMSYuH3sNMpDIgjc3iw1lLqurwd+iAL2dMN64I= github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20250225234217-098045d5e61f h1:GJRzEBoJv/A/E7JbTekq1Q0jFtAfY7TIxUFAK89Mmic= github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20250225234217-098045d5e61f/go.mod h1:ZT74/OE6eosKneM9/LQItNxIMBV6CI5S46EXAnvkTBI= -github.com/google/go-github/v73 v73.0.0 h1:aR+Utnh+Y4mMkS+2qLQwcQ/cF9mOTpdwnzlaw//rG24= -github.com/google/go-github/v73 v73.0.0/go.mod h1:fa6w8+/V+edSU0muqdhCVY7Beh1M8F1IlQPZIANKIYw= github.com/google/go-github/v87 v87.0.0 h1:9Ck3dcOxWJyfsN8tzdah4YvmqB/7ZsstMglv/PkOsl0= github.com/google/go-github/v87 v87.0.0/go.mod h1:hGUoT5pwm/ck5uLL+wroSVQfg8mpe+buxllCcGV4VaM= +github.com/google/go-github/v88 v88.0.0 h1:dZA9IKkPK1eXZj4ypngnpRj5FwdpTv4whix2PrQMP7M= +github.com/google/go-github/v88 v88.0.0/go.mod h1:rufTDgn2N45wjhukLTyxmvc9nilSp3mr3Rgtt6b1MPw= github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -559,19 +583,19 @@ github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ 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/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 h1:EwtI+Al+DeppwYX2oXJCETMO23COyaKGP6fHVpkpWpg= -github.com/google/pprof v0.0.0-20260402051712-545e8a4df936/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= -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/trillian v1.7.3 h1:hziW+vo4czis48tzx2GK5xRBl/ZxBA9B0/UR5avXOro= -github.com/google/trillian v1.7.3/go.mod h1:qh8iy4x/GvnVXUBd5pK4oncuT1Y9vVYfibQVsR/WpKg= +github.com/google/pprof v0.0.0-20260604005048-7023385849c0 h1:h1QTMDl6q9wDvDCJVpKQSjgleGFYnd2fOxmg2K+6BGE= +github.com/google/pprof v0.0.0-20260604005048-7023385849c0/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI= +github.com/google/s2a-go v0.1.11 h1:7v1V1TraNGP63WptsAkNqr2w8jgm4vM6A6qx6Lsw9iM= +github.com/google/s2a-go v0.1.11/go.mod h1:pz4tyvwXvJLLbyrkh6FW1eS2zPUXMaTmyNhYtyP2tNw= +github.com/google/trillian v1.8.0 h1:AnbjOFFMQyvqpPAARj97CKEXmoSpCy2hZw0T7Y1Y0BA= +github.com/google/trillian v1.8.0/go.mod h1:E9eOXWvvyzIMLXLBgYmwklSl2RORBqGX1hDJkEoHdRc= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/googleapis/enterprise-certificate-proxy v0.3.20 h1:t/xL64VUoN69MuMRQuJETqYGOw4Z9mSRJK9epIEtwFk= -github.com/googleapis/enterprise-certificate-proxy v0.3.20/go.mod h1:L3D/IQExI6LqEjBdXcZQ1WluSgigQmSwBboFstVPM4w= -github.com/googleapis/gax-go/v2 v2.24.0 h1:myMaPYyF9MecEmvQqMqomIwn9t/4KCZN9qnwsS76wlg= -github.com/googleapis/gax-go/v2 v2.24.0/go.mod h1:IaTHBDd7NHxSCiu0vEs8pQZu4dGZrWwuSoxCnk16OFM= +github.com/googleapis/enterprise-certificate-proxy v0.3.23 h1:J+VGF1wjAM2dU2kKUXZxJdDppRLReWP7KWcclfngQhc= +github.com/googleapis/enterprise-certificate-proxy v0.3.23/go.mod h1:Y8NO/15GNLkrXI3OR5xOFmeU3XLX1sLSdtlXgJ0zmIw= +github.com/googleapis/gax-go/v2 v2.26.2 h1:ydkmNXxj7bEmmeK5AihkKnWxyOyBR9TDebvp5L5izk8= +github.com/googleapis/gax-go/v2 v2.26.2/go.mod h1:sMKqnMesnKH+3wiRJROcttA+cJoZoGbZl1vDQ8XYtGk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= @@ -582,12 +606,11 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= -github.com/graph-gophers/graphql-go v1.9.0 h1:yu0ucKHLc5qGpRwLYKIWtr9bOoxovkWasuBrPQwlHls= -github.com/graph-gophers/graphql-go v1.9.0/go.mod h1:23olKZ7duEvHlF/2ELEoSZaY1aNPfShjP782SOoNTyM= github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI= -github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0 h1:/Tnpcb2E0Pz/tN9s3bfEY2Q8ePCEX9iuS+cneUwncnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.30.0/go.mod h1:zOBXOsUaBSjKgmH4OGzV1esUpR3oUSCPYVd2cUBjKYY= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.4 h1:9ZJYjPEJpcleIKcqysXOo14NLQYwu23fzmKRFpIjPjc= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.4/go.mod h1:0xydIeg2omQ9DH6zYMP24Kk57793rnLUvP8fwdbFvz8= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.31.0 h1:Bd7KaOxzULLxtZ/K5s1aLbWhR0+5RToO65TXHsf3bqQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.31.0/go.mod h1:nN7ts3dFXKtCZWc//yfkpcQNKJABg16/uDVAZpLDalo= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= @@ -615,8 +638,8 @@ github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I= github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hashicorp/vault/api v1.22.0 h1:+HYFquE35/B74fHoIeXlZIP2YADVboaPjaSicHEZiH0= -github.com/hashicorp/vault/api v1.22.0/go.mod h1:IUZA2cDvr4Ok3+NtK2Oq/r+lJeXkeCrHRmqdyWfpmGM= +github.com/hashicorp/vault/api v1.23.0 h1:gXgluBsSECfRWTSW9niY2jwg2e9mMJc4WoHNv4g3h6A= +github.com/hashicorp/vault/api v1.23.0/go.mod h1:zransKiB9ftp+kgY8ydjnvCU7Wk8i9L0DYWpXeMj9ko= github.com/hiddeco/sshsig v0.2.0 h1:gMWllgKCITXdydVkDL+Zro0PU96QI55LwUwebSwNTSw= github.com/hiddeco/sshsig v0.2.0/go.mod h1:nJc98aGgiH6Yql2doqH4CTBVHexQA40Q+hMMLHP4EqE= github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM= @@ -633,14 +656,6 @@ github.com/in-toto/in-toto-golang v0.11.0 h1:nfidMYBFx+E0lnmX5KUnN2Pdm8zdNKal1ay github.com/in-toto/in-toto-golang v0.11.0/go.mod h1:u3PjTnwFKjp5a1YCcw8SJg0G+tMeKfVoWsWeFMDCMtw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= -github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo= -github.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw= -github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= -github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= @@ -657,10 +672,8 @@ github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZ github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 h1:TMtDYDHKYY15rFihtRfck/bfFqNfvcabqvXAFQfAUpY= github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E= -github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY= -github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4= -github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs= -github.com/jmhodges/clock v1.2.0/go.mod h1:qKjhA7x7u/lQpPB1XAqX1b1lCI/w3/fNuYpI/ZjLynI= +github.com/jellydator/ttlcache/v3 v3.4.1 h1:bOdXmXiycyK6E6Qjyuj5vl+/vU3SCOoDs8a86NbHjAQ= +github.com/jellydator/ttlcache/v3 v3.4.1/go.mod h1:j7LO12PNghFg5+0v9budMAT4rDK4JY969jb9vOdOBBk= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -671,12 +684,12 @@ github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRg github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M= github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= -github.com/klauspost/compress v1.19.2 h1:hMRETovs/pu/dVWN7zIT1PGG8t509MwT6bO7XSi26R8= -github.com/klauspost/compress v1.19.2/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/compress v1.20.1 h1:T7kKElXUMXrUJ2E9QhQhxFtcK5rPyLdsGZvdbLMPdiQ= +github.com/klauspost/compress v1.20.1/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= -github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw= +github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU= github.com/klauspost/crc32 v1.3.0 h1:sSmTt3gUt81RP655XGZPElI0PelVTZ6YwCRnPSupoFM= github.com/klauspost/crc32 v1.3.0/go.mod h1:D7kQaZhnkX/Y0tstFGf8VUzv2UofNGqCjnC3zdHB0Hw= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -692,33 +705,30 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= -github.com/letsencrypt/boulder v0.20260309.0 h1:kZynrxK3QfqLGx6hhoz+Rfs3hgltJs1p9Mp+4+VwnY0= -github.com/letsencrypt/boulder v0.20260309.0/go.mod h1:yG8lj8pNPZ8taq3oNdTpfBS+eC74IaEuiewqzVpXiWE= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ= github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.23 h1:7ykA0T0jkPpzSvMS5i9uoNn2Xy3R383f9HDx3RybWcw= -github.com/mattn/go-runewidth v0.0.23/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY= +github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= +github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= +github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU= +github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= -github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= -github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= -github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/miekg/dns v1.1.73 h1:uhT8nJxmTrPJYClxVxTCX+CVn6qnzSiybRk72Z6DgrE= +github.com/miekg/dns v1.1.73/go.mod h1:RW2Obtfd5NZHvOFe3zYG0W8koWOQtAzyHaLo8vASBuQ= github.com/miekg/pkcs11 v1.1.2 h1:/VxmeAX5qU6Q3EwafypogwWbYryHFmF2RpkJmw3m4MQ= github.com/miekg/pkcs11 v1.1.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI= github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.2.1 h1:PfBfwvKB/MmqyN8Vb1G9voWisaM9OrLv+WwOvMwS9Dw= -github.com/minio/minio-go/v7 v7.2.1/go.mod h1:EU9hENAStx/xXduNdrGO5e4X5vk19NtgB+RIPjZO8o0= +github.com/minio/minio-go/v7 v7.4.1 h1:PtU/zIOfQRWZ+QydXAZysc+xZYHdG+BBR4Z6TUvy73w= +github.com/minio/minio-go/v7 v7.4.1/go.mod h1:nQnQi7zTfXR4lREtYXZQWqo5aw0Rqv+ZqCGrTORygRk= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -741,8 +751,10 @@ github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFd 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/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/mozillazg/docker-credential-acr-helper v0.4.0 h1:Uoh3Z9CcpEDnLiozDx+D7oDgRq7X+R296vAqAumnOcw= -github.com/mozillazg/docker-credential-acr-helper v0.4.0/go.mod h1:2kiicb3OlPytmlNC9XGkLvVC+f0qTiJw3f/mhmeeQBg= +github.com/mozillazg/docker-credential-acr-helper v0.4.1 h1:f8MOoueAItLW++oIuyHALHZVQU5RB9yOjgec6HPGFu8= +github.com/mozillazg/docker-credential-acr-helper v0.4.1/go.mod h1:dZ74CN1sQBbHqsx2LHvqmo+xV18Ml5e3LCGW5hmNQek= +github.com/mozillazg/docker-credential-acr-helper/pkg/acr v0.2.2 h1:kpucZE0Et2zh2/hObaMV4XqIF/UeZt0cLuD76Y7AYyo= +github.com/mozillazg/docker-credential-acr-helper/pkg/acr v0.2.2/go.mod h1:VN6/2/Qgpgh9nvVVQfslzVsBv7A3I4WnuYAxNv9mAzE= 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/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A= @@ -762,8 +774,8 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= -github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s= -github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= +github.com/oklog/ulid/v2 v2.1.2 h1:IEclFb9JNvzYA6MW2SCxbLzcHTVsfqm3PrqGQJH5zec= +github.com/oklog/ulid/v2 v2.1.2/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= github.com/oleiade/reflections v1.1.0 h1:D+I/UsXQB4esMathlt0kkZRJZdUDmhv5zGi/HOwYTWo= github.com/oleiade/reflections v1.1.0/go.mod h1:mCxx0QseeVCHs5Um5HhJeCKVC7AwS8kO67tky4rdisA= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -778,12 +790,12 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.43.0 h1:VlG/1FxqNxhSO+lq/OHBNaaqwiBK/mO8JbVkX9Y+FeU= -github.com/onsi/gomega v1.43.0/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg= +github.com/onsi/gomega v1.44.0 h1:eAiGl3Pw5jz5GQdDff0BcxYpAX1JxW8xD7mFUuwNfZQ= +github.com/onsi/gomega v1.44.0/go.mod h1:e/C2HwaZ1DhvjzXXuFhcR7hY7Sh9pl7MmoWKEjzwcdA= 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/blake3 v0.0.0-20250813155314-89707e38ad1a h1:IAncDmJeD90l6+YR1Gf6r0HrmnRmOatzPfUpMS80ZTI= -github.com/opencontainers/go-digest/blake3 v0.0.0-20250813155314-89707e38ad1a/go.mod h1:kqQaIc6bZstKgnGpL7GD5dWoLKbA6mH1Y9ULjGImBnM= +github.com/opencontainers/go-digest/blake3 v0.0.0-20261015205711-63db54280247 h1:S7t62qL81QHcqEVKklPO5Epd5YwYIo7+rj6ec6gscsM= +github.com/opencontainers/go-digest/blake3 v0.0.0-20261015205711-63db54280247/go.mod h1:kqQaIc6bZstKgnGpL7GD5dWoLKbA6mH1Y9ULjGImBnM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8= @@ -793,19 +805,22 @@ github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAq github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= -github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc= -github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY= +github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +github.com/phuslu/lru v1.0.18 h1:ioKRYLym7nv6UmaKHXSR0Z8s2KCEra+mcWcn9zXQnlM= +github.com/phuslu/lru v1.0.18/go.mod h1:ci5hb8dRIa+2I+KcPl4958OWCg09FxwZCP8InU1L1ME= +github.com/pierrec/lz4/v4 v4.1.28 h1:pPEPwRJ4kybBTfGt28q7lQsRJQHhC08axprdLD5Ppio= +github.com/pierrec/lz4/v4 v4.1.28/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= github.com/pjbgf/sha1cd v0.7.0 h1:ZRNPKHj+gfkLBf0KJv/p1Hmz+7bqCT5o311YX0nq+DA= github.com/pjbgf/sha1cd v0.7.0/go.mod h1:pKR5Li+qTCo+ebqITZfwPlJGoCFCvSO00qxjbNtTUFQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= @@ -815,8 +830,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= -github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU= -github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE= +github.com/prometheus/client_golang v1.25.0 h1:cDtW1TcFsMi1RJc8khwCKB4VhzwV04aKqbYtNOXuiK0= +github.com/prometheus/client_golang v1.25.0/go.mod h1:j2X/4/kaQHLPnmyEU8zjmkndo1T317VpCIQEml8LTKk= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.3 h1:O0jaTVAYNxTHYInEPFJt5I3+sN8zqBtVMPTB1qyxiEo= github.com/prometheus/client_model v0.6.3/go.mod h1:gpN5P9S7Rr6Yr92PiQ+Ixvhf6JZEkF1dnxsYL2aPBEM= @@ -830,10 +845,10 @@ github.com/redis/go-redis/extra/rediscmd/v9 v9.5.3 h1:1/BDligzCa40GTllkDnY3Y5DTH github.com/redis/go-redis/extra/rediscmd/v9 v9.5.3/go.mod h1:3dZmcLn3Qw6FLlWASn1g4y+YO9ycEFUOM+bhBmzLVKQ= github.com/redis/go-redis/extra/redisotel/v9 v9.5.3 h1:kuvuJL/+MZIEdvtb/kTBRiRgYaOmx1l+lYJyVdrRUOs= github.com/redis/go-redis/extra/redisotel/v9 v9.5.3/go.mod h1:7f/FMrf5RRRVHXgfk7CzSVzXHiWeuOQUu2bsVqWoa+g= -github.com/redis/go-redis/v9 v9.18.0 h1:pMkxYPkEbMPwRdenAzUNyFNrDgHx9U+DrBabWNfSRQs= -github.com/redis/go-redis/v9 v9.18.0/go.mod h1:k3ufPphLU5YXwNTUcCRXGxUoF1fqxnhFQmscfkCoDA0= -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/redis/go-redis/v9 v9.22.0 h1:laDvpYXTJtZLloinw1fA5Kqd6HAEH2XKxOkG/PDq2F0= +github.com/redis/go-redis/v9 v9.22.0/go.mod h1:y2g0Wj8rQvuK0ELM+oxSudcLtC09JScs98I/X9gRWY4= +github.com/rogpeppe/go-internal v1.16.0 h1:O9DK+vNMDVGLr2BeZqmpLeMjiMNkuXfcqntWbZV6S5g= +github.com/rogpeppe/go-internal v1.16.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs= github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rubenv/sql-migrate v1.8.1 h1:EPNwCvjAowHI3TnZ+4fQu3a915OpnQoPAjTXCGOy2U0= @@ -846,42 +861,38 @@ github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDc github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 h1:1EYB5IzjZawrrnELUi78f9fPu57HuXjmddZPjrls/28= github.com/santhosh-tekuri/jsonschema/v6 v6.0.3/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= -github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A= -github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk= -github.com/sassoftware/relic/v7 v7.6.2 h1:rS44Lbv9G9eXsukknS4mSjIAuuX+lMq/FnStgmZlUv4= -github.com/sassoftware/relic/v7 v7.6.2/go.mod h1:kjmP0IBVkJZ6gXeAu35/KCEfca//+PKM6vTAsyDPY+k= -github.com/secure-systems-lab/go-securesystemslib v0.11.0 h1:iuCR9kcMFD4QurdKrGvPLoKZLv9YvwPYVr0473BdtFs= -github.com/secure-systems-lab/go-securesystemslib v0.11.0/go.mod h1:+PMOTjUGwHj2vcZ+TFKlb1tXRbrdWE1LYDT5i9JC80Q= +github.com/sassoftware/relic/v8 v8.2.0 h1:9/L4S4I6an/JsPNhmcTpqfiOIsLb/iJaePZD1xR+ulE= +github.com/sassoftware/relic/v8 v8.2.0/go.mod h1:pZy7hLT9WCOKPonV8G/fplvtBLOZd6/kWtKqeHR6nKc= +github.com/secure-systems-lab/go-securesystemslib v0.11.1 h1:ayahDPjfSIqKegyt5YVGEvQ7SAi72GHXTy+b2YjUd5w= +github.com/secure-systems-lab/go-securesystemslib v0.11.1/go.mod h1:UyOjhoZLi76ir63u1ptubEevVe6dlNPmNxwgVvKqFFY= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/sigstore/cosign/v3 v3.0.6 h1:k8XaUd9pmLknHBst/v0rUGHVdB4D9cfaBmWUaMAOocE= -github.com/sigstore/cosign/v3 v3.0.6/go.mod h1:ckLRkVecfUCYxL8isHODY9lwyKmDaRCPn00p6yFxHg0= -github.com/sigstore/fulcio v1.8.6 h1:vkvRpdhVAZjZHa0ltiJeLSILG9U8ADJoe5Z+RdZSLJk= -github.com/sigstore/fulcio v1.8.6/go.mod h1:7RwoGdMM0xpc9qc382sjSw7P9OLKbKIwQZXvYz4JPb4= -github.com/sigstore/protobuf-specs v0.5.1 h1:/5OPaNuolRJmQfeZLayJGFXMpsRJEdgC6ah1/+7Px7U= -github.com/sigstore/protobuf-specs v0.5.1/go.mod h1:DRBzpFuE+LnvQMN10/dU6nBeKwVLGEQ6o2FovN2Rats= -github.com/sigstore/rekor v1.5.2 h1:k6pX4o1zFAzAvDbXiVIp5IHj1b0wcDaxsbsbNpuRO8o= -github.com/sigstore/rekor v1.5.2/go.mod h1:WkMnITBccOFauPkT6yte74tF5gC83pefKRGZvNOsbjI= -github.com/sigstore/rekor-tiles/v2 v2.2.2-0.20260601073857-5d098a2b6443 h1:/CO8F6m3Bo/f59bZo5dv1sTIfUnQqVnepIdDV24KoDw= -github.com/sigstore/rekor-tiles/v2 v2.2.2-0.20260601073857-5d098a2b6443/go.mod h1:w1h8wF8vq9lHjmtRdwJiEaoVxhP+WHIMpj4M39pkzp0= -github.com/sigstore/sigstore v1.10.8 h1:1Mgkxvkw4AXMfIP1DOjc6kw0GkUgA8pGVpveN/EfOq4= -github.com/sigstore/sigstore v1.10.8/go.mod h1:f9+B/4iaYimvUkySyb2mvc73n3RLqNn24grHZM/ET8M= -github.com/sigstore/sigstore-go v1.2.1 h1:YWP/rDbBaEBvtbkj6xtwsSj38ZCFEhTVVadNOXjVe3A= -github.com/sigstore/sigstore-go v1.2.1/go.mod h1:I8BqVwAb/SaQJ5pBu5IDFY+ksq8O/1/kCag8XUgrsko= -github.com/sigstore/sigstore/pkg/signature/kms/aws v1.10.8 h1:tofVQ+UWJgad/69I5zbqxdFCN5gpIn9tRQP7iBzIpBw= -github.com/sigstore/sigstore/pkg/signature/kms/aws v1.10.8/go.mod h1:73AfJE8H6w5KGCFPBu4x/OG+i1Yxgmh0L/FtV7prd88= -github.com/sigstore/sigstore/pkg/signature/kms/azure v1.10.8 h1:8Mt7J36GcUEmbiJaiFhz2tud5ZIgkfVVCe2H/WJCHmw= -github.com/sigstore/sigstore/pkg/signature/kms/azure v1.10.8/go.mod h1:YiTpAsxoWXhF9KlLOVWCh7BckN5cYO8X01WufDq1ido= -github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.10.8 h1:MxpAIMZVzn0Tpbarc9ax1I498oQBp7oYSMgoMSsOmKI= -github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.10.8/go.mod h1:bnAUEkFNam6STvkVZhptVwWzWR5pS24CEtQ+lhxu7S0= -github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.10.8 h1:1DGe4/clcdOnkz5MINEczWlmEvjUtZd+AjPPT/cBhQ8= -github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.10.8/go.mod h1:6IDFhpgxtzqbnzrFkyegbj7RfWwKeRrb3/+xAD1Wp+Y= -github.com/sigstore/timestamp-authority/v2 v2.1.2 h1:7DDhnknLL4w8VwomyvW2W8qblOS9LDR8oihna+jc7Ls= -github.com/sigstore/timestamp-authority/v2 v2.1.2/go.mod h1:o6rAVZceFyejClIj/uStRNIemP16bVMZtbMmhk6pr0U= +github.com/sigstore/cosign/v3 v3.1.4 h1:d3UHS8oJebMf6XUhPtWv9weU1ZYRyXq9HJ7QPccmqvY= +github.com/sigstore/cosign/v3 v3.1.4/go.mod h1:ZKgAdeS65uMLm3uoY8TiQGdzA8BoKYBSEIo2DEd1/4s= +github.com/sigstore/protobuf-specs v0.5.2 h1:RSWWUY8QrVTxbYH00jY/jg2e7YnjzrpwP+PeHTMll0E= +github.com/sigstore/protobuf-specs v0.5.2/go.mod h1:DRBzpFuE+LnvQMN10/dU6nBeKwVLGEQ6o2FovN2Rats= +github.com/sigstore/rekor v1.5.5 h1:nZKTBT0uRlKaFW4PafFIE7F5H26PInFF2qOlWouKYYA= +github.com/sigstore/rekor v1.5.5/go.mod h1:A0e/gTyJ5qCQn6/Zz2T0+TE+W3akuaLF5O0HJ4ogjgA= +github.com/sigstore/rekor-tiles/v2 v2.3.0 h1:HhMgH61UP0t899V8Fjt7pz1YdgOBptbaQdnCF+79cdc= +github.com/sigstore/rekor-tiles/v2 v2.3.0/go.mod h1:DEFiKSyQ4nF75QRVNdOPaIH3cmvMkO2B6xDZjNYngPc= +github.com/sigstore/sigstore v1.11.1 h1:kPqGeoOk3IGCJH76ssgX5PKMm8mbHNnM9fhKLDc6M8E= +github.com/sigstore/sigstore v1.11.1/go.mod h1:0jewOSMygmxdOaBIOhD1teBSIleg7RExQ1baacNrv4A= +github.com/sigstore/sigstore-go v1.3.0 h1:hnIMHREyCNTYFtOE1o7ae3Axa9B5W5EjUSBJICP2NBE= +github.com/sigstore/sigstore-go v1.3.0/go.mod h1:AyRQXfpH89py1twjE3kEZxlRersng90GSYqQV9zGJE8= +github.com/sigstore/sigstore/pkg/signature/kms/aws v1.11.1 h1:Xw+W6YvW/bQ3zSAYK6hsOQplGz/X9yOvCGeQLfkNy4c= +github.com/sigstore/sigstore/pkg/signature/kms/aws v1.11.1/go.mod h1:agtjJMPuI6q2rBTM0XZiOFsZjlcZnTi7xkBFQt0NISY= +github.com/sigstore/sigstore/pkg/signature/kms/azure v1.11.1 h1:UfIJOml1Y9uJwTy8cDbP7a6x/0By+fXIV7dyUHvEZlA= +github.com/sigstore/sigstore/pkg/signature/kms/azure v1.11.1/go.mod h1:xQ5RXq7EDorvH2+1cNQLXQUjXVlhz64q+CZf5Jol8FU= +github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.11.1 h1:Ab4K/QqBmlmOu5X05qLTeQnPblVqqzrC//F7J/imzoI= +github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.11.1/go.mod h1:db/U8Hp9wrarN12CE99SdDEef9FCsgyGTD2gaFRfep4= +github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.11.1 h1:WIgn5z+Wx9ZM8MPTZlMIJ5YXZq36VrcYXFfCCokMqGc= +github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.11.1/go.mod h1:byZFStGl4JcpWux75DD7gmpYyYjyy2BmkjsJhdSA8UE= +github.com/sigstore/timestamp-authority/v2 v2.1.5 h1:VryyxIBepZ2ALtvkAAjBf9IF33iI6wX7ifiVWeBVhog= +github.com/sigstore/timestamp-authority/v2 v2.1.5/go.mod h1:/rLXKc3T8+DtQYGqVNoCw/Z4OOcEe8Y8ED8/dXetaWw= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.10.2 h1:G2SED73/qrAu6YwbdxOD6peLkCBI3z7L+ykJFTXJBBo= github.com/sirupsen/logrus v1.10.2/go.mod h1:SLEg8TqYulVKKfIGHldVp2K2aYz2DKSVBq4g/H5bR7Q= @@ -903,8 +914,8 @@ github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= -github.com/spiffe/go-spiffe/v2 v2.7.0 h1:uXe1MflJoHw58wAUvxVlcM7WpKtijWG7I1UidcGh6g4= -github.com/spiffe/go-spiffe/v2 v2.7.0/go.mod h1:47Q0Q9/AqGha8QLHp+kxpH4Wca7X7EnOtlIJy3mxZ3U= +github.com/spiffe/go-spiffe/v2 v2.9.0 h1:g5qTg3qIP1HXV+GzuyEfSJFVxUCpEP5Ui1OINVJHPB4= +github.com/spiffe/go-spiffe/v2 v2.9.0/go.mod h1:w2CLWKLMTX/PPYUEUPv3ltH0RXsw5S8suwNF46w9/Aw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -939,20 +950,18 @@ github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gt github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI= github.com/theupdateframework/go-tuf v0.7.0/go.mod h1:uEB7WSY+7ZIugK6R1hiBMBjQftaFzn7ZCDJcp1tCUug= -github.com/theupdateframework/go-tuf/v2 v2.4.2-0.20260407074541-7e8f69f906ef h1:jJac5InhEfD0Z46/d5RayZjoavf/se7bPZpOgg8GLrM= -github.com/theupdateframework/go-tuf/v2 v2.4.2-0.20260407074541-7e8f69f906ef/go.mod h1:cLUSJ2cgR194lNWfp+TJT4P8PX7qGleCXdudqlCMtOE= +github.com/theupdateframework/go-tuf/v2 v2.4.2 h1:w7976/W8uTwlsegP5nRymlpjPgrwSh+AXUf85is6nJk= +github.com/theupdateframework/go-tuf/v2 v2.4.2/go.mod h1:JqBrIUnNLAaNq/8GmBcEMFWfAFBbqp/MkJEJseXKbks= github.com/tink-crypto/tink-go-awskms/v3 v3.0.0 h1:XSohRhCkXAVI0iaCnWB/GS05TEmpnKurQmzaY1jzt3Y= github.com/tink-crypto/tink-go-awskms/v3 v3.0.0/go.mod h1:+7MXsShLzVbSQ6dI0Pe4JuZM52jD1jQ1itAygd/MDsA= -github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0 h1:3B9i6XBXNTRspfkTC0asN5W0K6GhOSgcujNiECNRNb0= -github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0/go.mod h1:jY5YN2BqD/KSCHM9SqZPIpJNG/u3zwfLXHgws4x2IRw= +github.com/tink-crypto/tink-go-gcpkms/v2 v2.4.0 h1:xpI5pnEQ0erFCS3JmcEl7Blluo8ZieEPNOURqy9uD70= +github.com/tink-crypto/tink-go-gcpkms/v2 v2.4.0/go.mod h1:wDDAhjfd1t4TjeJCSsmFK7CARPUs/ITi16ZZ8kmAaFQ= github.com/tink-crypto/tink-go-hcvault/v2 v2.5.0 h1:eXuNqgrcYelxU1MVikOJDP3wTS5lvihM4ntoAbAMfvs= github.com/tink-crypto/tink-go-hcvault/v2 v2.5.0/go.mod h1:3RhcxAqek6xUlRFmJifvU4CYLZN60KMQdIKqpZAZJG0= -github.com/tink-crypto/tink-go/v2 v2.6.0 h1:+KHNBHhWH33Vn+igZWcsgdEPUxKwBMEe0QC60t388v4= -github.com/tink-crypto/tink-go/v2 v2.6.0/go.mod h1:2WbBA6pfNsAfBwDCggboaHeB2X29wkU8XHtGwh2YIk8= -github.com/tinylib/msgp v1.6.1 h1:ESRv8eL3u+DNHUoSAAQRE50Hm162zqAnBoGv9PzScPY= -github.com/tinylib/msgp v1.6.1/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= -github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= -github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= +github.com/tink-crypto/tink-go/v2 v2.9.0 h1:uvjgS1IYDB4zhDK0Zow8ZFB73IjzMAoSqW0JkFnvkrc= +github.com/tink-crypto/tink-go/v2 v2.9.0/go.mod h1:aNXZeyxjQU9iqAeARRNmbESXUW6Mao1HRCCTY8B1TFM= +github.com/tinylib/msgp v1.6.4 h1:mOwYbyYDLPj35mkA2BjjYejgJk9BuHxDdvRnb6v2ZcQ= +github.com/tinylib/msgp v1.6.4/go.mod h1:RSp0LW9oSxFut3KzESt5Voq4GVWyS+PSulT77roAqEA= github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= @@ -970,12 +979,12 @@ github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= -github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ= -github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns= +github.com/ysmood/fetchup v0.3.0 h1:UhYz9xnLEVn2ukSuK3KCgcznWpHMdrmbsPpllcylyu8= +github.com/ysmood/fetchup v0.3.0/go.mod h1:hbysoq65PXL0NQeNzUczNYIKpwpkwFL4LXMDEvIQq9A= github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= github.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18= -github.com/ysmood/got v0.40.0 h1:ZQk1B55zIvS7zflRrkGfPDrPG3d7+JOza1ZkNxcc74Q= -github.com/ysmood/got v0.40.0/go.mod h1:W7DdpuX6skL3NszLmAsC5hT7JAhuLZhByVzHTq874Qg= +github.com/ysmood/got v0.42.0 h1:CMRoknemO2nkZBpZmRgsWs79UoQz5eDfWmKIAGGFm94= +github.com/ysmood/got v0.42.0/go.mod h1:uFF8sPWgVvWIGrjASUgtH0AbnU7ipaXGVaFtWdl3yP0= github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE= github.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg= github.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU= @@ -986,8 +995,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= -github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s= -github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI= +github.com/zalando/go-keyring v0.2.8 h1:6sD/Ucpl7jNq10rM2pgqTs0sZ9V3qMrqfIIy5YPccHs= +github.com/zalando/go-keyring v0.2.8/go.mod h1:tsMo+VpRq5NGyKfxoBVjCuMrG47yj8cmakZDO5QGii0= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= @@ -997,64 +1006,64 @@ 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/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= -gitlab.com/gitlab-org/api/client-go v1.46.0 h1:YxBWFZIFYKcGESCb9fpkwzouo+apyB9pr/XTWzNoL24= -gitlab.com/gitlab-org/api/client-go v1.46.0/go.mod h1:FtgyU6g2HS5+fMhw6nLK96GBEEBx5MzntOiJWfIaiN8= +gitlab.com/gitlab-org/api/client-go/v3 v3.13.0 h1:CTUXvcL6OrGjYNafbws5C1lJwlXrxGJHWQGMF+hCdz0= +gitlab.com/gitlab-org/api/client-go/v3 v3.13.0/go.mod h1:k7uYxRoIeuSqyWZSJfAPZlf0L32OH0lUBImAl4gWUFc= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/bridges/prometheus v0.70.0 h1:qU2CqTGdlstwoVhu1WfjJJ3z2ntcNjTJO0ksTsFKzPI= -go.opentelemetry.io/contrib/bridges/prometheus v0.70.0/go.mod h1:Ekh3I2XXfhdWkqbRq4PrivJS4BS/se7Er9ZsbK6YEtQ= -go.opentelemetry.io/contrib/detectors/gcp v1.44.0 h1:NmLfL734pJhM0JKaYd2Y28+nY9dPRWYAAbxhRCrKXPw= -go.opentelemetry.io/contrib/detectors/gcp v1.44.0/go.mod h1:tNAsgd8avTGke1+MndXlU5Cru4PQ9Ai/cCNWQv/ZJ/s= -go.opentelemetry.io/contrib/exporters/autoexport v0.70.0 h1:wpCLEJ/4RHUadR11UOdznbmyyih5/OPYFcsehAh6PYI= -go.opentelemetry.io/contrib/exporters/autoexport v0.70.0/go.mod h1:x7MbNOwoKV5Hj6uYMXQksHlQdTNOP3hoFPvqWISiu6s= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.70.0 h1:oECp5f+hN7nkwjU/8BxQ/q23bGPb8FIrD839owX222E= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.70.0/go.mod h1:DqEFwLumhzMBDQv9PcWbyoDxHI/4lAk6CM4nJBH39sc= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0 h1:LMuyCAyfalSjDyjdC65nK6N0zoTT63+E/u95X0JovZI= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0/go.mod h1:085m8qbm4hgc8rZWGDEa4vmyyo2c3nPxUslYUKUIU04= -go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU= -go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.21.0 h1:WseeVYf5dJZTsyPiyW5L14k5qsSibqXAMTSiFEDiWr0= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.21.0/go.mod h1:SiLZnQS6Qk2eCpvr2CH/XMAOa64TWGXxEZJZCpD2Lmc= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.21.0 h1:fvNHGyo3CdRv/DQveXqhqBxnKTDyRaC5sMSQxilX/A0= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.21.0/go.mod h1:zyGrjRKL2B/6+Jc/m4/otPoZqV2MY9ZjC/aBraRO7zc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.45.0 h1:klTViGcsvLCd1xN3rZzfZ12NslC/OimbmR+k+A006RI= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.45.0/go.mod h1:jRsK04CWmXuY8A0O+wMpSf+t90RHZ53o5Qmxn2PQPfk= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.45.0 h1:pnxy6c/kvNBWdNNFzqpjuJLm9Hjhgk/Q0nY221rwuk0= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.45.0/go.mod h1:qw6YsFapotRwoDhXRZvljzaOvCQB7UfnafEJagpN2TA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0 h1:QRefszxJmfPdjXUUm3j6iDzY03mTPXMjqErFqQ67vUg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.45.0/go.mod h1:Tiz03lTBVBrm7eWZBOidzEaYaJa8tjwGUGv6d8mlTyk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.45.0 h1:fG5MCxGz8+2VtrN/WgqSpJFctVz24gpxj8CxkKmc8Ww= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.45.0/go.mod h1:BmAYTn+3ysbRe+IU2msxmf5Rx3g6DHvex+tWI3LdhYI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0 h1:QBajQ2SrwQijzHyZbQlPsuIzpl/ll8DY6wPWsajeGcI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.45.0/go.mod h1:08ZQLjrPLQ6R4kAXvuOvODEer5Yh4CoFvll5qB2BCI8= -go.opentelemetry.io/otel/exporters/prometheus v0.67.0 h1:7IefDa35e6V3NoiqIeLDMDxMFyZDk5qcoC0Ax4cC16E= -go.opentelemetry.io/otel/exporters/prometheus v0.67.0/go.mod h1:nsPI1awTg5Vmg1YrommL2mVarVGlqc4yXOoKAkPRD0c= -go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.21.0 h1:2lpf4hnrasYIsUyEXwnTZq5lsxrMm4T2Bwb06IctAZQ= -go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.21.0/go.mod h1:YWOW6h7jwApz9Pl76ie/izUsSPj0s2MdIlpqbPqaf3U= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.45.0 h1:dm9iyzn6tioYZtwqaiBSU0TSI8Yu/8dTIbfG0+B49DY= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.45.0/go.mod h1:xAvxYjYK28qvt+yu4BYZ/zMmAjwMXINXD6JiMyeB8iI= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.45.0 h1:lsA/S1bxgdbyFGkTj+3meEdJ6ADVU7QoFstV6MXgE68= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.45.0/go.mod h1:L7u+MirGoB1bjeLH66+xDykF4RC8C3RN7lIFpBiewUo= -go.opentelemetry.io/otel/log v0.21.0 h1:SLsVDGmtyBrdw8/a2Z0bOIxou/+bN4z56GebH7T0LvA= -go.opentelemetry.io/otel/log v0.21.0/go.mod h1:iReetQrZL9Wyg84cCkOoCmqDHS5RCFfyxC7J+r8fn8g= -go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M= -go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s= -go.opentelemetry.io/otel/metric/x v0.67.0 h1:PcicCNZFkZ4bXfSooXdo3WN7RBOVOtjVdo1wD358Uns= -go.opentelemetry.io/otel/metric/x v0.67.0/go.mod h1:FBjCWZe6wgcqxcMtjdGiClDKXb2YxxXii0CXftE4QtI= -go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw= -go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA= -go.opentelemetry.io/otel/sdk/log v0.21.0 h1:QsE7XSR0ktQdKmRKGnR+f1ObGF32WG+7MER/P9KgmYc= -go.opentelemetry.io/otel/sdk/log v0.21.0/go.mod h1:m9mApjCoD2/1QuKCAptjv+BrG9WKOvQLVdNx+iBldTo= -go.opentelemetry.io/otel/sdk/log/logtest v0.21.0 h1:X+JBBgKlswCGYsmgL0CnoUUtlE//VB345c84jYAYkdQ= -go.opentelemetry.io/otel/sdk/log/logtest v0.21.0/go.mod h1:HD1575K8e6sIFBBDd5tZB3t9DlMytWXq9FuR+Y4rfjE= -go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o= -go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA= -go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag= -go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc= -go.opentelemetry.io/proto/otlp v1.11.0 h1:5rrYs0Ykyj50sdU/JU0x8etU+LubXWb+gED6TbEdMIk= -go.opentelemetry.io/proto/otlp v1.11.0/go.mod h1:SmVizdCOAm3XBtG1g1NnOdhW6jtddT72hLMhv8VwA8E= -go.step.sm/crypto v0.81.0 h1:e+ouzpNt3Xm4dp7HGXhgYB5y4iFik3vh3phHKWmvugU= -go.step.sm/crypto v0.81.0/go.mod h1:fsTizqQeASjTXnbv9O00XtRlIuXRkCdoRiJNyXGQujc= +go.opentelemetry.io/contrib/bridges/prometheus v0.72.0 h1:OUIUu7ovHCEvG4IE5LQd2PNBH2eVYh50w/1ROwHEM7M= +go.opentelemetry.io/contrib/bridges/prometheus v0.72.0/go.mod h1:pCrnkC07AZ5+h+0A0Z/1GYyqB+xu3A8LaEEhxvv9s6o= +go.opentelemetry.io/contrib/detectors/gcp v1.45.0 h1:9jR0ZPRok9ryaOQ2Wx8rg5F7Aon59mxrqbVI60/vlBk= +go.opentelemetry.io/contrib/detectors/gcp v1.45.0/go.mod h1:VSme3o2fvSg5bVg0dRzyHaj4Z5EVhG+g2Fde6LKzmQA= +go.opentelemetry.io/contrib/exporters/autoexport v0.72.0 h1:TPXrl7vEkmh69tp7dHTpz+Y6AUenFg1Du3Nx3a6dlxY= +go.opentelemetry.io/contrib/exporters/autoexport v0.72.0/go.mod h1:0jE6K8nsOgeiF53rcEhy71cgakek+XEWi4OPROUqLoI= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.71.0 h1:B2h3uqicet1CT2N5TOFhS+Gq++9i0/CLmaxvhmhtP5s= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.71.0/go.mod h1:dylvB+ZiiwMvsDij9O84Uy7SijLgHMX4mbkncds+4Sw= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.72.0 h1:LxwW/9ctSCv+QkE/cLR7M91ZIkXNMqJtEMi1vCw9U8s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.72.0/go.mod h1:tOsftB4SslBwwErVEPaenU2RpThXWPIU8DoJHEC4dyw= +go.opentelemetry.io/otel v1.47.0 h1:j7ALJ/zgkS7Z6aeJW09p8VC9804bC+PpeTfCD4XPnOM= +go.opentelemetry.io/otel v1.47.0/go.mod h1:8wS9O2qfXrYrzp6hIF/HOYJJf/wIhFPhR2xLuP+iXQU= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.23.0 h1:5Ek4qQ5XX0PSQfY7lMW3CvcR36yy8YFDfGnYnaku8zE= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.23.0/go.mod h1:NUKIMOKSexQdTmS1pSxOQBDiVes3DHRier2ePsGW/gQ= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.23.0 h1:5WVp40k2fJHuc5BtNTgKk+EugKFT8Eg3lywFfCPI/nE= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.23.0/go.mod h1:59nH0RYCGHRlwt15WyioeSYINBbuObeBpTowPhEKWHY= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.47.0 h1:LaLVGJtIzHcvHjVs2OftmKcc+M9mdOqS8vZ1fBD1Yes= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.47.0/go.mod h1:vF6H5sNDuQBJWgSX00iZgbIqk2MZZoVMH0ar5t5bOpw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.47.0 h1:7kszRN4Ze5GeAkUnBjBSUIunVBMA+mZIG04auueqguM= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.47.0/go.mod h1:CLzakYy8jOdkBfcveCWYkeKJxujcF4xPhh2Z7IfE98U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.47.0 h1:julhjPeUH/q/7hinbSdDdqt5h7Zw9YWmRlWRhI0jd54= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.47.0/go.mod h1:Ao2mz688LH/tFf0yMAenidq6k2YNSx6SIY2q6jDACck= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.47.0 h1:UFpxOpYPmMNUtOWhdb+nC1WELIgVf8z9higURrbzYU4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.47.0/go.mod h1:YBGjxe3lt0jtXoEXxGrGhv/jM7oUBUDVK7zhAvNyEjc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.47.0 h1:aXZXsZ012wOgVkqaiQv+Z/d8V0xKjmg70F6m4CE94lc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.47.0/go.mod h1:ziu1gUIJhAaxM3EV1e3Ralk5AAyL1UOYz3lJJ4VSI38= +go.opentelemetry.io/otel/exporters/prometheus v0.69.0 h1:LQVBTGnHvzrfGJpDoCSVTY7eaLZArvaTSq5l84Skxew= +go.opentelemetry.io/otel/exporters/prometheus v0.69.0/go.mod h1:moif0nlgj/M16NRT8lDZVt8MC52+EOAJfR8EKRJ8LyU= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.23.0 h1:PW/E9Tw6ZykC88pey0MZY8hKOUeEw+hX6PU+oXA4A9o= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.23.0/go.mod h1:hUw4VNfiOnQ/yzGgy6ZdWVnudXw7iRfImhd6FjXOnvI= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.47.0 h1:ww5DYBpMKcqLdxjWrhGUIh6O+ZfvZUaMk4niVyNQPEQ= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.47.0/go.mod h1:Av5t+Ezx5S/Ks2dsEfWhny+GK+wrZ1K7NZ6aHSWnX3U= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.47.0 h1:N3YQCxjxQ/bMjyc3heladfRm9t9RTksGQH8z4w6yU/0= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.47.0/go.mod h1:Mp8HOFqcaUyypCuGv9IhDdTHnJ56lSudSHMd+pVSCEA= +go.opentelemetry.io/otel/log v1.47.0 h1:cOTS1CcLbSQeZKanGJ+0JpF/+t4PELi3O3bbl2lqCcI= +go.opentelemetry.io/otel/log v1.47.0/go.mod h1:9byitSQ5pLC6PpqwGXjqdMKya6ZTswHRZh2vvXT33nw= +go.opentelemetry.io/otel/metric v1.47.0 h1:4PptaldXx3Eat1XjMZ68pPJEs5wrhlemctZE9a3UdWY= +go.opentelemetry.io/otel/metric v1.47.0/go.mod h1:ADGSXxRrXM6bjbvLo535EstVFlPpPYZm4LBKixjDHwU= +go.opentelemetry.io/otel/metric/x v0.69.0 h1:DjRLr15H83v+hCW7JA9NoJvOkYTtmq5YoDRbe9deYpM= +go.opentelemetry.io/otel/metric/x v0.69.0/go.mod h1:uVvsMPMFFyj/HUQfrUnH3JjnOQ1dwFDorgFLRBasM0k= +go.opentelemetry.io/otel/sdk v1.47.0 h1:zWXEr4j2lFefG87TU6Yg8a7ngfohIKFZHKp0Hf5hC6I= +go.opentelemetry.io/otel/sdk v1.47.0/go.mod h1:VUc24kiOeoGsxG8G9ULx3fWKvB7jMhnGE8Oi607lgR0= +go.opentelemetry.io/otel/sdk/log v1.47.0 h1:W4N/ZfgpkRRnmARnqYoQ6BrWA0mrj8IzVDNLlpmUJZY= +go.opentelemetry.io/otel/sdk/log v1.47.0/go.mod h1:Wu74Z2EmOKXi2FleSNAb0Hyau2G+r5kubSEMJw5aAMs= +go.opentelemetry.io/otel/sdk/log/logtest v0.23.0 h1:j3pm42QCrKYYLLkI6OrV1QHFkUfDA6/4EP+Y1PzggLg= +go.opentelemetry.io/otel/sdk/log/logtest v0.23.0/go.mod h1:zt0gEnuTZgDvqvczHwYswz93eqMaIr6upTTsdV/HUlU= +go.opentelemetry.io/otel/sdk/metric v1.47.0 h1:lfISg2j93VT6yqdk9OfUaZmw/GfcZqCCV3jdXtsPnKw= +go.opentelemetry.io/otel/sdk/metric v1.47.0/go.mod h1:ypLp+mW1Nt2x+Szt3b5/i1syodyts49lMOwxpDI3VGw= +go.opentelemetry.io/otel/trace v1.47.0 h1:JOjX/Oci8K94QHddo+bbfya/Ai/nf6/dt9ZfrFNWSrM= +go.opentelemetry.io/otel/trace v1.47.0/go.mod h1:jNaSLa2PZEYFG6fRjJABAu+bw4FS08uDmPg28lTghu0= +go.opentelemetry.io/proto/otlp v1.11.1 h1:SCrPqH9NE5CmBrOqpjNCiT0X5mGwMyGERXdogfR7Yjw= +go.opentelemetry.io/proto/otlp v1.11.1/go.mod h1:wq8W4aSf6bOy8RZyESOeQFr9Pu7wydWX/TY4sgvrTPg= +go.step.sm/crypto v0.91.0 h1:0mN0DwVOvUuh7VbyTnxABZuAkxwak/Grp8Y/b4YaZDU= +go.step.sm/crypto v0.91.0/go.mod h1:NxxObRBymdbyXLoTCW5Ea6sLxuy5yI/xr9+hSBlbU/Q= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -1078,16 +1087,18 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.58.0 h1:COkYLr4k7nDI4r6QtMML2AfzeIswWHAJthLS6K/M/54= golang.org/x/crypto v0.58.0/go.mod h1:Xh+kl5A+M0gMiWDytH2Hhr5Z7WbNCpv/eB/EFgfSk+w= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba h1:Ck8QetSgk912qxWLMCKxd0in+aiyBQyDSMae6e/xmpU= +golang.org/x/exp v0.0.0-20260908205506-85c1c2202aba/go.mod h1:50RgIsmK7OwqzTTeqcSXQW8SswW0o8fRcDxmqGluJ8E= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -1098,8 +1109,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c= -golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o= +golang.org/x/mod v0.42.0 h1:ICeL3t2L30ljSPHHxDnR4fqVcBt/zR2zJAb9juavmQg= +golang.org/x/mod v0.42.0/go.mod h1:u5fzIrRInkL8MaO1cbRkUHT64v303YIT5KEqpNr7jRs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1122,11 +1133,11 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/net v0.61.0 h1:RR+6j/BTBCrggOLugxa+66R8FvoN6MHlzDlZIbN9YFw= golang.org/x/net v0.61.0/go.mod h1:WgDCOTH8iwtB66T4/GfI94AHo/hhdb7Hah9NDseQfiI= @@ -1172,14 +1183,13 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.49.0 h1:XbzkgYJHdqh/8m2Uu0W/dQv8nktxx4BFHp1M0gROTXA= golang.org/x/sys v0.49.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= @@ -1188,12 +1198,12 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/term v0.47.0 h1:cpyOwQkr2Nzxe2p6KnrTG7Ei2wtzjsuSjhpXXepZ/BM= golang.org/x/term v0.47.0/go.mod h1:efAfAuG6RKABvg1ArGebRkvX6LXul14SM67o2e4mUCY= @@ -1204,15 +1214,15 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/text v0.43.0 h1:1QivrlhwAsnMOcqgOdl9my7s9OyZl8O5/UGUTJVXoWc= golang.org/x/text v0.43.0/go.mod h1:hbSIYA/amXcRXTFSZqh8tWHEoqRvvbKPJ2uz/KU+a+s= -golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= -golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= +golang.org/x/time v0.16.0 h1:vMb6ptszcQMkcwiRTAuNNU50gom6++Q/6gY2hDM6VDE= +golang.org/x/time v0.16.0/go.mod h1:rVKOqvZeKvrDKTQiAHJ7wmwP0RzleSphoEA9RcdLA0s= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1237,24 +1247,24 @@ gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0 gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= -google.golang.org/api v0.297.0 h1:WktxTsnnx0yZNnsR6j0q6hR21RnnK81FHTOPy/ux4OE= -google.golang.org/api v0.297.0/go.mod h1:S4m8x0M6OkQpkOzGk1y9JG2sm4fFQrMh6dxzjCTszhE= +google.golang.org/api v0.308.0 h1:iGb+4qT3nVb3uuIzwor3yP2nyu+rMZr31PvLFiRNJQk= +google.golang.org/api v0.308.0/go.mod h1:4XMH+b/ZEfuTbK7FnrFRY9c4xfFHVflGTKuy/i5wUmE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20260715232425-e75dac1f907d h1:C9v1o0/4quuhOAfmRXA2j+we0PqZIp8traLdeogF3Ms= google.golang.org/genproto v0.0.0-20260715232425-e75dac1f907d/go.mod h1:Wz2wFJntZFmLGo7pLDXZ3wYk5hyc0Mb+SkHhDDXT+lU= -google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d h1:FarXi840EJWSHYTN3ERkADbPWjl307+FGrA22KAVjjc= -google.golang.org/genproto/googleapis/api v0.0.0-20260803160001-6ac0973c030d/go.mod h1:K/+WGbmBY7aNW1HDw1fJnKYo10i0DkAX6pows00dLig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688 h1:cYNAzI2sUwhmCcoj9TxvihSrqsxt6uIkj3rDRhSDmW4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA= +google.golang.org/genproto/googleapis/api v0.0.0-20260928230214-8a89bd6388cc h1:rZHRz0ogq4Pid1IKtA/ivHi8XONVF5tqUQXA2TtgpkE= +google.golang.org/genproto/googleapis/api v0.0.0-20260928230214-8a89bd6388cc/go.mod h1:K/n0XLazCJHjD73YuMyNJsN38BPPy1kfFnKxUXaThFU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20261019180522-ce908d29299b h1:6WNWHZk91dp4gZ25642OALkuG1UVNZbwENJD19DnB8k= +google.golang.org/genproto/googleapis/rpc v0.0.0-20261019180522-ce908d29299b/go.mod h1:OaIUM3+LpYcK2GXM4FTmhWoIq371Owdr+Cc7/BsYHHc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU= -google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8= +google.golang.org/grpc v1.84.0 h1:soMyaPJ8pAak5PIQ0DGBUir0XRo2fRoMqhNWMLlLxO0= +google.golang.org/grpc v1.84.0/go.mod h1:ljCht0DrxQrXBDRTZp52Qxh3Ffk8CdYm2sj4O2QN2C0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -1276,8 +1286,9 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.67.2 h1:JtOSMb9OuaCZKr7h5D/h6iii14sK0hLbplTc6frx4Ss= -gopkg.in/ini.v1 v1.67.2/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.3 h1:iM9Lhz5MRSGhHVGGwCuzG9KO8PoirCXj/m/qTmOJJQw= +gopkg.in/ini.v1 v1.67.3/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= @@ -1292,8 +1303,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= -helm.sh/helm/v4 v4.3.0 h1:wLRTNXzy96ro7waurWMsymlUPaCQOj5nokpJZJNen/w= -helm.sh/helm/v4 v4.3.0/go.mod h1:p6SMo6BMyg+4H52fJXvRNg1To0a0KGfiPxjVEcHHSk0= +helm.sh/helm/v4 v4.3.1 h1:UndCoLe9UPmNoL7IvWzaUXveL7T5t/ijoorEmgJdI9g= +helm.sh/helm/v4 v4.3.1/go.mod h1:Z663D/wNQG1UsG3X7svSEBPvosFVel4SIw4YIMZwmuY= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.37.1 h1:l6N77U7tjwB5L056bgrBTJIEdevac/naBZ3iSvDNfpM= @@ -1318,8 +1329,8 @@ k8s.io/kubectl v0.37.1 h1:n1VCIntOJiX943uByXCThSZQmE+4v9jGS8S9u94/aHU= k8s.io/kubectl v0.37.1/go.mod h1:66cc4Bz8PxBTxlrHzBuzXEzGIytAnNasKQPeEcpuaV8= k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3 h1:jVkFFVfXdXP74B/zbO3hM3hpSFD0xvhQ5U686DPurkE= k8s.io/utils v0.0.0-20260707023825-cf1189d6abe3/go.mod h1:M2s5JB1lIYP3jzZdorPLHXIPJzt9vv2muW5a6L9DtNM= -oras.land/oras-go/v2 v2.6.2 h1:N04RXngAp1LJKTG6ifz3xHPipasEkWr+hFmInja5YKo= -oras.land/oras-go/v2 v2.6.2/go.mod h1:PlTtg4JTDJkDe8yVHpM2wz7/YDc00GVas+i4jAW2TZ4= +oras.land/oras-go/v2 v2.6.4 h1:l4uv+UT7QK4Hju+KOEZ/c8iOjq3iZH7cULoRpUnSDAU= +oras.land/oras-go/v2 v2.6.4/go.mod h1:imXEFiYkTZUVVYkfv/r5GVmWeiLkGtNQA8FU4F/sjKs= sigs.k8s.io/controller-runtime v0.25.2 h1:bEkK3PVOIVK9X8QWLGVhJgmFc++47vfT6wakSzAOLsQ= sigs.k8s.io/controller-runtime v0.25.2/go.mod h1:4QqLdT6z/L6Olj8JJCtvztid4/fnIiYsfaTFScegctc= sigs.k8s.io/json v0.0.0-20260909141634-11ed52e25bc5 h1:k9jo7ED/fsfOcz3L1P20P5tjTNtsdQNwMOG2aKzHrl8= @@ -1330,11 +1341,11 @@ sigs.k8s.io/kustomize/kyaml v0.21.2 h1:1javwStFk7cgOeLU7yJtPmXcgMEhQgC2X0WjFT6U0 sigs.k8s.io/kustomize/kyaml v0.21.2/go.mod h1:zX3qwtuouXd2K1fMiCV0VSFReX06a+CY1rhyf5Dy7hQ= 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/release-utils v0.12.4 h1:kuG6WTWGCKx5uUrJwl2uFErOKOw+4Ba8WrPmOQh5J3g= -sigs.k8s.io/release-utils v0.12.4/go.mod h1:Tc3iM9DVM3W9oJu/6rEI+LnREuhy8lZ7wInQhRBtUoo= +sigs.k8s.io/release-utils v0.12.5 h1:/Ybntn/M7sK+6XjaH88y3pfx3tpJod3WKiItxoS4igg= +sigs.k8s.io/release-utils v0.12.5/go.mod h1:rL6dEVB93VNU8Bd3l8tnh98hR9yoqMMn/puxlCU5buw= sigs.k8s.io/structured-merge-diff/v6 v6.4.2 h1:qdOxHwrl2Kaag1aQEarlYcOA9vSyGCp3CIki3aW8c4Q= sigs.k8s.io/structured-merge-diff/v6 v6.4.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= -software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k= -software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= +software.sslmate.com/src/go-pkcs12 v0.5.0 h1:EC6R394xgENTpZ4RltKydeDUjtlM5drOYIG9c6TVj2M= +software.sslmate.com/src/go-pkcs12 v0.5.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= diff --git a/hack/ci/oci-zot-workload-identity-e2e.sh b/hack/ci/oci-zot-workload-identity-e2e.sh new file mode 100755 index 000000000..e5b71d742 --- /dev/null +++ b/hack/ci/oci-zot-workload-identity-e2e.sh @@ -0,0 +1,993 @@ +#!/usr/bin/env bash +# End-to-end test for OCI registry authentication with Kubernetes ServiceAccount +# tokens, using Zot's OIDC bearer authentication. +# +# It creates a dedicated kind cluster with a ServiceAccount issuer reachable +# from a Zot container on the kind network, deploys source-controller, and +# verifies that an OCIRepository using the generic provider authenticates to +# Zot with a projected ServiceAccount token. It also installs SPIRE with the +# SPIFFE Broker API and verifies the same using JWT-SVIDs. +set -euo pipefail + +IMG="${IMG:-test/source-controller:latest}" +ZOT_IMAGE="${ZOT_IMAGE:-ghcr.io/project-zot/zot-linux-amd64:v2.1.22}" +CLUSTER="${CLUSTER:-zot-oidc}" +ZOT_CONTAINER="${ZOT_CONTAINER:-zot-oidc}" +ZOT_PORT="${ZOT_PORT:-5000}" +ZOT_HOST_PORT="${ZOT_HOST_PORT:-5050}" +AUDIENCE="zot-registry" +NS="source-system" +APP_NS="zot-e2e" +SA_NAME="zot-puller" +SA_NAME_B="zot-puller-b" +WRONG_AUD_SA_NAME="zot-puller-wrong-aud" +REPO_A="zot-e2e-a" +REPO_B="zot-e2e-b" +OCIREPO_A="zot-oidc-a" +OCIREPO_B="zot-oidc-b" +REV="test" + +# SPIFFE Broker API test setup. +SPIRE_NAMESPACE="spire" +SPIRE_TRUST_DOMAIN="example.org" +SPIRE_SERVER_IMAGE="${SPIRE_SERVER_IMAGE:-ghcr.io/spiffe/spire-server:1.15.5}" +SPIRE_AGENT_IMAGE="${SPIRE_AGENT_IMAGE:-ghcr.io/spiffe/spire-agent:1.15.5}" +SPIRE_OIDC_IMAGE="${SPIRE_OIDC_IMAGE:-ghcr.io/spiffe/oidc-discovery-provider:1.15.5}" +BROKER_SA="default" +SPIRE_BROKER_ENDPOINT="dns:///spire-broker.spire:8443" +SPIRE_BROKER_ID="spiffe://${SPIRE_TRUST_DOMAIN}/spire/agent/k8s_psat/broker-cluster/" +SPIRE_OIDC_ISSUER="http://${CLUSTER}-control-plane:8082" +JWT_SA_NAME="zot-puller-jwt" +JWT_SA_NAME_B="zot-puller-jwt-b" +JWT_OBJ_TYPE="ocirepositories.source.toolkit.fluxcd.io" +JWT_OCIREPO_A="zot-jwt-a" +JWT_OCIREPO_B="zot-jwt-b" +JWT_OCIREPO_A_CROSS="zot-jwt-a-cross" +JWT_SPIFFE_A="spiffe://${SPIRE_TRUST_DOMAIN}/zot/ocirepositories/${JWT_OCIREPO_A}" +JWT_SPIFFE_B="spiffe://${SPIRE_TRUST_DOMAIN}/zot/ocirepositories/${JWT_OCIREPO_B}" + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +WORKDIR="$(mktemp -d)" +CREATED_CLUSTER=false + +log() { echo -e "\n\033[1;34m==> $*\033[0m"; } +info() { echo " $*"; } +fail() { echo -e "\033[1;31mFAIL: $*\033[0m" >&2; exit 1; } + +cleanup() { + local code=$? + if [ "$code" -ne 0 ]; then + log "FAILURE (exit ${code}) — dumping diagnostics" + kubectl -n "${NS}" logs deploy/source-controller --tail=120 2>/dev/null || true + kubectl -n "${APP_NS}" get ocirepositories -o yaml 2>/dev/null || true + docker logs --tail=80 "${ZOT_CONTAINER}" 2>/dev/null || true + fi + if [ -n "${KEEP:-}" ]; then + log "KEEP set — leaving cluster, container and ${WORKDIR} for inspection" + exit "${code}" + fi + log "Cleaning up" + docker rm -f "${ZOT_CONTAINER}" >/dev/null 2>&1 || true + if [ "${CREATED_CLUSTER}" = "true" ]; then + kind delete cluster --name "${CLUSTER}" >/dev/null 2>&1 || true + fi + rm -rf "${WORKDIR}" 2>/dev/null || sudo rm -rf "${WORKDIR}" 2>/dev/null || true + exit "${code}" +} +trap cleanup EXIT + +require() { command -v "$1" >/dev/null 2>&1 || fail "required tool '$1' not found in PATH"; } +for bin in docker kind kubectl kustomize flux helm; do require "$bin"; done + +start_zot() { + local config="$1" + docker rm -f "${ZOT_CONTAINER}" >/dev/null 2>&1 || true + docker run -d --name "${ZOT_CONTAINER}" --network kind \ + -p "127.0.0.1:${ZOT_HOST_PORT}:${ZOT_PORT}" \ + -v "${config}:/etc/zot/config.json:ro" \ + -v "${WORKDIR}/zot-data:/var/lib/zot" \ + "${ZOT_IMAGE}" serve /etc/zot/config.json >/dev/null + + local code + for _ in $(seq 1 60); do + code="$(curl -s -o /dev/null -w '%{http_code}' "http://localhost:${ZOT_HOST_PORT}/v2/" 2>/dev/null || echo 000)" + case "${code}" in + 200|401) return 0 ;; + esac + sleep 1 + done + docker logs --tail=80 "${ZOT_CONTAINER}" 2>/dev/null || true + fail "Zot did not become ready (last HTTP status: ${code})" +} + +############################################################################## +log "Creating kind cluster '${CLUSTER}' with an externally reachable issuer" +############################################################################## +if kind get clusters 2>/dev/null | grep -qx "${CLUSTER}"; then + info "Deleting pre-existing cluster" + kind delete cluster --name "${CLUSTER}" +fi +cat </dev/null +kubectl wait --for=condition=Ready nodes --all --timeout=180s + +OIDC_ISSUER="https://${CLUSTER}-control-plane:6443" +info "OIDC issuer: ${OIDC_ISSUER}" + +log "Allowing unauthenticated OIDC discovery" +kubectl create clusterrolebinding oidc-reviewer \ + --clusterrole=system:service-account-issuer-discovery \ + --group=system:unauthenticated >/dev/null 2>&1 || true + +log "Exporting the kind CA certificate" +docker cp "${CLUSTER}-control-plane:/etc/kubernetes/pki/ca.crt" "${WORKDIR}/kind-ca.pem" +mkdir -p "${WORKDIR}/zot-data" +chmod 0777 "${WORKDIR}/zot-data" + +############################################################################## +log "Seeding Zot with an artifact while authentication is disabled" +############################################################################## +cat > "${WORKDIR}/zot-open.json" < "${WORKDIR}/artifact-a/index.txt" +echo "artifact for ${SA_NAME_B}" > "${WORKDIR}/artifact-b/index.txt" +flux push artifact "oci://localhost:${ZOT_HOST_PORT}/${REPO_A}:${REV}" \ + --path="${WORKDIR}/artifact-a" \ + --source=zot-oidc-e2e-a \ + --revision="${REV}" +flux push artifact "oci://localhost:${ZOT_HOST_PORT}/${REPO_B}:${REV}" \ + --path="${WORKDIR}/artifact-b" \ + --source=zot-oidc-e2e-b \ + --revision="${REV}" +docker rm -f "${ZOT_CONTAINER}" >/dev/null + + +############################################################################## +log "Installing SPIRE with the SPIFFE Broker API and OIDC discovery" +############################################################################## +kubectl create namespace "${SPIRE_NAMESPACE}" --dry-run=client -o yaml | kubectl apply -f - >/dev/null + +kubectl apply -f - >/dev/null </dev/null +} + +create_entry "spiffe://${SPIRE_TRUST_DOMAIN}/oidc-discovery-provider" \ + -selector "k8s:ns:${SPIRE_NAMESPACE}" \ + -selector "k8s:sa:spire-agent" \ + -selector "k8s:container-name:oidc-discovery-provider" +create_entry "spiffe://${SPIRE_TRUST_DOMAIN}/ns/${NS}/sa/${BROKER_SA}" \ + -selector "k8s:ns:${NS}" \ + -selector "k8s:sa:${BROKER_SA}" +create_entry "${JWT_SPIFFE_A}" \ + -selector "k8s:resource:${JWT_OBJ_TYPE}" \ + -selector "k8s:namespace:${APP_NS}" \ + -selector "k8s:name:${JWT_OCIREPO_A}" +create_entry "${JWT_SPIFFE_B}" \ + -selector "k8s:resource:${JWT_OBJ_TYPE}" \ + -selector "k8s:namespace:${APP_NS}" \ + -selector "k8s:name:${JWT_OCIREPO_B}" + +kubectl apply -f - >/dev/null </dev/null 2>&1 || true +helm repo update spiffe >/dev/null +helm upgrade --install spire-csi spiffe/spire \ + --version "${SPIRE_CHART_VERSION:-0.30.3}" \ + --namespace "${SPIRE_NAMESPACE}" \ + --set global.spire.trustDomain="${SPIRE_TRUST_DOMAIN}" \ + --set global.spire.namespaces.create=false \ + --set spire-server.enabled=false \ + --set spire-agent.enabled=false \ + --set spiffe-csi-driver.enabled=true \ + --set spiffe-csi-driver.agentSocketPath=/run/spire/agent-sockets/api.sock \ + --set spiffe-oidc-discovery-provider.enabled=false \ + --wait --timeout 5m +kubectl -n "${SPIRE_NAMESPACE}" rollout status daemonset/spire-csi-spiffe-csi-driver --timeout=5m + +############################################################################## +log "Starting Zot with OIDC bearer authentication" +############################################################################## +cat > "${WORKDIR}/zot-auth.json" </dev/null + +for _ in $(seq 1 60); do + code="$(curl -s -o /dev/null -w '%{http_code}' "http://localhost:${ZOT_HOST_PORT}/v2/" 2>/dev/null || echo 000)" + [ "${code}" = "401" ] && break + sleep 1 +done +[ "${code}" = "401" ] || fail "Zot did not start with bearer auth (last HTTP status: ${code})" + +############################################################################## +log "Deploying source-controller" +############################################################################## +kind load docker-image "${IMG}" --name "${CLUSTER}" +make -C "${ROOT_DIR}" install >/dev/null +make -C "${ROOT_DIR}" dev-deploy IMG="${IMG%:*}" TAG="${IMG##*:}" >/dev/null +kubectl -n "${NS}" patch deploy/source-controller --type=json -p "$(cat </dev/null +kubectl -n "${NS}" patch deploy/source-controller --type=strategic -p "$(cat </dev/null +kubectl -n "${NS}" rollout status deploy/source-controller --timeout=2m +for _ in $(seq 1 30); do + logs="$(kubectl -n "${NS}" logs deploy/source-controller)" + if grep -qF "object-level workload identity will use the SPIFFE Broker API" <<<"${logs}"; then + break + fi + sleep 1 +done +grep -qF "object-level workload identity will use the SPIFFE Broker API" <<<"${logs}" \ + || fail "source-controller did not log SPIFFE Broker API usage" + +############################################################################## +log "Creating ServiceAccounts and OCIRepositories" +############################################################################## +kubectl create namespace "${APP_NS}" --dry-run=client -o yaml | kubectl apply -f - >/dev/null +kubectl -n "${APP_NS}" apply -f - >/dev/null </dev/null </dev/null || true)" + msg="$(kubectl -n "${APP_NS}" get "ocirepository/${name}" \ + -o jsonpath='{.status.conditions[?(@.type=="Ready")].message}' 2>/dev/null || true)" + if [ "${status}" = "True" ]; then + fail "OCIRepository ${name} unexpectedly succeeded" + fi + if [ "${status}" = "False" ] && printf '%s' "${msg}" | grep -qiE '401|403|unauthorized|forbidden|denied|authentication|permission|svid|spiffe'; then + info "OCIRepository ${name} correctly denied" + return 0 + fi + sleep 2 + done + fail "OCIRepository ${name} did not fail in time (status=${status:-unknown}; message=${msg:-})" +} + +log "Verifying cross-ServiceAccount access is denied" +expect_denied "${OCIREPO_A}-cross" +expect_denied "${OCIREPO_B}-cross" +expect_denied "${OCIREPO_A}-wrong-audience" +expect_denied "${JWT_OCIREPO_A_CROSS}" + +log "SUCCESS: each ServiceAccount and JWT-SVID identity accessed only its own repository" diff --git a/internal/bucket/azure/blob.go b/internal/bucket/azure/blob.go index ad70acb4e..4bbb9584d 100644 --- a/internal/bucket/azure/blob.go +++ b/internal/bucket/azure/blob.go @@ -30,7 +30,6 @@ import ( "github.com/Azure/azure-sdk-for-go/sdk/azcore" "github.com/Azure/azure-sdk-for-go/sdk/azcore/cloud" - "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" "github.com/Azure/azure-sdk-for-go/sdk/azidentity" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/bloberror" @@ -259,7 +258,7 @@ func ValidateSecret(secret *corev1.Secret) error { // exists, or returns a (client) error. func (c *BlobClient) BucketExists(ctx context.Context, bucketName string) (bool, error) { items := c.Client.NewListBlobsFlatPager(bucketName, &azblob.ListBlobsFlatOptions{ - MaxResults: to.Ptr(int32(1)), + MaxResults: new(int32(1)), }) // We call next page only once since we just want to see if we get an error if _, err := items.NextPage(ctx); err != nil { @@ -311,6 +310,11 @@ func (c *BlobClient) FGetObject(ctx context.Context, bucketName, objectName, loc if err != nil { return "", err } + defer func() { + if cerr := res.Body.Close(); cerr != nil { + ctrl.LoggerFrom(ctx).Error(cerr, "failed to close object reader") + } + }() // Prepare target file. f, err := os.OpenFile(localPath, os.O_CREATE|os.O_WRONLY, 0o600) @@ -326,8 +330,8 @@ func (c *BlobClient) FGetObject(ctx context.Context, bucketName, objectName, loc // Off we go. mw := io.MultiWriter(f, hash) if _, err = io.Copy(mw, res.Body); err != nil { - if err = f.Close(); err != nil { - ctrl.LoggerFrom(ctx).Error(err, "failed to close file after copy error") + if cerr := f.Close(); cerr != nil { + ctrl.LoggerFrom(ctx).Error(cerr, "failed to close file after copy error") } return "", err } diff --git a/internal/bucket/azure/blob_test.go b/internal/bucket/azure/blob_test.go index 21ab3d7d5..36d2189b4 100644 --- a/internal/bucket/azure/blob_test.go +++ b/internal/bucket/azure/blob_test.go @@ -25,6 +25,7 @@ import ( "encoding/pem" "errors" "fmt" + "io" "math/big" "net/http" "net/url" @@ -669,6 +670,90 @@ func TestBlobClient_FGetObject_MissingEtag(t *testing.T) { g.Expect(os.ReadFile(localPath)).To(BeEquivalentTo("file contents")) } +// A short response body makes io.Copy fail after some bytes are written. +// Closing that file succeeds, and the close must not replace the copy error. +func TestBlobClient_FGetObject_CopyError(t *testing.T) { + g := NewWithT(t) + + bucketName := "test-bucket" + objectName := "file.txt" + + bucketListener, bucketAddr, _ := testlistener.New(t) + bucketEndpoint := fmt.Sprintf("http://%s", bucketAddr) + bucketHandler := http.NewServeMux() + bucketHandler.HandleFunc(fmt.Sprintf("GET /%s/%s", bucketName, objectName), func(w http.ResponseWriter, r *http.Request) { + hj, ok := w.(http.Hijacker) + g.Expect(ok).To(BeTrue()) + conn, bufrw, err := hj.Hijack() + g.Expect(err).ToNot(HaveOccurred()) + _, err = bufrw.WriteString("HTTP/1.1 200 OK\r\nContent-Length: 64\r\nETag: \"abc\"\r\n\r\npartial") + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(bufrw.Flush()).To(Succeed()) + g.Expect(conn.Close()).To(Succeed()) + }) + bucketServer := &http.Server{ + Addr: bucketAddr, + Handler: bucketHandler, + } + go bucketServer.Serve(bucketListener) + defer bucketServer.Shutdown(context.Background()) + + bucket := &sourcev1.Bucket{ + Spec: sourcev1.BucketSpec{ + Endpoint: bucketEndpoint, + }, + } + client, err := NewClient(t.Context(), + bucket, + withoutCredentials(), + withoutRetries()) + g.Expect(err).ToNot(HaveOccurred()) + + localPath := filepath.Join(t.TempDir(), objectName) + _, err = client.FGetObject(t.Context(), bucketName, objectName, localPath) + g.Expect(err).To(HaveOccurred()) +} + +// The Get Blob response body must be closed so the connection can be reused. +func TestBlobClient_FGetObject_ClosesResponseBody(t *testing.T) { + g := NewWithT(t) + + bucketName := "test-bucket" + objectName := "file.txt" + + bucketListener, bucketAddr, _ := testlistener.New(t) + bucketEndpoint := fmt.Sprintf("http://%s", bucketAddr) + bucketHandler := http.NewServeMux() + bucketHandler.HandleFunc(fmt.Sprintf("GET /%s/%s", bucketName, objectName), func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("ETag", `"abc"`) + _, err := w.Write([]byte("file contents")) + g.Expect(err).ToNot(HaveOccurred()) + }) + bucketServer := &http.Server{ + Addr: bucketAddr, + Handler: bucketHandler, + } + go bucketServer.Serve(bucketListener) + defer bucketServer.Shutdown(context.Background()) + + tracker := &bodyCloseTracker{base: http.DefaultTransport} + clientOpts := &azblob.ClientOptions{} + clientOpts.Transport = &http.Client{Transport: tracker} + clientOpts.Retry.ShouldRetry = func(*http.Response, error) bool { + return false + } + raw, err := azblob.NewClientWithNoCredential(bucketEndpoint, clientOpts) + g.Expect(err).ToNot(HaveOccurred()) + client := &BlobClient{Client: raw} + + localPath := filepath.Join(t.TempDir(), objectName) + etag, err := client.FGetObject(t.Context(), bucketName, objectName, localPath) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(etag).To(Equal(`"abc"`)) + g.Expect(os.ReadFile(localPath)).To(BeEquivalentTo("file contents")) + g.Expect(tracker.closed).To(BeTrue()) +} + func TestBlobClient_VisitObjects_MissingFields(t *testing.T) { g := NewWithT(t) @@ -780,6 +865,32 @@ func Test_extractAccountNameFromEndpoint1(t *testing.T) { } } +// bodyCloseTracker records whether the response body from a blob +// download was closed. +type bodyCloseTracker struct { + base http.RoundTripper + closed bool +} + +func (b *bodyCloseTracker) RoundTrip(req *http.Request) (*http.Response, error) { + resp, err := b.base.RoundTrip(req) + if err != nil || resp == nil || resp.Body == nil { + return resp, err + } + resp.Body = &closeNotifier{ReadCloser: resp.Body, onClose: func() { b.closed = true }} + return resp, nil +} + +type closeNotifier struct { + io.ReadCloser + onClose func() +} + +func (c *closeNotifier) Close() error { + c.onClose() + return c.ReadCloser.Close() +} + func endpointURL(accountName string) string { return fmt.Sprintf("https://%s.blob.core.windows.net", accountName) } diff --git a/internal/bucket/gcp/gcp.go b/internal/bucket/gcp/gcp.go index 7467d5c56..8ccf4f17e 100644 --- a/internal/bucket/gcp/gcp.go +++ b/internal/bucket/gcp/gcp.go @@ -282,6 +282,9 @@ func (c *GCSClient) FGetObject(ctx context.Context, bucketName, objectName, loca GenerationMatch: objAttr.Generation, }).NewReader(ctx) if err != nil { + if cerr := objectFile.Close(); cerr != nil { + ctrl.LoggerFrom(ctx).Error(cerr, "failed to close file after reader error") + } return "", err } defer func() { @@ -292,6 +295,9 @@ func (c *GCSClient) FGetObject(ctx context.Context, bucketName, objectName, loca // Write Object to file. if _, err := io.Copy(objectFile, objectReader); err != nil { + if cerr := objectFile.Close(); cerr != nil { + ctrl.LoggerFrom(ctx).Error(cerr, "failed to close file after copy error") + } return "", err } diff --git a/internal/bucket/gcp/gcp_test.go b/internal/bucket/gcp/gcp_test.go index 296212652..0832fe751 100644 --- a/internal/bucket/gcp/gcp_test.go +++ b/internal/bucket/gcp/gcp_test.go @@ -35,7 +35,6 @@ import ( "cloud.google.com/go/compute/metadata" gcpstorage "cloud.google.com/go/storage" . "github.com/onsi/gomega" - "google.golang.org/api/googleapi" "google.golang.org/api/option" raw "google.golang.org/api/storage/v1" corev1 "k8s.io/api/core/v1" @@ -61,40 +60,32 @@ var ( close func() err error secret = corev1.Secret{ - ObjectMeta: v1.ObjectMeta{ - Name: "gcp-secret", - Namespace: "default", - }, + Name: "gcp-secret", + Namespace: "default", Data: map[string][]byte{ "serviceaccount": []byte(serviceAccountJSON), }, Type: "Opaque", } badSecret = corev1.Secret{ - ObjectMeta: v1.ObjectMeta{ - Name: "gcp-secret", - Namespace: "default", - }, + Name: "gcp-secret", + Namespace: "default", Data: map[string][]byte{ "username": []byte("test-user"), }, Type: "Opaque", } malformedSecret = corev1.Secret{ - ObjectMeta: v1.ObjectMeta{ - Name: "gcp-secret", - Namespace: "default", - }, + Name: "gcp-secret", + Namespace: "default", Data: map[string][]byte{ "serviceaccount": []byte("not-json"), }, Type: "Opaque", } externalAccountSecret = corev1.Secret{ - ObjectMeta: v1.ObjectMeta{ - Name: "gcp-secret", - Namespace: "default", - }, + Name: "gcp-secret", + Namespace: "default", Data: map[string][]byte{ "serviceaccount": []byte(externalAccountJSON), }, @@ -134,10 +125,8 @@ const externalAccountJSON = `{ // createTestBucket creates a test bucket for testing purposes func createTestBucket() *sourcev1.Bucket { return &sourcev1.Bucket{ - ObjectMeta: v1.ObjectMeta{ - Name: "test-bucket", - Namespace: "default", - }, + Name: "test-bucket", + Namespace: "default", Spec: sourcev1.BucketSpec{ BucketName: bucketName, Endpoint: "storage.googleapis.com", @@ -293,7 +282,7 @@ func TestNewClientWithProxyErr(t *testing.T) { bucketAttrs, err := gcpClient.Client.Bucket("some-bucket").Attrs(context.Background()) g.Expect(err).To(HaveOccurred()) g.Expect(bucketAttrs).To(BeNil()) - g.Expect(err.Error()).To(ContainSubstring("failed to create provider access token")) + g.Expect(err.Error()).To(ContainSubstring("failed to create provider access credential")) }) } @@ -325,7 +314,6 @@ func TestProxy(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { opts := []Option{WithProxyURL(tt.proxyURL)} opts = append(opts, func(o *options) { @@ -567,7 +555,7 @@ func getBucket() *raw.Bucket { }, Condition: &raw.BucketLifecycleRuleCondition{ Age: &age, - IsLive: googleapi.Bool(true), + IsLive: new(true), CreatedBefore: "2021-01-02", MatchesStorageClass: matchClasses, NumNewerVersions: 3, diff --git a/internal/bucket/minio/minio_test.go b/internal/bucket/minio/minio_test.go index 8d5878f49..039c7dcab 100644 --- a/internal/bucket/minio/minio_test.go +++ b/internal/bucket/minio/minio_test.go @@ -38,7 +38,6 @@ import ( "github.com/minio/minio-go/v7/pkg/credentials" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/sourceignore" @@ -80,10 +79,8 @@ var ( bucketName = "test-bucket-minio" + uuid.New().String() prefix = "" secret = corev1.Secret{ - ObjectMeta: v1.ObjectMeta{ - Name: "minio-secret", - Namespace: "default", - }, + Name: "minio-secret", + Namespace: "default", Data: map[string][]byte{ "accesskey": []byte(testAccessKey), "secretkey": []byte(testSecretKey), @@ -91,18 +88,14 @@ var ( Type: "Opaque", } emptySecret = corev1.Secret{ - ObjectMeta: v1.ObjectMeta{ - Name: "minio-secret", - Namespace: "default", - }, - Data: map[string][]byte{}, - Type: "Opaque", + Name: "minio-secret", + Namespace: "default", + Data: map[string][]byte{}, + Type: "Opaque", } bucket = sourcev1.Bucket{ - ObjectMeta: v1.ObjectMeta{ - Name: "minio-test-bucket", - Namespace: "default", - }, + Name: "minio-test-bucket", + Namespace: "default", Spec: sourcev1.BucketSpec{ BucketName: bucketName, Provider: "generic", @@ -112,10 +105,8 @@ var ( }, } bucketAwsProvider = sourcev1.Bucket{ - ObjectMeta: v1.ObjectMeta{ - Name: "minio-test-bucket", - Namespace: "default", - }, + Name: "minio-test-bucket", + Namespace: "default", Spec: sourcev1.BucketSpec{ BucketName: bucketName, Provider: "aws", @@ -193,10 +184,8 @@ func TestNewClientEmptySecret(t *testing.T) { func TestNewClientAWSProvider(t *testing.T) { t.Run("with secret", func(t *testing.T) { validSecret := corev1.Secret{ - ObjectMeta: v1.ObjectMeta{ - Name: "valid-secret", - Namespace: "default", - }, + Name: "valid-secret", + Namespace: "default", Data: map[string][]byte{ "accesskey": []byte(testAccessKey), "secretkey": []byte(testSecretKey), @@ -716,7 +705,7 @@ func TestValidateSTSSecret(t *testing.T) { { name: "empty ldap secret", provider: "ldap", - secret: &corev1.Secret{ObjectMeta: v1.ObjectMeta{Name: "ldap-secret"}}, + secret: &corev1.Secret{Name: "ldap-secret"}, err: "invalid 'ldap-secret' secret data for 'ldap' STS provider: required fields username, password", }, { diff --git a/internal/cache/cache.go b/internal/cache/cache.go index 6f8ee8608..beb143396 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -27,7 +27,7 @@ type Cache struct { // Item is an item stored in the cache. type Item struct { // Object is the item's value. - Object interface{} + Object any // Expiration is the item's expiration time. Expiration int64 } @@ -50,7 +50,7 @@ func (c *cache) ItemCount() int { return n } -func (c *cache) set(key string, value interface{}, expiration time.Duration) { +func (c *cache) set(key string, value any, expiration time.Duration) { var e int64 if expiration > 0 { e = time.Now().Add(expiration).UnixNano() @@ -65,7 +65,7 @@ func (c *cache) set(key string, value interface{}, expiration time.Duration) { // Set adds an item to the cache, replacing any existing item. // If expiration is zero, the item never expires. // If the cache is full, Set will return an error. -func (c *cache) Set(key string, value interface{}, expiration time.Duration) error { +func (c *cache) Set(key string, value any, expiration time.Duration) error { c.mu.Lock() _, found := c.Items[key] if found { @@ -87,7 +87,7 @@ func (c *cache) Set(key string, value interface{}, expiration time.Duration) err // Add an item to the cache, existing items will not be overwritten. // To overwrite existing items, use Set. // If the cache is full, Add will return an error. -func (c *cache) Add(key string, value interface{}, expiration time.Duration) error { +func (c *cache) Add(key string, value any, expiration time.Duration) error { c.mu.Lock() _, found := c.Items[key] if found { @@ -107,7 +107,7 @@ func (c *cache) Add(key string, value interface{}, expiration time.Duration) err // Get an item from the cache. Returns the item or nil, and a bool indicating // whether the key was found. -func (c *cache) Get(key string) (interface{}, bool) { +func (c *cache) Get(key string) (any, bool) { c.mu.RLock() item, found := c.Items[key] if !found { diff --git a/internal/controller/artifact_matchers_test.go b/internal/controller/artifact_matchers_test.go index af716e086..36a688e68 100644 --- a/internal/controller/artifact_matchers_test.go +++ b/internal/controller/artifact_matchers_test.go @@ -36,7 +36,7 @@ type matchArtifact struct { expected *meta.Artifact } -func (m matchArtifact) Match(actual interface{}) (success bool, err error) { +func (m matchArtifact) Match(actual any) (success bool, err error) { actualArtifact, ok := actual.(*meta.Artifact) if !ok { return false, fmt.Errorf("actual should be a pointer to an Artifact") @@ -59,10 +59,10 @@ func (m matchArtifact) Match(actual interface{}) (success bool, err error) { return ok, err } -func (m matchArtifact) FailureMessage(actual interface{}) (message string) { +func (m matchArtifact) FailureMessage(actual any) (message string) { return fmt.Sprintf("expected\n\t%#v\nto match\n\t%#v\n", actual, m.expected) } -func (m matchArtifact) NegatedFailureMessage(actual interface{}) (message string) { +func (m matchArtifact) NegatedFailureMessage(actual any) (message string) { return fmt.Sprintf("expected\n\t%#v\nto not match\n\t%#v\n", actual, m.expected) } diff --git a/internal/controller/bucket_controller.go b/internal/controller/bucket_controller.go index 8044875b8..a90e5608c 100644 --- a/internal/controller/bucket_controller.go +++ b/internal/controller/bucket_controller.go @@ -41,6 +41,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" + cryptov1 "github.com/fluxcd/pkg/apis/crypto/v1" eventv1 "github.com/fluxcd/pkg/apis/event/v1" "github.com/fluxcd/pkg/apis/meta" intdigest "github.com/fluxcd/pkg/artifact/digest" @@ -56,6 +57,7 @@ import ( rreconcile "github.com/fluxcd/pkg/runtime/reconcile" "github.com/fluxcd/pkg/runtime/secrets" "github.com/fluxcd/pkg/sourceignore" + fluxbroker "github.com/fluxcd/pkg/spiffe/broker" sourcev1 "github.com/fluxcd/source-controller/api/v1" "github.com/fluxcd/source-controller/internal/bucket/azure" @@ -132,6 +134,9 @@ type BucketReconciler struct { ControllerName string TokenCache *cache.TokenCache + DefaultServiceAccount string + SPIFFEBroker fluxbroker.Interface + patchOptions []patch.Option } @@ -433,18 +438,6 @@ func (r *BucketReconciler) reconcileStorage(ctx context.Context, sp *patch.Seria // the provider. If this fails, it records v1.FetchFailedCondition=True on // the object and returns early. func (r *BucketReconciler) reconcileSource(ctx context.Context, sp *patch.SerialPatcher, obj *sourcev1.Bucket, index *index.Digester, dir string) (sreconcile.Result, error) { - usesObjectLevelWorkloadIdentity := obj.Spec.Provider != "" && obj.Spec.Provider != sourcev1.BucketProviderGeneric && obj.Spec.ServiceAccountName != "" - if usesObjectLevelWorkloadIdentity { - if !auth.IsObjectLevelWorkloadIdentityEnabled() { - const gate = auth.FeatureGateObjectLevelWorkloadIdentity - const msgFmt = "to use spec.serviceAccountName for provider authentication please enable the %s feature gate in the controller" - err := fmt.Errorf(msgFmt, gate) - e := serror.NewStalling(err, meta.FeatureGateDisabledReason) - conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e) - return sreconcile.ResultEmpty, e - } - } - creds, err := r.setupCredentials(ctx, obj) if err != nil { e := serror.NewGeneric(err, sourcev1.AuthenticationFailedReason) @@ -454,12 +447,10 @@ func (r *BucketReconciler) reconcileSource(ctx context.Context, sp *patch.Serial provider, err := r.createBucketProvider(ctx, obj, creds) if err != nil { - var stallingErr *serror.Stalling - var genericErr *serror.Generic - if errors.As(err, &stallingErr) { + if stallingErr, ok := errors.AsType[*serror.Stalling](err); ok { conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, stallingErr.Reason, "%s", stallingErr) return sreconcile.ResultEmpty, stallingErr - } else if errors.As(err, &genericErr) { + } else if genericErr, ok := errors.AsType[*serror.Generic](err); ok { conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, genericErr.Reason, "%s", genericErr) return sreconcile.ResultEmpty, genericErr } else { @@ -839,35 +830,79 @@ func (r *BucketReconciler) setupCredentials(ctx context.Context, obj *sourcev1.B // createBucketProvider creates a provider-specific bucket client using the given credentials and configuration. // It handles different bucket providers (AWS, GCP, Azure, generic) and returns the appropriate client. func (r *BucketReconciler) createBucketProvider(ctx context.Context, obj *sourcev1.Bucket, creds *bucketCredentials) (BucketProvider, error) { - authOpts := []auth.Option{ - auth.WithClient(r.Client), - auth.WithServiceAccountNamespace(obj.GetNamespace()), + // Determine the cryptov1 service provider and resolve the ServiceAccount + // once, applying the default ServiceAccount when the object does not + // specify one. The generic provider does not support a ServiceAccount. + var serviceProvider string + switch obj.Spec.Provider { + case sourcev1.BucketProviderAmazon: + serviceProvider = cryptov1.ProviderAWS + case sourcev1.BucketProviderGoogle: + serviceProvider = cryptov1.ProviderGCP + case sourcev1.BucketProviderAzure: + serviceProvider = cryptov1.ProviderAzure } - if obj.Spec.ServiceAccountName != "" { - authOpts = append(authOpts, auth.WithServiceAccountName(obj.Spec.ServiceAccountName)) + var sa *corev1.ServiceAccount + if serviceProvider != "" { + saName := obj.Spec.ServiceAccountName + if saName == "" { + saName = r.DefaultServiceAccount + } + if saName != "" { + sa = &corev1.ServiceAccount{} + saRef := client.ObjectKey{Namespace: obj.GetNamespace(), Name: saName} + if err := r.Get(ctx, saRef, sa); err != nil { + return nil, fmt.Errorf("failed to get service account '%s': %w", saRef, err) + } + } } - if r.TokenCache != nil { - involvedObject := cache.InvolvedObject{ - Kind: sourcev1.BucketKind, - Name: obj.GetName(), - Namespace: obj.GetNamespace(), - Operation: cache.OperationReconcile, + var cryptoSpec *cryptov1.Spec + if sa != nil { + buildOpts := []cryptov1.Option{ + cryptov1.WithAnnotations(sa.Annotations), + cryptov1.WithServiceProvider(serviceProvider), + } + if sts := obj.Spec.STS; sts != nil { + buildOpts = append(buildOpts, cryptov1.WithSTSEndpoint(sts.Provider, sts.Endpoint)) + } + if obj.Spec.CertSecretRef != nil || + (obj.Spec.STS != nil && obj.Spec.STS.CertSecretRef != nil) { + buildOpts = append(buildOpts, cryptov1.WithCertSecretRef()) + } + var err error + cryptoSpec, err = cryptov1.BuildSpec(buildOpts...) + if err != nil { + return nil, fmt.Errorf("failed to build spec for service account '%s/%s': %w", + sa.Namespace, sa.Name, err) } - authOpts = append(authOpts, auth.WithCache(*r.TokenCache, involvedObject)) } + var authOpts []auth.Option + if cryptoSpec != nil { + authOpts = auth.OptionsFromSpec(cryptoSpec.Auth) + } + authOpts = append(authOpts, + auth.WithClient(r.Client), + auth.WithObject(sourcev1.GroupVersion.Group, sourcev1.BucketKind, obj), + auth.WithSPIFFEBroker(r.SPIFFEBroker)) + if sa != nil { + authOpts = append(authOpts, auth.WithServiceAccountName(sa.Name)) + } + if r.TokenCache != nil { + authOpts = append(authOpts, + auth.WithCache(*r.TokenCache), + auth.WithCacheOperation(cache.OperationReconcile)) + } if creds.proxyURL != nil { authOpts = append(authOpts, auth.WithProxyURL(*creds.proxyURL)) } - if obj.Spec.Region != "" { - authOpts = append(authOpts, auth.WithSTSRegion(obj.Spec.Region)) + authOpts = append(authOpts, auth.WithAWSRegion(obj.Spec.Region)) } - if sts := obj.Spec.STS; sts != nil { - authOpts = append(authOpts, auth.WithSTSEndpoint(sts.Endpoint)) + authOpts = append(authOpts, auth.WithAWSSTSEndpoint(sts.Endpoint)) } switch obj.Spec.Provider { diff --git a/internal/controller/bucket_controller_fetch_test.go b/internal/controller/bucket_controller_fetch_test.go index 9f9c31a12..2c21bb73f 100644 --- a/internal/controller/bucket_controller_fetch_test.go +++ b/internal/controller/bucket_controller_fetch_test.go @@ -319,7 +319,7 @@ func Test_fetchFiles(t *testing.T) { tmp := t.TempDir() client := mockBucketClient{bucketName: bucketName} - for i := 0; i < 2*maxConcurrentBucketFetches; i++ { + for i := range 2 * maxConcurrentBucketFetches { f := fmt.Sprintf("file-%d", i) client.addObject(f, mockBucketObject{etag: f, data: f}) } diff --git a/internal/controller/bucket_controller_test.go b/internal/controller/bucket_controller_test.go index 494c53303..6fbd50d2b 100644 --- a/internal/controller/bucket_controller_test.go +++ b/internal/controller/bucket_controller_test.go @@ -40,7 +40,6 @@ import ( "github.com/fluxcd/pkg/apis/meta" intdigest "github.com/fluxcd/pkg/artifact/digest" "github.com/fluxcd/pkg/artifact/storage" - "github.com/fluxcd/pkg/auth" "github.com/fluxcd/pkg/runtime/conditions" conditionscheck "github.com/fluxcd/pkg/runtime/conditions/check" "github.com/fluxcd/pkg/runtime/events" @@ -63,7 +62,7 @@ func TestBucketReconciler_deleteBeforeFinalizer(t *testing.T) { namespaceName := "bucket-" + randStringRunes(5) namespace := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{Name: namespaceName}, + Name: namespaceName, } g.Expect(k8sClient.Create(ctx, namespace)).ToNot(HaveOccurred()) t.Cleanup(func() { @@ -114,10 +113,8 @@ func TestBucketReconciler_Reconcile(t *testing.T) { g.Expect(err).NotTo(HaveOccurred()) secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "bucket-reconcile-", - Namespace: "default", - }, + GenerateName: "bucket-reconcile-", + Namespace: "default", Data: map[string][]byte{ "accesskey": []byte("key"), "secretkey": []byte("secret"), @@ -127,10 +124,8 @@ func TestBucketReconciler_Reconcile(t *testing.T) { defer testEnv.Delete(ctx, secret) origObj := &sourcev1.Bucket{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "bucket-reconcile-", - Namespace: "default", - }, + GenerateName: "bucket-reconcile-", + Namespace: "default", Spec: sourcev1.BucketSpec{ Provider: "generic", BucketName: s3Server.BucketName, @@ -235,7 +230,7 @@ func TestBucketReconciler_reconcileStorage(t *testing.T) { Revision: "d", Digest: "sha256:18ac3e7343f016890c510e93f935261169d9e3f565436429830faf0934f4f8e4", URL: testStorage.Hostname + "/reconcile-storage/d.txt", - Size: int64p(int64(len("d"))), + Size: new(int64(len("d"))), }, assertPaths: []string{ "/reconcile-storage/d.txt", @@ -364,7 +359,7 @@ func TestBucketReconciler_reconcileStorage(t *testing.T) { Revision: "f", Digest: "sha256:3b9c358f36f0a31b6ad3e14f309c7cf198ac9246e8316f9ce543d5b19ac02b80", URL: testStorage.Hostname + "/reconcile-storage/hostname.txt", - Size: int64p(int64(len("file"))), + Size: new(int64(len("file"))), }, assertConditions: []metav1.Condition{ *conditions.TrueCondition(meta.ReadyCondition, "foo", "bar"), @@ -390,10 +385,8 @@ func TestBucketReconciler_reconcileStorage(t *testing.T) { } obj := &sourcev1.Bucket{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-", - Generation: 1, - }, + GenerateName: "test-", + Generation: 1, } if tt.beforeFunc != nil { g.Expect(tt.beforeFunc(obj, testStorage)).To(Succeed()) @@ -494,9 +487,7 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) { name: "Observes invalid secretRef", bucketName: "dummy", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "dummy", - }, + Name: "dummy", }, beforeFunc: func(obj *sourcev1.Bucket) { obj.Spec.SecretRef = &meta.LocalObjectReference{ @@ -535,9 +526,7 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) { name: "Observes invalid certSecretRef", bucketName: "dummy", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "dummy", - }, + Name: "dummy", }, beforeFunc: func(obj *sourcev1.Bucket) { obj.Spec.CertSecretRef = &meta.LocalObjectReference{ @@ -576,9 +565,7 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) { name: "Observes invalid proxySecretRef", bucketName: "dummy", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "dummy", - }, + Name: "dummy", Data: map[string][]byte{}, }, beforeFunc: func(obj *sourcev1.Bucket) { @@ -618,9 +605,7 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) { name: "Observes invalid sts.secretRef", bucketName: "dummy", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "dummy", - }, + Name: "dummy", }, beforeFunc: func(obj *sourcev1.Bucket) { obj.Spec.Provider = "generic" @@ -662,9 +647,7 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) { name: "Observes invalid sts.certSecretRef", bucketName: "dummy", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "dummy", - }, + Name: "dummy", }, beforeFunc: func(obj *sourcev1.Bucket) { obj.Spec.Provider = "generic" @@ -927,10 +910,8 @@ func TestBucketReconciler_reconcileSource_generic(t *testing.T) { tmpDir := t.TempDir() obj := &sourcev1.Bucket{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-bucket-", - Generation: 1, - }, + GenerateName: "test-bucket-", + Generation: 1, Spec: sourcev1.BucketSpec{ Timeout: &metav1.Duration{Duration: timeout}, }, @@ -1004,17 +985,16 @@ const gcsExternalAccountConfig = `{ func TestBucketReconciler_reconcileSource_gcs(t *testing.T) { tests := []struct { - name string - bucketName string - bucketObjects []*gcsmock.Object - secret *corev1.Secret - serviceAccount *corev1.ServiceAccount - beforeFunc func(obj *sourcev1.Bucket) - want sreconcile.Result - wantErr bool - assertIndex *index.Digester - assertConditions []metav1.Condition - disableObjectLevelWorkloadIdentity bool + name string + bucketName string + bucketObjects []*gcsmock.Object + secret *corev1.Secret + serviceAccount *corev1.ServiceAccount + beforeFunc func(obj *sourcev1.Bucket) + want sreconcile.Result + wantErr bool + assertIndex *index.Digester + assertConditions []metav1.Condition }{ { name: "Reconciles GCS source", @@ -1028,9 +1008,7 @@ func TestBucketReconciler_reconcileSource_gcs(t *testing.T) { }, }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "dummy", - }, + Name: "dummy", Data: map[string][]byte{ "accesskey": []byte("key"), "secretkey": []byte("secret"), @@ -1074,9 +1052,7 @@ func TestBucketReconciler_reconcileSource_gcs(t *testing.T) { name: "Observes invalid secretRef", bucketName: "dummy", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "dummy", - }, + Name: "dummy", }, beforeFunc: func(obj *sourcev1.Bucket) { obj.Spec.SecretRef = &meta.LocalObjectReference{ @@ -1098,9 +1074,7 @@ func TestBucketReconciler_reconcileSource_gcs(t *testing.T) { name: "Observes unsupported credential type in secretRef", bucketName: "dummy", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "dummy", - }, + Name: "dummy", Data: map[string][]byte{ "serviceaccount": []byte(gcsExternalAccountConfig), }, @@ -1145,9 +1119,7 @@ func TestBucketReconciler_reconcileSource_gcs(t *testing.T) { name: "Observes invalid proxySecretRef", bucketName: "dummy", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "dummy", - }, + Name: "dummy", }, beforeFunc: func(obj *sourcev1.Bucket) { obj.Spec.ProxySecretRef = &meta.LocalObjectReference{ @@ -1357,9 +1329,7 @@ func TestBucketReconciler_reconcileSource_gcs(t *testing.T) { }, }, serviceAccount: &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-sa", - }, + Name: "test-sa", }, beforeFunc: func(obj *sourcev1.Bucket) { obj.Spec.ServiceAccountName = "test-sa" @@ -1396,29 +1366,6 @@ func TestBucketReconciler_reconcileSource_gcs(t *testing.T) { *conditions.UnknownCondition(meta.ReadyCondition, meta.ProgressingReason, "building artifact: new upstream revision 'sha256:b4c2a60ce44b67f5b659a95ce4e4cc9e2a86baf13afb72bd397c5384cbc0e479'"), }, }, - { - name: "GCS Object-Level fails when feature gate disabled", - bucketName: "dummy", - serviceAccount: &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-sa", - }, - }, - beforeFunc: func(obj *sourcev1.Bucket) { - obj.Spec.ServiceAccountName = "test-sa" - conditions.MarkReconciling(obj, meta.ProgressingReason, "foo") - conditions.MarkUnknown(obj, meta.ReadyCondition, "foo", "bar") - }, - want: sreconcile.ResultEmpty, - wantErr: true, - assertIndex: index.NewDigester(), - assertConditions: []metav1.Condition{ - *conditions.TrueCondition(sourcev1.FetchFailedCondition, meta.FeatureGateDisabledReason, "to use spec.serviceAccountName for provider authentication please enable the ObjectLevelWorkloadIdentity feature gate in the controller"), - *conditions.TrueCondition(meta.ReconcilingCondition, meta.ProgressingReason, "foo"), - *conditions.UnknownCondition(meta.ReadyCondition, "foo", "bar"), - }, - disableObjectLevelWorkloadIdentity: true, - }, // TODO: Middleware for mock server to test authentication using secret. } for _, tt := range tests { @@ -1444,20 +1391,12 @@ func TestBucketReconciler_reconcileSource_gcs(t *testing.T) { patchOptions: getPatchOptions(bucketReadyCondition.Owned, "sc"), } - // Handle ObjectLevelWorkloadIdentity feature gate - if !tt.disableObjectLevelWorkloadIdentity { - auth.EnableObjectLevelWorkloadIdentity() - t.Cleanup(auth.DisableObjectLevelWorkloadIdentity) - } - tmpDir := t.TempDir() // Test bucket object. obj := &sourcev1.Bucket{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-bucket-", - Generation: 1, - }, + GenerateName: "test-bucket-", + Generation: 1, Spec: sourcev1.BucketSpec{ BucketName: tt.bucketName, Timeout: &metav1.Duration{Duration: timeout}, @@ -1648,10 +1587,8 @@ func TestBucketReconciler_reconcileArtifact(t *testing.T) { } obj := &sourcev1.Bucket{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-bucket-", - Generation: 1, - }, + GenerateName: "test-bucket-", + Generation: 1, Spec: sourcev1.BucketSpec{ Timeout: &metav1.Duration{Duration: timeout}, }, @@ -1742,14 +1679,10 @@ func TestBucketReconciler_statusConditions(t *testing.T) { g := NewWithT(t) obj := &sourcev1.Bucket{ - TypeMeta: metav1.TypeMeta{ - APIVersion: sourcev1.GroupVersion.String(), - Kind: sourcev1.BucketKind, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "test-bucket", - Namespace: "foo", - }, + APIVersion: sourcev1.GroupVersion.String(), + Kind: sourcev1.BucketKind, + Name: "test-bucket", + Namespace: "foo", } c := fakeclient.NewClientBuilder(). @@ -2045,10 +1978,8 @@ func TestBucketReconciler_APIServerValidation_STS(t *testing.T) { g := NewWithT(t) obj := &sourcev1.Bucket{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "bucket-reconcile-", - Namespace: "default", - }, + GenerateName: "bucket-reconcile-", + Namespace: "default", Spec: sourcev1.BucketSpec{ Provider: tt.bucketProvider, BucketName: "test", diff --git a/internal/controller/gitrepository_controller.go b/internal/controller/gitrepository_controller.go index 8228037e3..eaefa27f0 100644 --- a/internal/controller/gitrepository_controller.go +++ b/internal/controller/gitrepository_controller.go @@ -34,6 +34,7 @@ import ( "github.com/fluxcd/pkg/runtime/events" "github.com/fluxcd/pkg/runtime/logger" "github.com/fluxcd/pkg/runtime/secrets" + fluxbroker "github.com/fluxcd/pkg/spiffe/broker" "github.com/go-git/go-git/v5/plumbing/transport" "github.com/opencontainers/go-digest" ssh "golang.org/x/crypto/ssh" @@ -49,6 +50,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" + cryptov1 "github.com/fluxcd/pkg/apis/crypto/v1" eventv1 "github.com/fluxcd/pkg/apis/event/v1" "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/artifact/storage" @@ -66,7 +68,6 @@ import ( sourcev1 "github.com/fluxcd/source-controller/api/v1" serror "github.com/fluxcd/source-controller/internal/error" - "github.com/fluxcd/source-controller/internal/features" sreconcile "github.com/fluxcd/source-controller/internal/reconcile" "github.com/fluxcd/source-controller/internal/reconcile/summarize" "github.com/fluxcd/source-controller/internal/util" @@ -173,8 +174,10 @@ type GitRepositoryReconciler struct { ControllerName string TokenCache *cache.TokenCache + DefaultServiceAccount string + SPIFFEBroker fluxbroker.Interface + requeueDependency time.Duration - features map[string]bool patchOptions []patch.Option } @@ -193,10 +196,6 @@ func (r *GitRepositoryReconciler) SetupWithManager(mgr ctrl.Manager, opts GitRep r.requeueDependency = opts.DependencyRequeueInterval - if r.features == nil { - r.features = features.FeatureGates() - } - return ctrl.NewControllerManagedBy(mgr). For(&sourcev1.GitRepository{}, builder.WithPredicates( predicate.Or(predicate.GenerationChangedPredicate{}, predicates.ReconcileRequestedPredicate{}), @@ -694,8 +693,7 @@ func (r *GitRepositoryReconciler) getAuthOpts(ctx context.Context, obj *sourcev1 // Check if SSH identity key is encrypted but no password was provided. if opts.Transport == git.SSH && len(opts.Identity) > 0 && opts.Password == "" { _, err := ssh.ParseRawPrivateKey(opts.Identity) - var missingErr *ssh.PassphraseMissingError - if errors.As(err, &missingErr) { + if _, ok := errors.AsType[*ssh.PassphraseMissingError](err); ok { e := serror.NewGeneric( fmt.Errorf("SSH identity key is encrypted but no 'password' field was provided in the secret '%s/%s'", obj.GetNamespace(), obj.Spec.SecretRef.Name), @@ -707,45 +705,86 @@ func (r *GitRepositoryReconciler) getAuthOpts(ctx context.Context, obj *sourcev1 } // Configure provider authentication if specified. + provider := obj.GetProvider() + withCerts := len(opts.ClientCert) > 0 || len(opts.ClientKey) > 0 || len(opts.CAFile) > 0 + withAuth := obj.Spec.SecretRef != nil && + (opts.Username != "" || opts.Password != "" || opts.BearerToken != "" || len(opts.Identity) > 0 || + len(authData[githubapp.KeyAppID]) > 0 || len(authData[githubapp.KeyAppClientID]) > 0) + + // Resolve the ServiceAccount once, applying the default ServiceAccount when + // the object does not specify one. + saName := obj.Spec.ServiceAccountName + if saName == "" { + saName = r.DefaultServiceAccount + } + var sa *corev1.ServiceAccount + if saName != "" { + sa = &corev1.ServiceAccount{} + saRef := types.NamespacedName{Namespace: obj.GetNamespace(), Name: saName} + if err := r.Get(ctx, saRef, sa); err != nil { + e := serror.NewGeneric( + fmt.Errorf("failed to get service account '%s': %w", saRef, err), + sourcev1.AuthenticationFailedReason, + ) + conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e) + return nil, e + } + } + + // Build the authentication spec. This is done even when no + // ServiceAccount is specified, as some inputs are validated regardless. + var cryptoSpec *cryptov1.Spec + if sa != nil || (provider != "" && provider != sourcev1.GitProviderGeneric) { + buildOpts := []cryptov1.Option{ + cryptov1.WithServiceProvider(provider), + } + if sa != nil { + buildOpts = append(buildOpts, cryptov1.WithAnnotations(sa.Annotations)) + } + if withCerts { + buildOpts = append(buildOpts, cryptov1.WithSecretRefCerts()) + } + if withAuth { + buildOpts = append(buildOpts, cryptov1.WithSecretRefAuth()) + } + if provider == sourcev1.GitProviderGitHub && len(authData[githubapp.KeyAppBaseURL]) > 0 { + buildOpts = append(buildOpts, cryptov1.WithGitHubEnterpriseServer()) + } + var err error + cryptoSpec, err = cryptov1.BuildSpec(buildOpts...) + if err != nil { + e := serror.NewGeneric( + fmt.Errorf("failed to validate configuration: %w", err), + sourcev1.AuthenticationFailedReason, + ) + conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e) + return nil, e + } + } + var getCreds func() (*auth.GitCredentials, error) - switch provider := obj.GetProvider(); provider { + switch provider { // If other providers (GCP, etc.) are added in the future they can be added here separated by a comma. case sourcev1.GitProviderAzure, sourcev1.GitProviderAWS: getCreds = func() (*auth.GitCredentials, error) { - opts := []auth.Option{ + authOpts := auth.OptionsFromSpec(cryptoSpec.Auth) + authOpts = append(authOpts, auth.WithClient(r.Client), - auth.WithServiceAccountNamespace(obj.GetNamespace()), auth.WithGitURL(u), + auth.WithObject(sourcev1.GroupVersion.Group, sourcev1.GitRepositoryKind, obj), + auth.WithSPIFFEBroker(r.SPIFFEBroker)) + if sa != nil { + authOpts = append(authOpts, auth.WithServiceAccountName(sa.Name)) } - - if obj.Spec.ServiceAccountName != "" { - // Check object-level workload identity feature gate. - if !auth.IsObjectLevelWorkloadIdentityEnabled() { - const gate = auth.FeatureGateObjectLevelWorkloadIdentity - const msgFmt = "to use spec.serviceAccountName for provider authentication please enable the %s feature gate in the controller" - err := serror.NewStalling(fmt.Errorf(msgFmt, gate), meta.FeatureGateDisabledReason) - conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, meta.FeatureGateDisabledReason, "%s", err) - return nil, err - } - // Set ServiceAccountName only if explicitly specified - opts = append(opts, auth.WithServiceAccountName(obj.Spec.ServiceAccountName)) - } - if r.TokenCache != nil { - involvedObject := cache.InvolvedObject{ - Kind: sourcev1.GitRepositoryKind, - Name: obj.GetName(), - Namespace: obj.GetNamespace(), - Operation: cache.OperationReconcile, - } - opts = append(opts, auth.WithCache(*r.TokenCache, involvedObject)) + authOpts = append(authOpts, + auth.WithCache(*r.TokenCache), + auth.WithCacheOperation(cache.OperationReconcile)) } - if proxyURL != nil { - opts = append(opts, auth.WithProxyURL(*proxyURL)) + authOpts = append(authOpts, auth.WithProxyURL(*proxyURL)) } - - return authutils.GetGitCredentials(ctx, provider, opts...) + return authutils.GetGitCredentials(ctx, provider, authOpts...) } case sourcev1.GitProviderGitHub: // if provider is github, but secret ref is not specified @@ -779,8 +818,12 @@ func (r *GitRepositoryReconciler) getAuthOpts(ctx context.Context, obj *sourcev1 } if r.TokenCache != nil { - appOpts = append(appOpts, githubapp.WithCache(r.TokenCache, sourcev1.GitRepositoryKind, - obj.GetName(), obj.GetNamespace(), cache.OperationReconcile)) + appOpts = append(appOpts, githubapp.WithCache(r.TokenCache, cache.InvolvedObject{ + Kind: sourcev1.GitRepositoryKind, + Name: obj.GetName(), + Namespace: obj.GetNamespace(), + Operation: cache.OperationReconcile, + })) } if authMethods.HasTLS() { diff --git a/internal/controller/gitrepository_controller_test.go b/internal/controller/gitrepository_controller_test.go index 4a18c8e37..93d44c50f 100644 --- a/internal/controller/gitrepository_controller_test.go +++ b/internal/controller/gitrepository_controller_test.go @@ -39,7 +39,6 @@ import ( sshtestdata "golang.org/x/crypto/ssh/testdata" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" @@ -48,7 +47,6 @@ import ( kstatus "github.com/fluxcd/cli-utils/pkg/kstatus/status" "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/artifact/storage" - "github.com/fluxcd/pkg/auth" "github.com/fluxcd/pkg/auth/githubapp" "github.com/fluxcd/pkg/git" "github.com/fluxcd/pkg/gittestserver" @@ -62,7 +60,6 @@ import ( sourcev1 "github.com/fluxcd/source-controller/api/v1" serror "github.com/fluxcd/source-controller/internal/error" - "github.com/fluxcd/source-controller/internal/features" sreconcile "github.com/fluxcd/source-controller/internal/reconcile" "github.com/fluxcd/source-controller/internal/reconcile/summarize" ) @@ -210,7 +207,7 @@ func TestGitRepositoryReconciler_deleteBeforeFinalizer(t *testing.T) { namespaceName := "gitrepo-" + randStringRunes(5) namespace := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{Name: namespaceName}, + Name: namespaceName, } g.Expect(k8sClient.Create(ctx, namespace)).ToNot(HaveOccurred()) t.Cleanup(func() { @@ -255,10 +252,8 @@ func TestGitRepositoryReconciler_Reconcile(t *testing.T) { g.Expect(err).NotTo(HaveOccurred()) origObj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "gitrepository-reconcile-", - Namespace: "default", - }, + GenerateName: "gitrepository-reconcile-", + Namespace: "default", Spec: sourcev1.GitRepositorySpec{ Interval: metav1.Duration{Duration: interval}, URL: server.HTTPAddress() + repoPath, @@ -328,10 +323,8 @@ func TestGitRepositoryReconciler_reconcileSource_emptyRepository(t *testing.T) { defer server.StopHTTP() obj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "empty-", - Generation: 1, - }, + GenerateName: "empty-", + Generation: 1, Spec: sourcev1.GitRepositorySpec{ Interval: metav1.Duration{Duration: interval}, Timeout: &metav1.Duration{Duration: timeout}, @@ -407,9 +400,7 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) { password: "1234", }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "basic-auth", - }, + Name: "basic-auth", Data: map[string][]byte{ "username": []byte("git"), "password": []byte("1234"), @@ -433,9 +424,7 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) { ca: tlsCA, }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "mtls-certs", - }, + Name: "mtls-certs", Data: map[string][]byte{ "ca.crt": tlsCA, "tls.crt": clientPublicKey, @@ -460,9 +449,7 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) { ca: tlsCA, }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "invalid-mtls-certs", - }, + Name: "invalid-mtls-certs", Data: map[string][]byte{ "ca.crt": tlsCA, "tls.crt": clientPublicKey, @@ -490,9 +477,7 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) { ca: tlsCA, }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ca-file", - }, + Name: "ca-file", Data: map[string][]byte{ "caFile": tlsCA, }, @@ -515,9 +500,7 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) { ca: tlsCA, }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ca-file", - }, + Name: "ca-file", Data: map[string][]byte{ "ca.crt": tlsCA, "caFile": []byte("invalid"), @@ -541,9 +524,7 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) { ca: tlsCA, }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "invalid-ca", - }, + Name: "invalid-ca", Data: map[string][]byte{ "caFile": []byte("invalid"), }, @@ -574,7 +555,7 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) { ca: tlsCA, }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "gh-app-no-ca"}, + Name: "gh-app-no-ca", Data: map[string][]byte{ githubapp.KeyAppID: []byte("123"), githubapp.KeyAppInstallationID: []byte("456"), @@ -609,7 +590,7 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) { password: "some-enterprise-token", }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "gh-app-ca"}, + Name: "gh-app-ca", Data: map[string][]byte{ githubapp.KeyAppID: []byte("123"), githubapp.KeyAppInstallationID: []byte("456"), @@ -654,9 +635,7 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) { username: "git", }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "private-key", - }, + Name: "private-key", Data: map[string][]byte{ "username": []byte("git"), "identity": sshtestdata.PEMBytes["rsa"], @@ -678,9 +657,7 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) { username: "git", }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "private-key", - }, + Name: "private-key", Data: map[string][]byte{ "username": []byte("git"), "identity": sshtestdata.PEMEncryptedKeys[2].PEMBytes, @@ -722,9 +699,7 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) { password: "1234", }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "basic-auth", - }, + Name: "basic-auth", Data: map[string][]byte{ "username": []byte("git"), "password": []byte("1234"), @@ -770,9 +745,7 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) { name: "empty provider with github app data in secret makes FetchFailed=True", protocol: "http", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "github-app-secret", - }, + Name: "github-app-secret", Data: map[string][]byte{ githubapp.KeyAppID: []byte("1111"), }, @@ -796,9 +769,7 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) { name: "github provider without github app data in secret makes FetchFailed=True", protocol: "http", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "github-basic-auth", - }, + Name: "github-basic-auth", Data: map[string][]byte{ "username": []byte("abc"), "password": []byte("1234"), @@ -825,10 +796,8 @@ func TestGitRepositoryReconciler_reconcileSource_authStrategy(t *testing.T) { g := NewWithT(t) obj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "auth-strategy-", - Generation: 1, - }, + GenerateName: "auth-strategy-", + Generation: 1, Spec: sourcev1.GitRepositorySpec{ Interval: metav1.Duration{Duration: interval}, Timeout: &metav1.Duration{Duration: timeout}, @@ -954,15 +923,6 @@ func TestGitRepositoryReconciler_getAuthOpts_provider(t *testing.T) { }, wantErr: "ManagedIdentityCredential", }, - { - name: "azure provider with service account and feature gate for object-level identity disabled", - url: "https://dev.azure.com/foo/bar/_git/baz", - beforeFunc: func(obj *sourcev1.GitRepository) { - obj.Spec.Provider = sourcev1.GitProviderAzure - obj.Spec.ServiceAccountName = "azure-sa" - }, - wantErr: auth.FeatureGateObjectLevelWorkloadIdentity, - }, { name: "aws provider with non codecommit URL", url: "https://github.com/org/repo.git", @@ -971,15 +931,6 @@ func TestGitRepositoryReconciler_getAuthOpts_provider(t *testing.T) { }, wantErr: "invalid AWS CodeCommit Git URL: github.com", }, - { - name: "aws provider with service account and feature gate for object-level identity disabled", - url: "https://git-codecommit.us-east-1.amazonaws.com/v1/repos/my-repo", - beforeFunc: func(obj *sourcev1.GitRepository) { - obj.Spec.Provider = sourcev1.GitProviderAWS - obj.Spec.ServiceAccountName = "aws-sa" - }, - wantErr: auth.FeatureGateObjectLevelWorkloadIdentity, - }, { name: "github provider with no secret ref", url: "https://github.com/org/repo.git", @@ -992,9 +943,7 @@ func TestGitRepositoryReconciler_getAuthOpts_provider(t *testing.T) { name: "github provider with github app data in secret", url: "https://example.com/org/repo", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "githubAppSecret", - }, + Name: "githubAppSecret", Data: map[string][]byte{ githubapp.KeyAppID: []byte("123"), githubapp.KeyAppInstallationID: []byte("456"), @@ -1013,9 +962,7 @@ func TestGitRepositoryReconciler_getAuthOpts_provider(t *testing.T) { name: "generic provider with github app data in secret", url: "https://example.com/org/repo", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "githubAppSecret", - }, + Name: "githubAppSecret", Data: map[string][]byte{ githubapp.KeyAppID: []byte("123"), }, @@ -1032,9 +979,7 @@ func TestGitRepositoryReconciler_getAuthOpts_provider(t *testing.T) { name: "generic provider with github app client id in secret", url: "https://example.com/org/repo", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "githubAppSecret", - }, + Name: "githubAppSecret", Data: map[string][]byte{ githubapp.KeyAppClientID: []byte("Iv23liXXXXXXX"), }, @@ -1051,9 +996,7 @@ func TestGitRepositoryReconciler_getAuthOpts_provider(t *testing.T) { name: "github provider with basic auth secret", url: "https://github.com/org/repo.git", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "basic-auth-secret", - }, + Name: "basic-auth-secret", Data: map[string][]byte{ "username": []byte("abc"), "password": []byte("1234"), @@ -1105,7 +1048,6 @@ func TestGitRepositoryReconciler_getAuthOpts_provider(t *testing.T) { r := &GitRepositoryReconciler{ Recorder: events.NewFakeRecorder(32, false), Client: clientBuilder.Build(), - features: features.FeatureGates(), patchOptions: getPatchOptions(gitRepositoryReadyCondition.Owned, "sc"), } @@ -1277,7 +1219,7 @@ func TestGitRepositoryReconciler_reconcileSource_checkoutStrategy(t *testing.T) }, beforeFunc: func(obj *sourcev1.GitRepository, latestRev string) { // Set new ignore value. - obj.Spec.Ignore = ptr.To("foo") + obj.Spec.Ignore = new("foo") // Add existing artifact on the object and storage. obj.Status = sourcev1.GitRepositoryStatus{ Artifact: &meta.Artifact{ @@ -1334,10 +1276,8 @@ func TestGitRepositoryReconciler_reconcileSource_checkoutStrategy(t *testing.T) g := NewWithT(t) obj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "checkout-strategy-", - Generation: 1, - }, + GenerateName: "checkout-strategy-", + Generation: 1, Spec: sourcev1.GitRepositorySpec{ Interval: metav1.Duration{Duration: interval}, Timeout: &metav1.Duration{Duration: timeout}, @@ -1451,7 +1391,7 @@ func TestGitRepositoryReconciler_reconcileArtifact(t *testing.T) { dir: "testdata/git/repository", beforeFunc: func(obj *sourcev1.GitRepository) { obj.Spec.Interval = metav1.Duration{Duration: interval} - obj.Spec.Ignore = ptr.To("!**.txt\n") + obj.Spec.Ignore = new("!**.txt\n") }, afterFunc: func(t *WithT, obj *sourcev1.GitRepository) { t.Expect(obj.GetArtifact()).ToNot(BeNil()) @@ -1530,16 +1470,13 @@ func TestGitRepositoryReconciler_reconcileArtifact(t *testing.T) { r := &GitRepositoryReconciler{ Recorder: events.NewFakeRecorder(32, false), Storage: testStorage, - features: features.FeatureGates(), patchOptions: getPatchOptions(gitRepositoryReadyCondition.Owned, "sc"), } obj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "reconcile-artifact-", - Generation: 1, - }, - Status: sourcev1.GitRepositoryStatus{}, + GenerateName: "reconcile-artifact-", + Generation: 1, + Status: sourcev1.GitRepositoryStatus{}, } if tt.beforeFunc != nil { @@ -1651,9 +1588,7 @@ func TestGitRepositoryReconciler_reconcileInclude(t *testing.T) { var depObjs []client.Object for _, d := range tt.dependencies { obj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - Name: d.name, - }, + Name: d.name, Status: sourcev1.GitRepositoryStatus{ Conditions: d.conditions, }, @@ -1682,14 +1617,11 @@ func TestGitRepositoryReconciler_reconcileInclude(t *testing.T) { Recorder: events.NewFakeRecorder(32, false), Storage: storage, requeueDependency: dependencyInterval, - features: features.FeatureGates(), patchOptions: getPatchOptions(gitRepositoryReadyCondition.Owned, "sc"), } obj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - Name: "reconcile-include", - }, + Name: "reconcile-include", Spec: sourcev1.GitRepositorySpec{ Interval: metav1.Duration{Duration: interval}, }, @@ -1787,7 +1719,7 @@ func TestGitRepositoryReconciler_reconcileStorage(t *testing.T) { Revision: "d", Digest: "sha256:18ac3e7343f016890c510e93f935261169d9e3f565436429830faf0934f4f8e4", URL: testStorage.Hostname + "/reconcile-storage/d.txt", - Size: int64p(int64(len("d"))), + Size: new(int64(len("d"))), }, assertPaths: []string{ "/reconcile-storage/d.txt", @@ -1916,7 +1848,7 @@ func TestGitRepositoryReconciler_reconcileStorage(t *testing.T) { Revision: "f", Digest: "sha256:3b9c358f36f0a31b6ad3e14f309c7cf198ac9246e8316f9ce543d5b19ac02b80", URL: testStorage.Hostname + "/reconcile-storage/hostname.txt", - Size: int64p(int64(len("file"))), + Size: new(int64(len("file"))), }, assertConditions: []metav1.Condition{ *conditions.TrueCondition(meta.ReadyCondition, "foo", "bar"), @@ -1938,15 +1870,12 @@ func TestGitRepositoryReconciler_reconcileStorage(t *testing.T) { Build(), Recorder: events.NewFakeRecorder(32, false), Storage: testStorage, - features: features.FeatureGates(), patchOptions: getPatchOptions(gitRepositoryReadyCondition.Owned, "sc"), } obj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-", - Generation: 1, - }, + GenerateName: "test-", + Generation: 1, } if tt.beforeFunc != nil { g.Expect(tt.beforeFunc(obj, testStorage)).To(Succeed()) @@ -1992,17 +1921,14 @@ func TestGitRepositoryReconciler_reconcileDelete(t *testing.T) { r := &GitRepositoryReconciler{ Recorder: events.NewFakeRecorder(32, false), Storage: testStorage, - features: features.FeatureGates(), patchOptions: getPatchOptions(gitRepositoryReadyCondition.Owned, "sc"), } obj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - Name: "reconcile-delete-", - DeletionTimestamp: &metav1.Time{Time: time.Now()}, - Finalizers: []string{ - sourcev1.SourceFinalizer, - }, + Name: "reconcile-delete-", + DeletionTimestamp: &metav1.Time{Time: time.Now()}, + Finalizers: []string{ + sourcev1.SourceFinalizer, }, Status: sourcev1.GitRepositoryStatus{}, } @@ -2032,9 +1958,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Valid commit with mode=HEAD makes SourceVerifiedCondition=True", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "existing", - }, + Name: "existing", Data: map[string][]byte{ "foo": []byte(armoredKeyRingFixture), }, @@ -2054,7 +1978,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { } }, want: sreconcile.ResultSuccess, - wantSourceVerificationMode: ptrToVerificationMode(sourcev1.ModeGitHEAD), + wantSourceVerificationMode: new(sourcev1.ModeGitHEAD), assertConditions: []metav1.Condition{ *conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of\n\t- commit 'shasum' with key '5982D0279C227FFD'"), }, @@ -2062,9 +1986,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Valid commit with mode=head makes SourceVerifiedCondition=True", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "existing", - }, + Name: "existing", Data: map[string][]byte{ "foo": []byte(armoredKeyRingFixture), }, @@ -2084,7 +2006,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { } }, want: sreconcile.ResultSuccess, - wantSourceVerificationMode: ptrToVerificationMode(sourcev1.ModeGitHEAD), + wantSourceVerificationMode: new(sourcev1.ModeGitHEAD), assertConditions: []metav1.Condition{ *conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of\n\t- commit 'shasum' with key '5982D0279C227FFD'"), }, @@ -2092,9 +2014,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Valid tag with mode=tag makes SourceVerifiedCondition=True", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "existing", - }, + Name: "existing", Data: map[string][]byte{ "foo": []byte(armoredKeyRingFixture), }, @@ -2120,7 +2040,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { } }, want: sreconcile.ResultSuccess, - wantSourceVerificationMode: ptrToVerificationMode(sourcev1.ModeGitTag), + wantSourceVerificationMode: new(sourcev1.ModeGitTag), assertConditions: []metav1.Condition{ *conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of\n\t- tag 'v0.1.0@shasum' with key '5982D0279C227FFD'"), }, @@ -2128,9 +2048,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Valid tag and commit with mode=TagAndHEAD makes SourceVerifiedCondition=True", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "existing", - }, + Name: "existing", Data: map[string][]byte{ "foo": []byte(armoredKeyRingFixture), }, @@ -2159,7 +2077,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { } }, want: sreconcile.ResultSuccess, - wantSourceVerificationMode: ptrToVerificationMode(sourcev1.ModeGitTagAndHEAD), + wantSourceVerificationMode: new(sourcev1.ModeGitTagAndHEAD), assertConditions: []metav1.Condition{ *conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of\n\t- tag 'v0.1.0@shasum' with key '5982D0279C227FFD'\n\t- commit 'shasum' with key '5982D0279C227FFD'"), }, @@ -2167,7 +2085,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Source verification mode in status is unset if there's no verification in spec", beforeFunc: func(obj *sourcev1.GitRepository) { - obj.Status.SourceVerificationMode = ptrToVerificationMode(sourcev1.ModeGitHEAD) + obj.Status.SourceVerificationMode = new(sourcev1.ModeGitHEAD) obj.Status.SourceVerificationFingerprint = "stale-fingerprint" obj.Spec.Verification = nil }, @@ -2176,9 +2094,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Verification of tag with no tag ref SourceVerifiedCondition=False and returns a stalling error", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "existing", - }, + Name: "existing", Data: map[string][]byte{ "foo": []byte(armoredKeyRingFixture), }, @@ -2207,9 +2123,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Unsigned tag with mode=tag makes SourceVerifiedCondition=False", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "existing", - }, + Name: "existing", Data: map[string][]byte{ "foo": []byte(armoredKeyRingFixture), }, @@ -2241,9 +2155,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Partially successful verification makes SourceVerifiedCondition=False", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "existing", - }, + Name: "existing", Data: map[string][]byte{ "foo": []byte(armoredKeyRingFixture), }, @@ -2279,9 +2191,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "No PGP keys in secret makes SourceVerifiedCondition=False and returns error", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "existing", - }, + Name: "existing", }, commit: git.Commit{ Hash: []byte("shasum"), @@ -2305,9 +2215,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Invalid tag signature with mode=tag makes SourceVerifiedCondition=False", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "existing", - }, + Name: "existing", Data: map[string][]byte{ "foo": []byte(armoredKeyRingFixture), }, @@ -2340,9 +2248,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Invalid PGP key makes SourceVerifiedCondition=False and returns error", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "invalid", - }, + Name: "invalid", Data: map[string][]byte{ "foo": []byte("invalid PGP public key"), }, @@ -2404,9 +2310,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Valid SSH-signed commit with mode=HEAD makes SourceVerifiedCondition=True", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ssh-keys", - }, + Name: "ssh-keys", Data: map[string][]byte{ "author1.sshpub": []byte(sshAuthorizedKeysFixture), }, @@ -2426,7 +2330,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { } }, want: sreconcile.ResultSuccess, - wantSourceVerificationMode: ptrToVerificationMode(sourcev1.ModeGitHEAD), + wantSourceVerificationMode: new(sourcev1.ModeGitHEAD), assertConditions: []metav1.Condition{ *conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of\n\t- commit 'shasum' with key 'SHA256:SDB4adE/BP2VLwX9Pdf7aFUwW9JNdzoPSsHjd/wZIw4'"), }, @@ -2434,9 +2338,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Valid SSH-signed tag with mode=Tag makes SourceVerifiedCondition=True", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ssh-keys", - }, + Name: "ssh-keys", Data: map[string][]byte{ "author1.sshpub": []byte(sshAuthorizedKeysFixture), }, @@ -2462,7 +2364,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { } }, want: sreconcile.ResultSuccess, - wantSourceVerificationMode: ptrToVerificationMode(sourcev1.ModeGitTag), + wantSourceVerificationMode: new(sourcev1.ModeGitTag), assertConditions: []metav1.Condition{ *conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of\n\t- tag 'v0.1.0@shasum' with key 'SHA256:SDB4adE/BP2VLwX9Pdf7aFUwW9JNdzoPSsHjd/wZIw4'"), }, @@ -2470,9 +2372,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Valid SSH-signed tag and commit with mode=TagAndHEAD makes SourceVerifiedCondition=True", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ssh-keys", - }, + Name: "ssh-keys", Data: map[string][]byte{ "author1.sshpub": []byte(sshAuthorizedKeysFixture), }, @@ -2501,7 +2401,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { } }, want: sreconcile.ResultSuccess, - wantSourceVerificationMode: ptrToVerificationMode(sourcev1.ModeGitTagAndHEAD), + wantSourceVerificationMode: new(sourcev1.ModeGitTagAndHEAD), assertConditions: []metav1.Condition{ *conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of\n\t- tag 'v0.1.0@shasum' with key 'SHA256:SDB4adE/BP2VLwX9Pdf7aFUwW9JNdzoPSsHjd/wZIw4'\n\t- commit 'shasum' with key 'SHA256:SDB4adE/BP2VLwX9Pdf7aFUwW9JNdzoPSsHjd/wZIw4'"), }, @@ -2509,9 +2409,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "SSH-signed commit but Secret only has .asc keys makes SourceVerifiedCondition=False", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "pgp-keys-only", - }, + Name: "pgp-keys-only", Data: map[string][]byte{ "author1.asc": []byte(armoredKeyRingFixture), }, @@ -2538,9 +2436,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "PGP-signed commit but Secret only has .sshpub keys makes SourceVerifiedCondition=False", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ssh-keys-only", - }, + Name: "ssh-keys-only", Data: map[string][]byte{ "author1.sshpub": []byte(sshAuthorizedKeysFixture), }, @@ -2567,9 +2463,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "SSH-signed tag but Secret only has .asc keys makes SourceVerifiedCondition=False", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "pgp-keys-only", - }, + Name: "pgp-keys-only", Data: map[string][]byte{ "author1.asc": []byte(armoredKeyRingFixture), }, @@ -2602,9 +2496,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Invalid SSH key in .sshpub entry makes SourceVerifiedCondition=False", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "invalid-ssh-key", - }, + Name: "invalid-ssh-key", Data: map[string][]byte{ "author1.sshpub": []byte("ssh-ed25519 INVALIDKEY test@example.com"), }, @@ -2631,9 +2523,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Mixed: PGP-signed tag and SSH-signed commit with both key types in Secret and mode=TagAndHEAD makes SourceVerifiedCondition=True", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "mixed-keys", - }, + Name: "mixed-keys", Data: map[string][]byte{ "author1.asc": []byte(armoredKeyRingFixture), "author1.sshpub": []byte(sshAuthorizedKeysFixture), @@ -2663,7 +2553,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { } }, want: sreconcile.ResultSuccess, - wantSourceVerificationMode: ptrToVerificationMode(sourcev1.ModeGitTagAndHEAD), + wantSourceVerificationMode: new(sourcev1.ModeGitTagAndHEAD), assertConditions: []metav1.Condition{ *conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of\n\t- tag 'v0.1.0@shasum' with key '5982D0279C227FFD'\n\t- commit 'shasum' with key 'SHA256:SDB4adE/BP2VLwX9Pdf7aFUwW9JNdzoPSsHjd/wZIw4'"), }, @@ -2671,9 +2561,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Mixed: SSH-signed tag and PGP-signed commit with both key types in Secret and mode=TagAndHEAD makes SourceVerifiedCondition=True", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "mixed-keys-reverse", - }, + Name: "mixed-keys-reverse", Data: map[string][]byte{ "author1.asc": []byte(armoredKeyRingFixture), "author1.sshpub": []byte(sshAuthorizedKeysFixture), @@ -2703,7 +2591,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { } }, want: sreconcile.ResultSuccess, - wantSourceVerificationMode: ptrToVerificationMode(sourcev1.ModeGitTagAndHEAD), + wantSourceVerificationMode: new(sourcev1.ModeGitTagAndHEAD), assertConditions: []metav1.Condition{ *conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of\n\t- tag 'v0.1.0@shasum' with key 'SHA256:SDB4adE/BP2VLwX9Pdf7aFUwW9JNdzoPSsHjd/wZIw4'\n\t- commit 'shasum' with key '5982D0279C227FFD'"), }, @@ -2711,9 +2599,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Unsupported signature type makes SourceVerifiedCondition=False", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "existing", - }, + Name: "existing", Data: map[string][]byte{ "foo": []byte(armoredKeyRingFixture), }, @@ -2740,9 +2626,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { { name: "Valid commit with explicit .asc key suffix makes SourceVerifiedCondition=True", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "explicit-asc", - }, + Name: "explicit-asc", Data: map[string][]byte{ "foo.asc": []byte(armoredKeyRingFixture), }, @@ -2762,7 +2646,7 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { } }, want: sreconcile.ResultSuccess, - wantSourceVerificationMode: ptrToVerificationMode(sourcev1.ModeGitHEAD), + wantSourceVerificationMode: new(sourcev1.ModeGitHEAD), assertConditions: []metav1.Condition{ *conditions.TrueCondition(sourcev1.SourceVerifiedCondition, meta.SucceededReason, "verified signature of\n\t- commit 'shasum' with key '5982D0279C227FFD'"), }, @@ -2784,16 +2668,13 @@ func TestGitRepositoryReconciler_verifySignature(t *testing.T) { r := &GitRepositoryReconciler{ Recorder: events.NewFakeRecorder(32, false), Client: clientBuilder.Build(), - features: features.FeatureGates(), patchOptions: getPatchOptions(gitRepositoryReadyCondition.Owned, "sc"), } obj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "verify-commit-", - Generation: 1, - }, - Status: sourcev1.GitRepositoryStatus{}, + GenerateName: "verify-commit-", + Generation: 1, + Status: sourcev1.GitRepositoryStatus{}, } if tt.beforeFunc != nil { @@ -2916,11 +2797,9 @@ func TestGitRepositoryReconciler_ConditionsUpdate(t *testing.T) { g := NewWithT(t) obj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - Name: "condition-update", - Namespace: "default", - Finalizers: []string{sourcev1.SourceFinalizer}, - }, + Name: "condition-update", + Namespace: "default", + Finalizers: []string{sourcev1.SourceFinalizer}, Spec: sourcev1.GitRepositorySpec{ URL: server.HTTPAddress() + repoPath, Interval: metav1.Duration{Duration: interval}, @@ -2941,7 +2820,6 @@ func TestGitRepositoryReconciler_ConditionsUpdate(t *testing.T) { Client: clientBuilder.Build(), Recorder: events.NewFakeRecorder(32, false), Storage: testStorage, - features: features.FeatureGates(), patchOptions: getPatchOptions(gitRepositoryReadyCondition.Owned, "sc"), } @@ -3165,14 +3043,10 @@ func TestGitRepositoryReconciler_statusConditions(t *testing.T) { g := NewWithT(t) obj := &sourcev1.GitRepository{ - TypeMeta: metav1.TypeMeta{ - APIVersion: sourcev1.GroupVersion.String(), - Kind: sourcev1.GitRepositoryKind, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gitrepo", - Namespace: "foo", - }, + APIVersion: sourcev1.GroupVersion.String(), + Kind: sourcev1.GitRepositoryKind, + Name: "gitrepo", + Namespace: "foo", } clientBuilder := fakeclient.NewClientBuilder(). @@ -3347,7 +3221,6 @@ func TestGitRepositoryReconciler_notify(t *testing.T) { reconciler := &GitRepositoryReconciler{ Recorder: recorder, - features: features.FeatureGates(), patchOptions: getPatchOptions(gitRepositoryReadyCondition.Owned, "sc"), } reconciler.notify(ctx, oldObj, newObj, tt.commit, tt.res, tt.resErr) @@ -3466,9 +3339,7 @@ func TestGitRepositoryReconciler_fetchIncludes(t *testing.T) { var depObjs []client.Object for _, d := range tt.dependencies { obj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - Name: d.name, - }, + Name: d.name, Status: sourcev1.GitRepositoryStatus{ Conditions: d.conditions, }, @@ -3498,9 +3369,7 @@ func TestGitRepositoryReconciler_fetchIncludes(t *testing.T) { } obj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - Name: "reconcile-include", - }, + Name: "reconcile-include", Spec: sourcev1.GitRepositorySpec{ Interval: metav1.Duration{Duration: interval}, }, @@ -3708,15 +3577,15 @@ func TestGitContentConfigChanged(t *testing.T) { { name: "unobserved ignore", obj: sourcev1.GitRepository{ - Spec: sourcev1.GitRepositorySpec{Ignore: ptr.To("foo")}, + Spec: sourcev1.GitRepositorySpec{Ignore: new("foo")}, }, want: true, }, { name: "observed ignore", obj: sourcev1.GitRepository{ - Spec: sourcev1.GitRepositorySpec{Ignore: ptr.To("foo")}, - Status: sourcev1.GitRepositoryStatus{ObservedIgnore: ptr.To("foo")}, + Spec: sourcev1.GitRepositorySpec{Ignore: new("foo")}, + Status: sourcev1.GitRepositoryStatus{ObservedIgnore: new("foo")}, }, want: false, }, @@ -4045,7 +3914,7 @@ func Test_requiresVerification(t *testing.T) { }, }, Status: sourcev1.GitRepositoryStatus{ - SourceVerificationMode: ptrToVerificationMode(sourcev1.ModeGitTag), + SourceVerificationMode: new(sourcev1.ModeGitTag), }, }, want: true, @@ -4059,7 +3928,7 @@ func Test_requiresVerification(t *testing.T) { }, }, Status: sourcev1.GitRepositoryStatus{ - SourceVerificationMode: ptrToVerificationMode(sourcev1.ModeGitTag), + SourceVerificationMode: new(sourcev1.ModeGitTag), }, }, want: true, @@ -4073,7 +3942,7 @@ func Test_requiresVerification(t *testing.T) { }, }, Status: sourcev1.GitRepositoryStatus{ - SourceVerificationMode: ptrToVerificationMode(sourcev1.ModeGitHEAD), + SourceVerificationMode: new(sourcev1.ModeGitHEAD), }, }, want: true, @@ -4087,7 +3956,7 @@ func Test_requiresVerification(t *testing.T) { }, }, Status: sourcev1.GitRepositoryStatus{ - SourceVerificationMode: ptrToVerificationMode(sourcev1.ModeGitHEAD), + SourceVerificationMode: new(sourcev1.ModeGitHEAD), }, }, want: true, @@ -4101,7 +3970,7 @@ func Test_requiresVerification(t *testing.T) { }, }, Status: sourcev1.GitRepositoryStatus{ - SourceVerificationMode: ptrToVerificationMode(sourcev1.ModeGitTagAndHEAD), + SourceVerificationMode: new(sourcev1.ModeGitTagAndHEAD), }, }, want: false, @@ -4115,7 +3984,7 @@ func Test_requiresVerification(t *testing.T) { }, }, Status: sourcev1.GitRepositoryStatus{ - SourceVerificationMode: ptrToVerificationMode(sourcev1.ModeGitTagAndHEAD), + SourceVerificationMode: new(sourcev1.ModeGitTagAndHEAD), }, }, want: false, @@ -4161,17 +4030,17 @@ func Test_verificationFingerprint(t *testing.T) { func TestGitRepositoryReconciler_verificationPolicyChanged(t *testing.T) { secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "keys", Namespace: "default"}, - Data: map[string][]byte{"foo": []byte(armoredKeyRingFixture)}, + Name: "keys", Namespace: "default", + Data: map[string][]byte{"foo": []byte(armoredKeyRingFixture)}, } rotatedSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "keys", Namespace: "default"}, - Data: map[string][]byte{"foo": []byte(armoredKeyRingFixture + "\n")}, + Name: "keys", Namespace: "default", + Data: map[string][]byte{"foo": []byte(armoredKeyRingFixture + "\n")}, } newObj := func(mode sourcev1.GitVerificationMode, fingerprint string) *sourcev1.GitRepository { obj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{Name: "repo", Namespace: "default"}, + Name: "repo", Namespace: "default", Spec: sourcev1.GitRepositorySpec{ Verification: &sourcev1.GitRepositoryVerification{ Mode: mode, @@ -4262,8 +4131,8 @@ func TestGitRepositoryReconciler_reconcileSource_verificationPolicyChange(t *tes g.Expect(remoteBranchForHead(localRepo, headRef, "staging")).To(Succeed()) secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "keys", Namespace: "default"}, - Data: map[string][]byte{"foo": []byte(armoredKeyRingFixture)}, + Name: "keys", Namespace: "default", + Data: map[string][]byte{"foo": []byte(armoredKeyRingFixture)}, } tests := []struct { @@ -4287,11 +4156,9 @@ func TestGitRepositoryReconciler_reconcileSource_verificationPolicyChange(t *tes g := NewWithT(t) obj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "verification-policy-", - Namespace: "default", - Generation: 1, - }, + GenerateName: "verification-policy-", + Namespace: "default", + Generation: 1, Spec: sourcev1.GitRepositorySpec{ Interval: metav1.Duration{Duration: interval}, Timeout: &metav1.Duration{Duration: timeout}, @@ -4309,7 +4176,7 @@ func TestGitRepositoryReconciler_reconcileSource_verificationPolicyChange(t *tes Revision: "staging@sha1:" + headRef.Hash().String(), Path: randStringRunes(10), }, - SourceVerificationMode: ptrToVerificationMode(sourcev1.ModeGitHEAD), + SourceVerificationMode: new(sourcev1.ModeGitHEAD), SourceVerificationFingerprint: tt.fingerprint, }, } @@ -4344,6 +4211,7 @@ func TestGitRepositoryReconciler_reconcileSource_verificationPolicyChange(t *tes } } +//go:fix inline func ptrToVerificationMode(mode sourcev1.GitVerificationMode) *sourcev1.GitVerificationMode { - return &mode + return new(mode) } diff --git a/internal/controller/helmchart_controller_test.go b/internal/controller/helmchart_controller_test.go index 762dce5c6..c10943c60 100644 --- a/internal/controller/helmchart_controller_test.go +++ b/internal/controller/helmchart_controller_test.go @@ -86,7 +86,7 @@ func TestHelmChartReconciler_deleteBeforeFinalizer(t *testing.T) { namespaceName := "helmchart-" + randStringRunes(5) namespace := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{Name: namespaceName}, + Name: namespaceName, } g.Expect(k8sClient.Create(ctx, namespace)).ToNot(HaveOccurred()) t.Cleanup(func() { @@ -293,10 +293,8 @@ func TestHelmChartReconciler_Reconcile(t *testing.T) { defer func() { g.Expect(testEnv.Delete(ctx, ns)).To(Succeed()) }() repository := sourcev1.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "helmrepository-", - Namespace: ns.Name, - }, + GenerateName: "helmrepository-", + Namespace: ns.Name, Spec: sourcev1.HelmRepositorySpec{ URL: server.URL(), }, @@ -310,10 +308,8 @@ func TestHelmChartReconciler_Reconcile(t *testing.T) { defer func() { g.Expect(testEnv.Delete(ctx, &repository)).To(Succeed()) }() obj := sourcev1.HelmChart{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "helmrepository-reconcile-", - Namespace: ns.Name, - }, + GenerateName: "helmrepository-reconcile-", + Namespace: ns.Name, Spec: sourcev1.HelmChartSpec{ Chart: chartName, Version: chartVersion, @@ -371,7 +367,7 @@ func TestHelmChartReconciler_reconcileStorage(t *testing.T) { Revision: "d", Digest: "sha256:18ac3e7343f016890c510e93f935261169d9e3f565436429830faf0934f4f8e4", URL: testStorage.Hostname + "/reconcile-storage/d.txt", - Size: int64p(int64(len("d"))), + Size: new(int64(len("d"))), }, assertPaths: []string{ "/reconcile-storage/d.txt", @@ -500,7 +496,7 @@ func TestHelmChartReconciler_reconcileStorage(t *testing.T) { Revision: "f", Digest: "sha256:3b9c358f36f0a31b6ad3e14f309c7cf198ac9246e8316f9ce543d5b19ac02b80", URL: testStorage.Hostname + "/reconcile-storage/hostname.txt", - Size: int64p(int64(len("file"))), + Size: new(int64(len("file"))), }, assertConditions: []metav1.Condition{ *conditions.TrueCondition(meta.ReadyCondition, "foo", "bar"), @@ -526,10 +522,8 @@ func TestHelmChartReconciler_reconcileStorage(t *testing.T) { } obj := &sourcev1.HelmChart{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-", - Generation: 1, - }, + GenerateName: "test-", + Generation: 1, } if tt.beforeFunc != nil { g.Expect(tt.beforeFunc(obj, testStorage)).To(Succeed()) @@ -602,10 +596,8 @@ func TestHelmChartReconciler_reconcileSource(t *testing.T) { { name: "Observes Artifact revision and build result", source: &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gitrepository", - Namespace: "default", - }, + Name: "gitrepository", + Namespace: "default", Status: sourcev1.GitRepositoryStatus{ Artifact: gitArtifact, }, @@ -637,10 +629,8 @@ func TestHelmChartReconciler_reconcileSource(t *testing.T) { { name: "Existing artifact makes AritfactOutdated=True", source: &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gitrepository", - Namespace: "default", - }, + Name: "gitrepository", + Namespace: "default", Status: sourcev1.GitRepositoryStatus{ Artifact: gitArtifact, }, @@ -718,10 +708,8 @@ func TestHelmChartReconciler_reconcileSource(t *testing.T) { { name: "Stalling on persistent build error", source: &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gitrepository", - Namespace: "default", - }, + Name: "gitrepository", + Namespace: "default", Status: sourcev1.GitRepositoryStatus{ Artifact: gitArtifact, }, @@ -751,11 +739,9 @@ func TestHelmChartReconciler_reconcileSource(t *testing.T) { { name: "ResultRequeue when source artifact is unavailable", source: &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - Name: "gitrepository", - Namespace: "default", - }, - Status: sourcev1.GitRepositoryStatus{}, + Name: "gitrepository", + Namespace: "default", + Status: sourcev1.GitRepositoryStatus{}, }, beforeFunc: func(obj *sourcev1.HelmChart) { obj.Spec.Chart = "testdata/charts/helmchart-0.1.0.tgz" @@ -801,12 +787,10 @@ func TestHelmChartReconciler_reconcileSource(t *testing.T) { } obj := sourcev1.HelmChart{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "chart", - Namespace: "default", - Generation: 1, - }, - Spec: sourcev1.HelmChartSpec{}, + GenerateName: "chart", + Namespace: "default", + Generation: 1, + Spec: sourcev1.HelmChartSpec{}, } if tt.beforeFunc != nil { tt.beforeFunc(&obj) @@ -901,9 +885,7 @@ func TestHelmChartReconciler_buildFromHelmRepository(t *testing.T) { password: "bar", }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth", - }, + Name: "auth", Data: map[string][]byte{ "username": []byte("foo"), "password": []byte("bar"), @@ -1139,9 +1121,7 @@ func TestHelmChartReconciler_buildFromHelmRepository(t *testing.T) { } repository := &sourcev1.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "helmrepository-", - }, + GenerateName: "helmrepository-", Spec: sourcev1.HelmRepositorySpec{ URL: server.URL(), Timeout: &metav1.Duration{Duration: timeout}, @@ -1153,10 +1133,8 @@ func TestHelmChartReconciler_buildFromHelmRepository(t *testing.T) { }, } obj := &sourcev1.HelmChart{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "helmrepository-", - }, - Spec: sourcev1.HelmChartSpec{}, + GenerateName: "helmrepository-", + Spec: sourcev1.HelmChartSpec{}, } if tt.beforeFunc != nil { @@ -1229,9 +1207,7 @@ func TestHelmChartReconciler_buildFromOCIHelmRepository(t *testing.T) { { name: "Reconciles chart build with docker repository credentials", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth", - }, + Name: "auth", Type: corev1.SecretTypeDockerConfigJson, Data: map[string][]byte{ ".dockerconfigjson": []byte(`{"auths":{"` + @@ -1258,9 +1234,7 @@ func TestHelmChartReconciler_buildFromOCIHelmRepository(t *testing.T) { { name: "Reconciles chart build with repository credentials", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth", - }, + Name: "auth", Data: map[string][]byte{ "username": []byte(testRegistryUsername), "password": []byte(testRegistryPassword), @@ -1301,9 +1275,7 @@ func TestHelmChartReconciler_buildFromOCIHelmRepository(t *testing.T) { { name: "Forces build on generation change", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth", - }, + Name: "auth", Data: map[string][]byte{ "username": []byte(testRegistryUsername), "password": []byte(testRegistryPassword), @@ -1392,9 +1364,7 @@ func TestHelmChartReconciler_buildFromOCIHelmRepository(t *testing.T) { } repository := &sourcev1.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "helmrepository-", - }, + GenerateName: "helmrepository-", Spec: sourcev1.HelmRepositorySpec{ URL: fmt.Sprintf("oci://%s/testrepo", testRegistryServer.registryHost), Timeout: &metav1.Duration{Duration: timeout}, @@ -1404,10 +1374,8 @@ func TestHelmChartReconciler_buildFromOCIHelmRepository(t *testing.T) { }, } obj := &sourcev1.HelmChart{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "helmrepository-", - }, - Spec: sourcev1.HelmChartSpec{}, + GenerateName: "helmrepository-", + Spec: sourcev1.HelmChartSpec{}, } if tt.beforeFunc != nil { @@ -1634,11 +1602,9 @@ func TestHelmChartReconciler_buildFromTarballArtifact(t *testing.T) { } obj := &sourcev1.HelmChart{ - ObjectMeta: metav1.ObjectMeta{ - Name: "artifact", - Namespace: "default", - }, - Spec: sourcev1.HelmChartSpec{}, + Name: "artifact", + Namespace: "default", + Spec: sourcev1.HelmChartSpec{}, } if tt.beforeFunc != nil { tt.beforeFunc(obj) @@ -1844,11 +1810,9 @@ func TestHelmChartReconciler_reconcileArtifact(t *testing.T) { } obj := &sourcev1.HelmChart{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "reconcile-artifact-", - Generation: 1, - }, - Status: sourcev1.HelmChartStatus{}, + GenerateName: "reconcile-artifact-", + Generation: 1, + Status: sourcev1.HelmChartStatus{}, } if tt.beforeFunc != nil { tt.beforeFunc(obj) @@ -1875,34 +1839,22 @@ func TestHelmChartReconciler_reconcileArtifact(t *testing.T) { func TestHelmChartReconciler_getSource(t *testing.T) { mocks := []client.Object{ &sourcev1.HelmRepository{ - TypeMeta: metav1.TypeMeta{ - Kind: sourcev1.HelmRepositoryKind, - APIVersion: sourcev1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "helmrepository", - Namespace: "foo", - }, + Kind: sourcev1.HelmRepositoryKind, + APIVersion: sourcev1.GroupVersion.String(), + Name: "helmrepository", + Namespace: "foo", }, &sourcev1.GitRepository{ - TypeMeta: metav1.TypeMeta{ - Kind: sourcev1.GitRepositoryKind, - APIVersion: sourcev1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "gitrepository", - Namespace: "foo", - }, + Kind: sourcev1.GitRepositoryKind, + APIVersion: sourcev1.GroupVersion.String(), + Name: "gitrepository", + Namespace: "foo", }, &sourcev1.Bucket{ - TypeMeta: metav1.TypeMeta{ - Kind: sourcev1.BucketKind, - APIVersion: sourcev1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "bucket", - Namespace: "foo", - }, + Kind: sourcev1.BucketKind, + APIVersion: sourcev1.GroupVersion.String(), + Name: "bucket", + Namespace: "foo", }, } @@ -1925,9 +1877,7 @@ func TestHelmChartReconciler_getSource(t *testing.T) { { name: "Get HelmRepository source for reference", obj: &sourcev1.HelmChart{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: mocks[0].GetNamespace(), - }, + Namespace: mocks[0].GetNamespace(), Spec: sourcev1.HelmChartSpec{ SourceRef: sourcev1.LocalHelmChartSourceReference{ Name: mocks[0].GetName(), @@ -1940,9 +1890,7 @@ func TestHelmChartReconciler_getSource(t *testing.T) { { name: "Get GitRepository source for reference", obj: &sourcev1.HelmChart{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: mocks[1].GetNamespace(), - }, + Namespace: mocks[1].GetNamespace(), Spec: sourcev1.HelmChartSpec{ SourceRef: sourcev1.LocalHelmChartSourceReference{ Name: mocks[1].GetName(), @@ -1955,9 +1903,7 @@ func TestHelmChartReconciler_getSource(t *testing.T) { { name: "Get Bucket source for reference", obj: &sourcev1.HelmChart{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: mocks[2].GetNamespace(), - }, + Namespace: mocks[2].GetNamespace(), Spec: sourcev1.HelmChartSpec{ SourceRef: sourcev1.LocalHelmChartSourceReference{ Name: mocks[2].GetName(), @@ -1970,9 +1916,7 @@ func TestHelmChartReconciler_getSource(t *testing.T) { { name: "Error on client error", obj: &sourcev1.HelmChart{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: mocks[2].GetNamespace(), - }, + Namespace: mocks[2].GetNamespace(), Spec: sourcev1.HelmChartSpec{ SourceRef: sourcev1.LocalHelmChartSourceReference{ Name: mocks[1].GetName(), @@ -2035,12 +1979,10 @@ func TestHelmChartReconciler_reconcileDelete(t *testing.T) { } obj := &sourcev1.HelmChart{ - ObjectMeta: metav1.ObjectMeta{ - Name: "reconcile-delete-", - DeletionTimestamp: &metav1.Time{Time: time.Now()}, - Finalizers: []string{ - sourcev1.SourceFinalizer, - }, + Name: "reconcile-delete-", + DeletionTimestamp: &metav1.Time{Time: time.Now()}, + Finalizers: []string{ + sourcev1.SourceFinalizer, }, Status: sourcev1.HelmChartStatus{}, } @@ -2173,10 +2115,8 @@ func TestHelmChartReconciler_reconcileSubRecs(t *testing.T) { patchOptions: getPatchOptions(helmChartReadyCondition.Owned, "sc"), } obj := &sourcev1.HelmChart{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-", - Generation: tt.generation, - }, + GenerateName: "test-", + Generation: tt.generation, Status: sourcev1.HelmChartStatus{ ObservedGeneration: tt.observedGeneration, }, @@ -2275,14 +2215,10 @@ func TestHelmChartReconciler_statusConditions(t *testing.T) { g := NewWithT(t) obj := &sourcev1.HelmChart{ - TypeMeta: metav1.TypeMeta{ - Kind: sourcev1.HelmChartKind, - APIVersion: sourcev1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "helmchart", - Namespace: "foo", - }, + Kind: sourcev1.HelmChartKind, + APIVersion: sourcev1.GroupVersion.String(), + Name: "helmchart", + Namespace: "foo", } clientBuilder := fakeclient.NewClientBuilder(). @@ -2493,9 +2429,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { password: testRegistryPassword, }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth-secretref", - }, + Name: "auth-secretref", Type: corev1.SecretTypeDockerConfigJson, Data: map[string][]byte{}, }, @@ -2517,9 +2451,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { password: "wrong-pass", }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth-secretref", - }, + Name: "auth-secretref", Type: corev1.SecretTypeDockerConfigJson, Data: map[string][]byte{}, }, @@ -2534,7 +2466,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { provider: "aws", providerImg: "oci://123456789000.dkr.ecr.us-east-2.amazonaws.com/test", assertConditions: []metav1.Condition{ - *conditions.TrueCondition(sourcev1.FetchFailedCondition, "Unknown", "unknown build error: unable to get authentication data from OIDC: failed to get access token for artifact registry"), + *conditions.TrueCondition(sourcev1.FetchFailedCondition, "Unknown", "unknown build error: unable to get authentication data from OIDC: failed to get access credential for artifact registry"), }, }, { @@ -2549,9 +2481,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { password: testRegistryPassword, }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth-secretref", - }, + Name: "auth-secretref", Type: corev1.SecretTypeDockerConfigJson, Data: map[string][]byte{}, }, @@ -2573,16 +2503,12 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { password: testRegistryPassword, }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth-secretref", - }, + Name: "auth-secretref", Type: corev1.SecretTypeDockerConfigJson, Data: map[string][]byte{}, }, certSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "certs-secretref", - }, + Name: "certs-secretref", Data: map[string][]byte{ "ca.crt": []byte("invalid caFile"), }, @@ -2598,9 +2524,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { withTLS: true, }, certSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "certs-secretref", - }, + Name: "certs-secretref", Type: corev1.SecretTypeOpaque, Data: map[string][]byte{ "ca.crt": tlsCA, @@ -2619,9 +2543,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { withClientCertAuth: true, }, certSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "certs-secretref", - }, + Name: "certs-secretref", Data: map[string][]byte{ "ca.crt": tlsCA, "tls.crt": clientPublicKey, @@ -2643,9 +2565,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { withClientCertAuth: true, }, certSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "certs-secretref", - }, + Name: "certs-secretref", Data: map[string][]byte{ "ca.crt": tlsCA, "tls.crt": clientInvalidPublicKey, @@ -2683,9 +2603,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { g.Expect(err).ToNot(HaveOccurred()) repo := &sourcev1.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "auth-strategy-", - }, + GenerateName: "auth-strategy-", Spec: sourcev1.HelmRepositorySpec{ Interval: metav1.Duration{Duration: interval}, Timeout: &metav1.Duration{Duration: timeout}, @@ -2728,9 +2646,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_authStrategy(t *testing.T) { clientBuilder.WithObjects(repo) obj := &sourcev1.HelmChart{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "auth-strategy-", - }, + GenerateName: "auth-strategy-", Spec: sourcev1.HelmChartSpec{ Chart: metadata.Name, Version: metadata.Version, @@ -2889,9 +2805,7 @@ func TestHelmChartRepository_reconcileSource_verifyOCISourceSignature_keyless(t clientBuilder := fakeclient.NewClientBuilder() repository := &sourcev1.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "helmrepository-", - }, + GenerateName: "helmrepository-", Spec: sourcev1.HelmRepositorySpec{ URL: "oci://ghcr.io/stefanprodan/charts", Timeout: &metav1.Duration{Duration: timeout}, @@ -2911,9 +2825,7 @@ func TestHelmChartRepository_reconcileSource_verifyOCISourceSignature_keyless(t } obj := &sourcev1.HelmChart{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "helmchart-", - }, + GenerateName: "helmchart-", Spec: sourcev1.HelmChartSpec{ SourceRef: sourcev1.LocalHelmChartSourceReference{ Kind: sourcev1.HelmRepositoryKind, @@ -3158,9 +3070,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_verifySignatureNotation(t *t clientBuilder := fakeclient.NewClientBuilder() repository := &sourcev1.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "helmrepository-", - }, + GenerateName: "helmrepository-", Spec: sourcev1.HelmRepositorySpec{ URL: fmt.Sprintf("oci://%s/testrepo", server.registryHost), Timeout: &metav1.Duration{Duration: timeout}, @@ -3190,17 +3100,13 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_verifySignatureNotation(t *t } secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "notation-config", - }, + Name: "notation-config", Data: data, } caSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "valid-trust-store", - Generation: 1, - }, + Name: "valid-trust-store", + Generation: 1, Data: map[string][]byte{ "ca.crt": tlsCA, }, @@ -3218,9 +3124,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_verifySignatureNotation(t *t } obj := &sourcev1.HelmChart{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "helmchart-", - }, + GenerateName: "helmchart-", Spec: sourcev1.HelmChartSpec{ SourceRef: sourcev1.LocalHelmChartSourceReference{ Kind: sourcev1.HelmRepositoryKind, @@ -3250,10 +3154,8 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_verifySignatureNotation(t *t signatureMediaType := cose.MediaTypeEnvelope signOptions := notation.SignOptions{ - SignerSignOptions: notation.SignerSignOptions{ - SignatureMediaType: signatureMediaType, - }, - ArtifactReference: artifact, + SignatureMediaType: signatureMediaType, + ArtifactReference: artifact, } _, err = notation.Sign(ctx, sg, repo, signOptions) @@ -3440,9 +3342,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_verifySignatureCosign(t *tes clientBuilder := fakeclient.NewClientBuilder() repository := &sourcev1.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "helmrepository-", - }, + GenerateName: "helmrepository-", Spec: sourcev1.HelmRepositorySpec{ URL: fmt.Sprintf("oci://%s/testrepo", server.registryHost), Timeout: &metav1.Duration{Duration: timeout}, @@ -3453,9 +3353,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_verifySignatureCosign(t *tes } secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "cosign-key", - }, + Name: "cosign-key", Data: map[string][]byte{ "cosign.pub": keys.PublicBytes, }} @@ -3472,9 +3370,7 @@ func TestHelmChartReconciler_reconcileSourceFromOCI_verifySignatureCosign(t *tes } obj := &sourcev1.HelmChart{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "helmchart-", - }, + GenerateName: "helmchart-", Spec: sourcev1.HelmChartSpec{ SourceRef: sourcev1.LocalHelmChartSourceReference{ Kind: sourcev1.HelmRepositoryKind, diff --git a/internal/controller/helmrepository_controller.go b/internal/controller/helmrepository_controller.go index 7266e6e89..d3a966fe3 100644 --- a/internal/controller/helmrepository_controller.go +++ b/internal/controller/helmrepository_controller.go @@ -390,7 +390,7 @@ func (r *HelmRepositoryReconciler) reconcileStorage(ctx context.Context, sp *pat // v1.FetchFailedCondition is removed, and the repository.ChartRepository // pointer is set to the newly fetched index. func (r *HelmRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch.SerialPatcher, - obj *sourcev1.HelmRepository, artifact *meta.Artifact, chartRepo *repository.ChartRepository) (sreconcile.Result, error) { + obj *sourcev1.HelmRepository, artifact *meta.Artifact, chartRepo *repository.ChartRepository) (_ sreconcile.Result, retErr error) { // Ensure it's not an OCI URL. API validation ensures that only // http/https/oci scheme are allowed. if helmreg.IsOCI(obj.Spec.URL) { @@ -461,6 +461,16 @@ func (r *HelmRepositoryReconciler) reconcileSource(ctx context.Context, sp *patc } *chartRepo = *newChartRepo + // reconcileArtifact removes the cached index on success. On error the + // deferred cleanup below removes it, covering error paths added later. + defer func() { + if retErr != nil { + if err := chartRepo.Clear(); err != nil { + ctrl.LoggerFrom(ctx).Error(err, "failed to remove temporary cached index file") + } + } + }() + // Early comparison to current Artifact. if curArtifact := obj.GetArtifact(); curArtifact != nil { curRev := digest.Digest(curArtifact.Revision) @@ -478,7 +488,7 @@ func (r *HelmRepositoryReconciler) reconcileSource(ctx context.Context, sp *patc // Load the cached repository index to ensure it passes validation. if err := chartRepo.LoadFromPath(); err != nil { e := serror.NewGeneric( - fmt.Errorf("failed to load Helm repository from index YAML: %w", err), + fmt.Errorf("failed to load Helm repository from index YAML at %s: %w", redactURL(obj.Spec.URL), err), sourcev1.IndexationFailedReason, ) conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e) @@ -491,7 +501,7 @@ func (r *HelmRepositoryReconciler) reconcileSource(ctx context.Context, sp *patc revision := chartRepo.Digest(intdigest.Canonical) if revision.Validate() != nil { e := serror.NewGeneric( - fmt.Errorf("failed to calculate revision: %w", err), + fmt.Errorf("failed to calculate revision: invalid digest '%s'", revision), sourcev1.IndexationFailedReason, ) conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e) @@ -722,3 +732,13 @@ func (r *HelmRepositoryReconciler) migrationToStatic(ctx context.Context, sp *pa return ctrl.Result{}, nil } + +// redactURL returns the given URL with the password of its user info +// replaced, so it can be added to conditions and events. +func redactURL(repoURL string) string { + u, err := url.Parse(repoURL) + if err != nil { + return "" + } + return u.Redacted() +} diff --git a/internal/controller/helmrepository_controller_test.go b/internal/controller/helmrepository_controller_test.go index c34c49d27..fb6a628f5 100644 --- a/internal/controller/helmrepository_controller_test.go +++ b/internal/controller/helmrepository_controller_test.go @@ -38,6 +38,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/controller-runtime/pkg/client/interceptor" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" kstatus "github.com/fluxcd/cli-utils/pkg/kstatus/status" @@ -64,7 +65,7 @@ func TestHelmRepositoryReconciler_deleteBeforeFinalizer(t *testing.T) { namespaceName := "helmrepo-" + randStringRunes(5) namespace := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{Name: namespaceName}, + Name: namespaceName, } g.Expect(k8sClient.Create(ctx, namespace)).ToNot(HaveOccurred()) t.Cleanup(func() { @@ -108,10 +109,8 @@ func TestHelmRepositoryReconciler_Reconcile(t *testing.T) { defer testServer.Stop() origObj := &sourcev1.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "helmrepository-reconcile-", - Namespace: "default", - }, + GenerateName: "helmrepository-reconcile-", + Namespace: "default", Spec: sourcev1.HelmRepositorySpec{ Interval: metav1.Duration{Duration: interval}, URL: testServer.URL(), @@ -210,7 +209,7 @@ func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) { Revision: "d", Digest: "sha256:18ac3e7343f016890c510e93f935261169d9e3f565436429830faf0934f4f8e4", URL: testStorage.Hostname + "/reconcile-storage/d.txt", - Size: int64p(int64(len("d"))), + Size: new(int64(len("d"))), }, assertPaths: []string{ "/reconcile-storage/d.txt", @@ -342,7 +341,7 @@ func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) { Revision: "f", Digest: "sha256:3b9c358f36f0a31b6ad3e14f309c7cf198ac9246e8316f9ce543d5b19ac02b80", URL: testStorage.Hostname + "/reconcile-storage/hostname.txt", - Size: int64p(int64(len("file"))), + Size: new(int64(len("file"))), }, assertConditions: []metav1.Condition{ *conditions.TrueCondition(meta.ReadyCondition, "foo", "bar"), @@ -366,10 +365,8 @@ func TestHelmRepositoryReconciler_reconcileStorage(t *testing.T) { } obj := &sourcev1.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-", - Generation: 1, - }, + GenerateName: "test-", + Generation: 1, } if tt.beforeFunc != nil { g.Expect(tt.beforeFunc(obj, testStorage)).To(Succeed()) @@ -450,10 +447,8 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) { want: sreconcile.ResultSuccess, wantErr: false, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ca-file", - Namespace: "default", - }, + Name: "ca-file", + Namespace: "default", Data: map[string][]byte{ "ca.crt": tlsCA, }, @@ -475,10 +470,8 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) { ca: tlsCA, }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ca-file", - Namespace: "default", - }, + Name: "ca-file", + Namespace: "default", Data: map[string][]byte{ "ca.crt": tlsCA, }, @@ -527,10 +520,8 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) { ca: tlsCA, }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ca-file", - Namespace: "default", - }, + Name: "ca-file", + Namespace: "default", Data: map[string][]byte{ "caFile": tlsCA, }, @@ -580,10 +571,8 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) { ca: tlsCA, }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ca-file", - Namespace: "default", - }, + Name: "ca-file", + Namespace: "default", Data: map[string][]byte{ "caFile": tlsCA, }, @@ -664,10 +653,8 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) { password: "1234", }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "basic-auth", - Namespace: "default", - }, + Name: "basic-auth", + Namespace: "default", Data: map[string][]byte{ "username": []byte("git"), "password": []byte("1234"), @@ -718,10 +705,8 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) { password: "1234", }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "basic-auth", - Namespace: "default", - }, + Name: "basic-auth", + Namespace: "default", Data: map[string][]byte{ "username": []byte("git"), "password": []byte("1234"), @@ -773,10 +758,8 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) { ca: tlsCA, }, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "invalid-ca", - Namespace: "default", - }, + Name: "invalid-ca", + Namespace: "default", Data: map[string][]byte{ "ca.crt": []byte("invalid"), }, @@ -868,10 +851,8 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) { name: "Malformed secret returns FetchFailed=True and returns error", protocol: "http", secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "malformed-basic-auth", - Namespace: "default", - }, + Name: "malformed-basic-auth", + Namespace: "default", Data: map[string][]byte{ "username": []byte("git"), }, @@ -977,11 +958,9 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) { for _, tt := range tests { obj := &sourcev1.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "auth-strategy-", - Generation: 1, - Namespace: "default", - }, + GenerateName: "auth-strategy-", + Generation: 1, + Namespace: "default", Spec: sourcev1.HelmRepositorySpec{ Interval: metav1.Duration{Duration: interval}, Timeout: &metav1.Duration{Duration: timeout}, @@ -1089,6 +1068,161 @@ func TestHelmRepositoryReconciler_reconcileSource(t *testing.T) { } } +func TestHelmRepositoryReconciler_reconcileSource_cachedIndexFile(t *testing.T) { + newReconciler := func(g *WithT, c client.Client) *HelmRepositoryReconciler { + return &HelmRepositoryReconciler{ + Recorder: events.NewFakeRecorder(32, false), + Client: c, + Storage: testStorage, + Getters: testGetters, + patchOptions: getPatchOptions(helmRepositoryReadyCondition.Owned, "sc"), + } + } + + startServer := func(g *WithT, index []byte) *helmtestserver.HelmServer { + server, err := helmtestserver.NewTempHelmServer() + g.Expect(err).NotTo(HaveOccurred()) + if index == nil { + g.Expect(server.PackageChart("testdata/charts/helmchart")).To(Succeed()) + g.Expect(server.GenerateIndex()).To(Succeed()) + } else { + g.Expect(os.WriteFile(filepath.Join(server.Root(), "index.yaml"), index, 0o600)).To(Succeed()) + } + server.Start() + return server + } + + newObject := func(url string) *sourcev1.HelmRepository { + return &sourcev1.HelmRepository{ + Name: "index-cache", + Namespace: "default", + Generation: 1, + Spec: sourcev1.HelmRepositorySpec{ + Interval: metav1.Duration{Duration: interval}, + Timeout: &metav1.Duration{Duration: timeout}, + URL: url, + }, + } + } + + chartIndexFiles := func(g *WithT, dir string) []string { + matches, err := filepath.Glob(filepath.Join(dir, "chart-index-*.yaml")) + g.Expect(err).NotTo(HaveOccurred()) + return matches + } + + t.Run("invalid index removes cached file", func(t *testing.T) { + g := NewWithT(t) + dir := t.TempDir() + t.Setenv("TMPDIR", dir) + + // HTTP 200 with a body that is not a Helm index. Download succeeds + // and LoadFromPath fails. + server := startServer(g, []byte("{}\n")) + defer server.Stop() + defer os.RemoveAll(server.Root()) + + c := fakeclient.NewClientBuilder(). + WithScheme(testEnv.GetScheme()). + WithStatusSubresource(&sourcev1.HelmRepository{}). + Build() + obj := newObject(server.URL()) + g.Expect(c.Create(context.TODO(), obj)).To(Succeed()) + + var chartRepo repository.ChartRepository + got, err := newReconciler(g, c).reconcileSource(context.TODO(), patch.NewSerialPatcher(obj, c), obj, &meta.Artifact{}, &chartRepo) + + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("failed to load Helm repository from index YAML at " + server.URL() + ":")) + g.Expect(got).To(Equal(sreconcile.ResultEmpty)) + g.Expect(conditions.IsTrue(obj, sourcev1.FetchFailedCondition)).To(BeTrue()) + g.Expect(conditions.GetMessage(obj, sourcev1.FetchFailedCondition)).To(ContainSubstring("failed to load Helm repository from index YAML at " + server.URL() + ":")) + g.Expect(chartIndexFiles(g, dir)).To(BeEmpty()) + }) + + t.Run("invalid index message redacts credentials in the URL", func(t *testing.T) { + g := NewWithT(t) + dir := t.TempDir() + t.Setenv("TMPDIR", dir) + + server := startServer(g, []byte("{}\n")) + defer server.Stop() + defer os.RemoveAll(server.Root()) + + c := fakeclient.NewClientBuilder(). + WithScheme(testEnv.GetScheme()). + WithStatusSubresource(&sourcev1.HelmRepository{}). + Build() + obj := newObject(strings.Replace(server.URL(), "http://", "http://user:s3cr3t@", 1)) + g.Expect(c.Create(context.TODO(), obj)).To(Succeed()) + + var chartRepo repository.ChartRepository + _, err := newReconciler(g, c).reconcileSource(context.TODO(), patch.NewSerialPatcher(obj, c), obj, &meta.Artifact{}, &chartRepo) + + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("failed to load Helm repository from index YAML at")) + g.Expect(err.Error()).To(ContainSubstring("user:xxxxx@")) + g.Expect(err.Error()).ToNot(ContainSubstring("s3cr3t")) + g.Expect(conditions.GetMessage(obj, sourcev1.FetchFailedCondition)).ToNot(ContainSubstring("s3cr3t")) + }) + + t.Run("status patch failure removes cached file", func(t *testing.T) { + g := NewWithT(t) + dir := t.TempDir() + t.Setenv("TMPDIR", dir) + + server := startServer(g, nil) + defer server.Stop() + defer os.RemoveAll(server.Root()) + + base := fakeclient.NewClientBuilder(). + WithScheme(testEnv.GetScheme()). + WithStatusSubresource(&sourcev1.HelmRepository{}). + Build() + c := interceptor.NewClient(base, interceptor.Funcs{ + SubResourcePatch: func(context.Context, client.Client, string, client.Object, client.Patch, ...client.SubResourcePatchOption) error { + return errors.New("injected patch failure") + }, + }) + obj := newObject(server.URL()) + g.Expect(c.Create(context.TODO(), obj)).To(Succeed()) + + var chartRepo repository.ChartRepository + got, err := newReconciler(g, c).reconcileSource(context.TODO(), patch.NewSerialPatcher(obj, c), obj, &meta.Artifact{}, &chartRepo) + + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("injected patch failure")) + g.Expect(got).To(Equal(sreconcile.ResultEmpty)) + g.Expect(chartIndexFiles(g, dir)).To(BeEmpty()) + }) + + t.Run("successful fetch keeps cached file", func(t *testing.T) { + g := NewWithT(t) + dir := t.TempDir() + t.Setenv("TMPDIR", dir) + + server := startServer(g, nil) + defer server.Stop() + defer os.RemoveAll(server.Root()) + + c := fakeclient.NewClientBuilder(). + WithScheme(testEnv.GetScheme()). + WithStatusSubresource(&sourcev1.HelmRepository{}). + Build() + obj := newObject(server.URL()) + g.Expect(c.Create(context.TODO(), obj)).To(Succeed()) + + var chartRepo repository.ChartRepository + got, err := newReconciler(g, c).reconcileSource(context.TODO(), patch.NewSerialPatcher(obj, c), obj, &meta.Artifact{}, &chartRepo) + defer os.Remove(chartRepo.Path) + + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(got).To(Equal(sreconcile.ResultSuccess)) + matches := chartIndexFiles(g, dir) + g.Expect(matches).To(Equal([]string{chartRepo.Path})) + }) +} + func TestHelmRepositoryReconciler_reconcileArtifact(t *testing.T) { // The cache is at capacity with the previous revision and an index of // another repository, the new revision can only be added after eviction. @@ -1226,14 +1360,10 @@ func TestHelmRepositoryReconciler_reconcileArtifact(t *testing.T) { } obj := &sourcev1.HelmRepository{ - TypeMeta: metav1.TypeMeta{ - Kind: sourcev1.HelmRepositoryKind, - }, - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-bucket-", - Generation: 1, - Namespace: "default", - }, + Kind: sourcev1.HelmRepositoryKind, + GenerateName: "test-bucket-", + Generation: 1, + Namespace: "default", Spec: sourcev1.HelmRepositorySpec{ Timeout: &metav1.Duration{Duration: timeout}, URL: "https://example.com/index.yaml", @@ -1303,13 +1433,9 @@ func TestHelmRepositoryReconciler_garbageCollectEvictsCache(t *testing.T) { } obj := &sourcev1.HelmRepository{ - TypeMeta: metav1.TypeMeta{ - Kind: sourcev1.HelmRepositoryKind, - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "cache-evict", - Namespace: "default", - }, + Kind: sourcev1.HelmRepositoryKind, + Name: "cache-evict", + Namespace: "default", } artifact := testStorage.NewArtifactFor(obj.Kind, obj, "existing", "index-existing.yaml") obj.Status.Artifact = &artifact @@ -1441,10 +1567,8 @@ func TestHelmRepositoryReconciler_reconcileSubRecs(t *testing.T) { patchOptions: getPatchOptions(helmRepositoryReadyCondition.Owned, "sc"), } obj := &sourcev1.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-", - Generation: tt.generation, - }, + GenerateName: "test-", + Generation: tt.generation, Status: sourcev1.HelmRepositoryStatus{ ObservedGeneration: tt.observedGeneration, }, @@ -1520,14 +1644,10 @@ func TestHelmRepositoryReconciler_statusConditions(t *testing.T) { g := NewWithT(t) obj := &sourcev1.HelmRepository{ - TypeMeta: metav1.TypeMeta{ - Kind: sourcev1.HelmRepositoryKind, - APIVersion: sourcev1.GroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "helmrepo", - Namespace: "foo", - }, + Kind: sourcev1.HelmRepositoryKind, + APIVersion: sourcev1.GroupVersion.String(), + Name: "helmrepo", + Namespace: "foo", } clientBuilder := fakeclient.NewClientBuilder(). @@ -1715,10 +1835,8 @@ func TestHelmRepositoryReconciler_ReconcileTypeUpdatePredicateFilter(t *testing. defer testServer.Stop() obj := &sourcev1.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "helmrepository-reconcile-", - Namespace: "default", - }, + GenerateName: "helmrepository-reconcile-", + Namespace: "default", Spec: sourcev1.HelmRepositorySpec{ Interval: metav1.Duration{Duration: interval}, URL: testServer.URL(), @@ -1804,10 +1922,8 @@ func TestHelmRepositoryReconciler_ReconcileSpecUpdatePredicateFilter(t *testing. defer testServer.Stop() obj := &sourcev1.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "helmrepository-reconcile-", - Namespace: "default", - }, + GenerateName: "helmrepository-reconcile-", + Namespace: "default", Spec: sourcev1.HelmRepositorySpec{ Interval: metav1.Duration{Duration: interval}, URL: testServer.URL(), @@ -1906,10 +2022,8 @@ func TestHelmRepositoryReconciler_InMemoryCaching(t *testing.T) { defer func() { g.Expect(testEnv.Delete(ctx, ns)).To(Succeed()) }() helmRepo := &sourcev1.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "helmrepository-", - Namespace: ns.Name, - }, + GenerateName: "helmrepository-", + Namespace: ns.Name, Spec: sourcev1.HelmRepositorySpec{ URL: testServer.URL(), }, @@ -1965,10 +2079,8 @@ func TestHelmRepositoryReconciler_ociMigration(t *testing.T) { }) hr := &sourcev1.HelmRepository{ - ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("hr-%s", randStringRunes(5)), - Namespace: testns.Name, - }, + Name: fmt.Sprintf("hr-%s", randStringRunes(5)), + Namespace: testns.Name, } hrKey := client.ObjectKeyFromObject(hr) diff --git a/internal/controller/ocirepository_controller.go b/internal/controller/ocirepository_controller.go index cbd4f7777..5329432e3 100644 --- a/internal/controller/ocirepository_controller.go +++ b/internal/controller/ocirepository_controller.go @@ -47,6 +47,7 @@ import ( "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/reconcile" + cryptov1 "github.com/fluxcd/pkg/apis/crypto/v1" eventv1 "github.com/fluxcd/pkg/apis/event/v1" "github.com/fluxcd/pkg/apis/meta" "github.com/fluxcd/pkg/artifact/storage" @@ -62,6 +63,7 @@ import ( rreconcile "github.com/fluxcd/pkg/runtime/reconcile" "github.com/fluxcd/pkg/runtime/secrets" "github.com/fluxcd/pkg/sourceignore" + fluxbroker "github.com/fluxcd/pkg/spiffe/broker" "github.com/fluxcd/pkg/tar" "github.com/fluxcd/pkg/version" ctrl "sigs.k8s.io/controller-runtime" @@ -144,7 +146,10 @@ type OCIRepositoryReconciler struct { ControllerName string TokenCache *cache.TokenCache CosignVerifierFactory *scosign.CosignVerifierFactory - requeueDependency time.Duration + DefaultServiceAccount string + SPIFFEBroker fluxbroker.Interface + + requeueDependency time.Duration patchOptions []patch.Option } @@ -340,8 +345,67 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch conditions.Delete(obj, sourcev1.SourceVerifiedCondition) } - // Generate the registry credential keychain either from static credentials or using cloud OIDC - keychain, err := r.keychain(ctx, obj) + provider := obj.GetProvider() + + // Apply the default ServiceAccount when the object does not specify one, so + // that both the keychain and the auth library are aware of it. + saName := obj.Spec.ServiceAccountName + if saName == "" { + saName = r.DefaultServiceAccount + } + + // Fetch the ServiceAccount once. + var sa *corev1.ServiceAccount + if saName != "" { + sa = &corev1.ServiceAccount{} + saRef := types.NamespacedName{Namespace: obj.GetNamespace(), Name: saName} + if err := r.Get(ctx, saRef, sa); err != nil { + e := serror.NewGeneric( + fmt.Errorf("failed to get service account '%s': %w", saRef, err), + sourcev1.AuthenticationFailedReason, + ) + conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e) + return sreconcile.ResultEmpty, e + } + } + + // Build the authentication spec. BuildSpec is safe to call even when there + // is no ServiceAccount or provider-specific configuration: it returns a + // spec with a nil Auth in that case. + buildOpts := []cryptov1.Option{ + cryptov1.WithServiceProvider(provider), + cryptov1.WithOCI(), + } + if obj.Spec.SecretRef != nil { + buildOpts = append(buildOpts, cryptov1.WithSecretRefAuth()) + } + if sa != nil { + buildOpts = append(buildOpts, cryptov1.WithAnnotations(sa.Annotations)) + if len(sa.ImagePullSecrets) > 0 { + buildOpts = append(buildOpts, cryptov1.WithImagePullSecrets()) + } + } + if obj.Spec.CertSecretRef != nil { + buildOpts = append(buildOpts, cryptov1.WithCertSecretRef()) + } + cryptoSpec, err := cryptov1.BuildSpec(buildOpts...) + if err != nil { + e := serror.NewGeneric( + fmt.Errorf("failed to validate configuration: %w", err), + sourcev1.AuthenticationFailedReason, + ) + conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e) + return sreconcile.ResultEmpty, e + } + + // Generate the registry credential keychain either from static credentials + // or using cloud OIDC. The ServiceAccount is only used for its image pull + // secrets when it has any. + var keychainSA *corev1.ServiceAccount + if sa != nil && len(sa.ImagePullSecrets) > 0 { + keychainSA = sa + } + keychain, err := r.keychain(ctx, obj, keychainSA) if err != nil { e := serror.NewGeneric( fmt.Errorf("failed to get credential: %w", err), @@ -368,40 +432,30 @@ func (r *OCIRepositoryReconciler) reconcileSource(ctx context.Context, sp *patch } } - if _, ok := keychain.(soci.Anonymous); obj.Spec.Provider != "" && obj.Spec.Provider != sourcev1.GenericOCIProvider && ok { - opts := []auth.Option{ + // Workload identity kicks in when the provider is not generic or + // the SA has opted into object-level workload identity. + if _, ok := keychain.(soci.Anonymous); ok && (provider != sourcev1.GenericOCIProvider || cryptoSpec.Auth.OptedIn()) { + opts := auth.OptionsFromSpec(cryptoSpec.Auth) + opts = append(opts, auth.WithClient(r.Client), - auth.WithServiceAccountNamespace(obj.GetNamespace()), - } - - if obj.Spec.ServiceAccountName != "" { - // Check object-level workload identity feature gate. - if !auth.IsObjectLevelWorkloadIdentityEnabled() { - const gate = auth.FeatureGateObjectLevelWorkloadIdentity - const msgFmt = "to use spec.serviceAccountName for provider authentication please enable the %s feature gate in the controller" - err := fmt.Errorf(msgFmt, gate) - return sreconcile.ResultEmpty, serror.NewStalling(err, meta.FeatureGateDisabledReason) - } - // Set ServiceAccountName only if explicitly specified - opts = append(opts, auth.WithServiceAccountName(obj.Spec.ServiceAccountName)) + auth.WithObject(sourcev1.GroupVersion.Group, sourcev1.OCIRepositoryKind, obj), + auth.WithSPIFFEBroker(r.SPIFFEBroker)) + if sa != nil { + opts = append(opts, auth.WithServiceAccountName(sa.Name)) } if r.TokenCache != nil { - involvedObject := cache.InvolvedObject{ - Kind: sourcev1.OCIRepositoryKind, - Name: obj.GetName(), - Namespace: obj.GetNamespace(), - Operation: cache.OperationReconcile, - } - opts = append(opts, auth.WithCache(*r.TokenCache, involvedObject)) + opts = append(opts, + auth.WithCache(*r.TokenCache), + auth.WithCacheOperation(cache.OperationReconcile)) } if proxyURL != nil { opts = append(opts, auth.WithProxyURL(*proxyURL)) } var authErr error - authenticator, authErr = soci.OIDCAuth(ctxTimeout, obj.Spec.URL, obj.Spec.Provider, opts...) + authenticator, authErr = soci.OIDCAuth(ctxTimeout, obj.Spec.URL, provider, opts...) if authErr != nil { e := serror.NewGeneric( - fmt.Errorf("failed to get credential from %s: %w", obj.Spec.Provider, authErr), + fmt.Errorf("failed to get credential from %s: %w", provider, authErr), sourcev1.AuthenticationFailedReason, ) conditions.MarkTrue(obj, sourcev1.FetchFailedCondition, e.Reason, "%s", e) @@ -1079,8 +1133,11 @@ func convertOCIToSemVerTag(ociTag, mediaType string) string { // keychain generates the credential keychain based on the resource // configuration. If no auth is specified a default keychain with -// anonymous access is returned -func (r *OCIRepositoryReconciler) keychain(ctx context.Context, obj *sourcev1.OCIRepository) (authn.Keychain, error) { +// anonymous access is returned. +// +// The ServiceAccount is only used for its image pull secrets and must be +// resolved and validated by the caller beforehand. +func (r *OCIRepositoryReconciler) keychain(ctx context.Context, obj *sourcev1.OCIRepository, sa *corev1.ServiceAccount) (authn.Keychain, error) { var imagePullSecrets []corev1.Secret // lookup auth secret @@ -1096,10 +1153,9 @@ func (r *OCIRepositoryReconciler) keychain(ctx context.Context, obj *sourcev1.OC imagePullSecrets = append(imagePullSecrets, imagePullSecret) } - // lookup service account - if obj.Spec.ServiceAccountName != "" { - saRef := types.NamespacedName{Namespace: obj.Namespace, Name: obj.Spec.ServiceAccountName} - saSecrets, err := secrets.PullSecretsFromServiceAccountRef(ctx, r.Client, saRef) + // lookup service account image pull secrets + if sa != nil { + saSecrets, err := secrets.PullSecretsFromServiceAccount(ctx, r.Client, sa) if err != nil { return nil, err } diff --git a/internal/controller/ocirepository_controller_test.go b/internal/controller/ocirepository_controller_test.go index c3cfb2712..41c7f45eb 100644 --- a/internal/controller/ocirepository_controller_test.go +++ b/internal/controller/ocirepository_controller_test.go @@ -52,7 +52,6 @@ import ( apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/utils/ptr" oras "oras.land/oras-go/v2/registry/remote" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -60,11 +59,11 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" kstatus "github.com/fluxcd/cli-utils/pkg/kstatus/status" + cryptov1 "github.com/fluxcd/pkg/apis/crypto/v1" eventv1 "github.com/fluxcd/pkg/apis/event/v1" "github.com/fluxcd/pkg/apis/meta" intdigest "github.com/fluxcd/pkg/artifact/digest" "github.com/fluxcd/pkg/artifact/storage" - "github.com/fluxcd/pkg/auth" "github.com/fluxcd/pkg/git" "github.com/fluxcd/pkg/oci" "github.com/fluxcd/pkg/runtime/conditions" @@ -90,7 +89,7 @@ func TestOCIRepositoryReconciler_deleteBeforeFinalizer(t *testing.T) { namespaceName := "ocirepo-" + randStringRunes(5) namespace := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{Name: namespaceName}, + Name: namespaceName, } g.Expect(k8sClient.Create(ctx, namespace)).ToNot(HaveOccurred()) t.Cleanup(func() { @@ -126,7 +125,7 @@ func TestOCIRepository_CELValidation(t *testing.T) { namespaceName := "ocirepo-" + randStringRunes(5) namespace := &corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{Name: namespaceName}, + Name: namespaceName, } g.Expect(k8sClient.Create(ctx, namespace)).ToNot(HaveOccurred()) t.Cleanup(func() { @@ -135,7 +134,7 @@ func TestOCIRepository_CELValidation(t *testing.T) { tests := []struct { name string - spec map[string]interface{} + spec map[string]any wantErr []string }{ { @@ -144,26 +143,26 @@ func TestOCIRepository_CELValidation(t *testing.T) { }, { name: "empty spec", - spec: map[string]interface{}{}, + spec: map[string]any{}, wantErr: []string{"spec.url is required", "spec.interval is required"}, }, { name: "missing url", - spec: map[string]interface{}{ + spec: map[string]any{ "interval": "1m", }, wantErr: []string{"spec.url is required"}, }, { name: "missing interval", - spec: map[string]interface{}{ + spec: map[string]any{ "url": "oci://example.com", }, wantErr: []string{"spec.interval is required"}, }, { name: "valid", - spec: map[string]interface{}{ + spec: map[string]any{ "url": "oci://example.com", "interval": "1m", }, @@ -269,10 +268,8 @@ func TestOCIRepository_Reconcile(t *testing.T) { defer func() { g.Expect(testEnv.Delete(ctx, ns)).To(Succeed()) }() origObj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "ocirepository-reconcile", - Namespace: ns.Name, - }, + GenerateName: "ocirepository-reconcile", + Namespace: ns.Name, Spec: sourcev1.OCIRepositorySpec{ URL: tt.url, Interval: &metav1.Duration{Duration: 60 * time.Minute}, @@ -437,10 +434,8 @@ func TestOCIRepository_Reconcile_MediaType(t *testing.T) { defer func() { g.Expect(testEnv.Delete(ctx, ns)).To(Succeed()) }() obj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "ocirepository-reconcile", - Namespace: ns.Name, - }, + GenerateName: "ocirepository-reconcile", + Namespace: ns.Name, Spec: sourcev1.OCIRepositorySpec{ URL: tt.url, Interval: &metav1.Duration{Duration: 60 * time.Minute}, @@ -655,9 +650,7 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) { }), }, tlsCertSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ca-file", - }, + Name: "ca-file", Data: map[string][]byte{ "ca.crt": tlsCA, }, @@ -680,9 +673,7 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) { }), }, tlsCertSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ca-file", - }, + Name: "ca-file", Data: map[string][]byte{ "caFile": tlsCA, }, @@ -723,9 +714,7 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) { }), }, tlsCertSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ca-file", - }, + Name: "ca-file", Data: map[string][]byte{ "ca.crt": []byte("invalid"), }, @@ -747,9 +736,7 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) { }), }, tlsCertSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ca-file", - }, + Name: "ca-file", Data: map[string][]byte{ "ca.crt": tlsCA, "caFile": []byte("invalid"), @@ -769,7 +756,7 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) { crane.Insecure, }, assertConditions: []metav1.Condition{ - *conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.OCIPullFailedReason, "%s", "failed to get access token for artifact registry"), + *conditions.TrueCondition(sourcev1.FetchFailedCondition, sourcev1.OCIPullFailedReason, "%s", "failed to get access credential for artifact registry"), }, }, { @@ -808,10 +795,8 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) { WithStatusSubresource(&sourcev1.OCIRepository{}) obj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "auth-strategy-", - Generation: 1, - }, + GenerateName: "auth-strategy-", + Generation: 1, Spec: sourcev1.OCIRepositorySpec{ Interval: &metav1.Duration{Duration: interval}, Timeout: &metav1.Duration{Duration: timeout}, @@ -844,9 +829,7 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) { if tt.secretOpts.username != "" && tt.secretOpts.password != "" { secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth-secretref", - }, + Name: "auth-secretref", Type: corev1.SecretTypeDockerConfigJson, Data: map[string][]byte{ ".dockerconfigjson": fmt.Appendf(nil, `{"auths": {%q: {"username": %q, "password": %q}}}`, @@ -857,9 +840,7 @@ func TestOCIRepository_reconcileSource_authStrategy(t *testing.T) { if tt.secretOpts.includeSA { serviceAccount := &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Name: "sa-ocitest", - }, + Name: "sa-ocitest", ImagePullSecrets: []corev1.LocalObjectReference{{Name: secret.Name}}, } clientBuilder.WithObjects(serviceAccount) @@ -1014,11 +995,9 @@ func TestOCIRepository_CertSecret(t *testing.T) { defer func() { g.Expect(testEnv.Delete(ctx, ns)).To(Succeed()) }() obj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "ocirepository-test-resource", - Namespace: ns.Name, - Generation: 1, - }, + GenerateName: "ocirepository-test-resource", + Namespace: ns.Name, + Generation: 1, Spec: sourcev1.OCIRepositorySpec{ URL: tt.url, Interval: &metav1.Duration{Duration: 60 * time.Minute}, @@ -1140,11 +1119,9 @@ func TestOCIRepository_ProxySecret(t *testing.T) { defer func() { g.Expect(testEnv.Delete(ctx, ns)).To(Succeed()) }() obj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "ocirepository-test-resource", - Namespace: ns.Name, - Generation: 1, - }, + GenerateName: "ocirepository-test-resource", + Namespace: ns.Name, + Generation: 1, Spec: sourcev1.OCIRepositorySpec{ URL: tt.url, Interval: &metav1.Duration{Duration: 60 * time.Minute}, @@ -1354,10 +1331,8 @@ func TestOCIRepository_reconcileSource_remoteReference(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { obj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "checkout-strategy-", - Generation: 1, - }, + GenerateName: "checkout-strategy-", + Generation: 1, Spec: sourcev1.OCIRepositorySpec{ URL: fmt.Sprintf("oci://%s/podinfo", server.registryHost), Interval: &metav1.Duration{Duration: interval}, @@ -1598,10 +1573,8 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignatureNotation(t *testi g.Expect(err).NotTo(HaveOccurred()) caSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "valid-trust-store", - Generation: 1, - }, + Name: "valid-trust-store", + Generation: 1, Data: map[string][]byte{ "ca.crt": tlsCA, }, @@ -1623,10 +1596,8 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignatureNotation(t *testi }) obj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "verify-oci-source-signature-", - Generation: 1, - }, + GenerateName: "verify-oci-source-signature-", + Generation: 1, Spec: sourcev1.OCIRepositorySpec{ URL: fmt.Sprintf("oci://%s/podinfo", server.registryHost), Verify: &sourcev1.OCIRepositoryVerification{ @@ -1652,10 +1623,8 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignatureNotation(t *testi data["trustpolicy.json"] = policy secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "notation-config-", - }, - Data: data, + GenerateName: "notation-config-", + Data: data, } g.Expect(r.Create(ctx, secret)).NotTo(HaveOccurred()) @@ -1681,7 +1650,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignatureNotation(t *testi obj.Spec.Reference.Digest = podinfoVersions[tt.reference.Tag].digest.String() } - keychain, err := r.keychain(ctx, obj) + keychain, err := r.keychain(ctx, obj, nil) if err != nil { g.Expect(err).ToNot(HaveOccurred()) } @@ -1717,10 +1686,8 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignatureNotation(t *testi signatureMediaType := cose.MediaTypeEnvelope signOptions := notation.SignOptions{ - SignerSignOptions: notation.SignerSignOptions{ - SignatureMediaType: signatureMediaType, - }, - ArtifactReference: artifactRef.String(), + SignatureMediaType: signatureMediaType, + ArtifactReference: artifactRef.String(), } _, err = notation.Sign(ctx, signer, repo, signOptions) @@ -1960,10 +1927,8 @@ func TestOCIRepository_reconcileSource_verifyOCISourceTrustPolicyNotation(t *tes }) obj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "verify-oci-source-signature-", - Generation: 1, - }, + GenerateName: "verify-oci-source-signature-", + Generation: 1, Spec: sourcev1.OCIRepositorySpec{ URL: fmt.Sprintf("oci://%s/podinfo", server.registryHost), Verify: &sourcev1.OCIRepositoryVerification{ @@ -2005,10 +1970,8 @@ func TestOCIRepository_reconcileSource_verifyOCISourceTrustPolicyNotation(t *tes data["notation.crt"] = certTuple.Cert.Raw secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "notation-", - }, - Data: data, + GenerateName: "notation-", + Data: data, } g.Expect(r.Create(ctx, secret)).NotTo(HaveOccurred()) @@ -2028,7 +1991,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceTrustPolicyNotation(t *tes obj.Spec.Reference.Digest = podinfoVersions[tt.reference.Tag].digest.String() } - keychain, err := r.keychain(ctx, obj) + keychain, err := r.keychain(ctx, obj, nil) if err != nil { g.Expect(err).ToNot(HaveOccurred()) } @@ -2048,10 +2011,8 @@ func TestOCIRepository_reconcileSource_verifyOCISourceTrustPolicyNotation(t *tes signatureMediaType := cose.MediaTypeEnvelope signOptions := notation.SignOptions{ - SignerSignOptions: notation.SignerSignOptions{ - SignatureMediaType: signatureMediaType, - }, - ArtifactReference: artifactRef.String(), + SignatureMediaType: signatureMediaType, + ArtifactReference: artifactRef.String(), } _, err = notation.Sign(ctx, signer, repo, signOptions) @@ -2268,9 +2229,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignatureCosign(t *testing g.Expect(err).ToNot(HaveOccurred()) secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "cosign-key", - }, + Name: "cosign-key", Data: map[string][]byte{ "cosign.pub": keys.PublicBytes, }} @@ -2278,10 +2237,8 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignatureCosign(t *testing g.Expect(r.Create(ctx, secret)).NotTo(HaveOccurred()) caSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ca-cert-cosign", - Generation: 1, - }, + Name: "ca-cert-cosign", + Generation: 1, Data: map[string][]byte{ "ca.crt": tlsCA, }, @@ -2304,10 +2261,8 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignatureCosign(t *testing }) obj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "verify-oci-source-signature-", - Generation: 1, - }, + GenerateName: "verify-oci-source-signature-", + Generation: 1, Spec: sourcev1.OCIRepositorySpec{ URL: fmt.Sprintf("oci://%s/podinfo", server.registryHost), Verify: &sourcev1.OCIRepositoryVerification{ @@ -2337,7 +2292,7 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignatureCosign(t *testing podinfoVersions, err := pushMultiplePodinfoImages(server.registryHost, tt.insecure, tt.reference.Tag) g.Expect(err).ToNot(HaveOccurred()) - keychain, err := r.keychain(ctx, obj) + keychain, err := r.keychain(ctx, obj, nil) if err != nil { g.Expect(err).ToNot(HaveOccurred()) } @@ -2534,10 +2489,8 @@ func TestOCIRepository_reconcileSource_verifyOCISourceSignature_keyless(t *testi g := NewWithT(t) obj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "verify-oci-source-signature-", - Generation: 1, - }, + GenerateName: "verify-oci-source-signature-", + Generation: 1, Spec: sourcev1.OCIRepositorySpec{ URL: "oci://ghcr.io/stefanprodan/manifests/podinfo", Verify: &sourcev1.OCIRepositoryVerification{ @@ -2626,7 +2579,7 @@ func TestOCIRepository_reconcileSource_noop(t *testing.T) { { name: "full reconcile - same rev, unobserved ignore", beforeFunc: func(obj *sourcev1.OCIRepository) { - obj.Status.ObservedIgnore = ptr.To("aaa") + obj.Status.ObservedIgnore = new("aaa") obj.Status.Artifact = &meta.Artifact{ Revision: testRevision, } @@ -2638,8 +2591,8 @@ func TestOCIRepository_reconcileSource_noop(t *testing.T) { { name: "noop - same rev, observed ignore", beforeFunc: func(obj *sourcev1.OCIRepository) { - obj.Spec.Ignore = ptr.To("aaa") - obj.Status.ObservedIgnore = ptr.To("aaa") + obj.Spec.Ignore = new("aaa") + obj.Status.ObservedIgnore = new("aaa") obj.Status.Artifact = &meta.Artifact{ Revision: testRevision, } @@ -2719,10 +2672,8 @@ func TestOCIRepository_reconcileSource_noop(t *testing.T) { g := NewWithT(t) obj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "noop-", - Generation: 1, - }, + GenerateName: "noop-", + Generation: 1, Spec: sourcev1.OCIRepositorySpec{ URL: fmt.Sprintf("oci://%s/podinfo", server.registryHost), Reference: &sourcev1.OCIRepositoryRef{Tag: "6.1.5"}, @@ -2794,7 +2745,7 @@ func TestOCIRepository_reconcileArtifact(t *testing.T) { targetPath: "testdata/oci/repository", artifact: &meta.Artifact{Revision: "revision"}, beforeFunc: func(obj *sourcev1.OCIRepository) { - obj.Spec.Ignore = ptr.To("foo.txt") + obj.Spec.Ignore = new("foo.txt") }, want: sreconcile.ResultSuccess, assertPaths: []string{ @@ -2834,7 +2785,7 @@ func TestOCIRepository_reconcileArtifact(t *testing.T) { }, beforeFunc: func(obj *sourcev1.OCIRepository) { obj.Status.Artifact = &meta.Artifact{Revision: "revision"} - obj.Spec.Ignore = ptr.To("aaa") + obj.Spec.Ignore = new("aaa") }, want: sreconcile.ResultSuccess, assertPaths: []string{ @@ -2901,10 +2852,10 @@ func TestOCIRepository_reconcileArtifact(t *testing.T) { Revision: "revision", }, beforeFunc: func(obj *sourcev1.OCIRepository) { - obj.Spec.Ignore = ptr.To("aaa") + obj.Spec.Ignore = new("aaa") obj.Spec.LayerSelector = &sourcev1.OCILayerSelector{MediaType: "foo"} obj.Status.Artifact = &meta.Artifact{Revision: "revision"} - obj.Status.ObservedIgnore = ptr.To("aaa") + obj.Status.ObservedIgnore = new("aaa") obj.Status.ObservedLayerSelector = &sourcev1.OCILayerSelector{MediaType: "foo"} }, want: sreconcile.ResultSuccess, @@ -2953,10 +2904,8 @@ func TestOCIRepository_reconcileArtifact(t *testing.T) { _ = resetChmod(tt.targetPath, 0o755, 0o644) obj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "reconcile-artifact-", - Generation: 1, - }, + GenerateName: "reconcile-artifact-", + Generation: 1, } if tt.beforeFunc != nil { tt.beforeFunc(obj) @@ -3114,9 +3063,7 @@ func TestOCIRepository_getArtifactRef(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { obj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "artifact-url-", - }, + GenerateName: "artifact-url-", Spec: sourcev1.OCIRepositorySpec{ URL: tt.url, Interval: &metav1.Duration{Duration: interval}, @@ -3152,10 +3099,8 @@ func TestOCIRepository_invalidURL(t *testing.T) { defer func() { g.Expect(testEnv.Delete(ctx, ns)).To(Succeed()) }() obj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "ocirepository-reconcile", - Namespace: ns.Name, - }, + GenerateName: "ocirepository-reconcile", + Namespace: ns.Name, Spec: sourcev1.OCIRepositorySpec{ URL: "oci://ghcr.io/test/test:v1", Interval: &metav1.Duration{Duration: 60 * time.Minute}, @@ -3186,30 +3131,30 @@ func TestOCIRepository_invalidURL(t *testing.T) { g.Expect(stalledCondition.Reason).Should(Equal(sourcev1.URLInvalidReason)) } -func TestOCIRepository_objectLevelWorkloadIdentityFeatureGate(t *testing.T) { +func TestOCIRepository_genericWorkloadIdentity(t *testing.T) { g := NewWithT(t) - ns, err := testEnv.CreateNamespace(ctx, "ocirepository-olwifg-test") + ns, err := testEnv.CreateNamespace(ctx, "ocirepository-generic-olwi-test") g.Expect(err).ToNot(HaveOccurred()) defer func() { g.Expect(testEnv.Delete(ctx, ns)).To(Succeed()) }() err = testEnv.Create(ctx, &corev1.ServiceAccount{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: ns.Name, - Name: "test", + Namespace: ns.Name, + Name: "test", + Annotations: map[string]string{ + cryptov1.AnnotationCredentialProvider: cryptov1.CredentialProviderKubernetes, + cryptov1.AnnotationCredentialType: cryptov1.CredentialTypeJWT, }, }) g.Expect(err).NotTo(HaveOccurred()) obj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "ocirepository-reconcile", - Namespace: ns.Name, - }, + GenerateName: "ocirepository-generic-olwi-", + Namespace: ns.Name, Spec: sourcev1.OCIRepositorySpec{ URL: "oci://ghcr.io/stefanprodan/manifests/podinfo", Interval: &metav1.Duration{Duration: 60 * time.Minute}, - Provider: "aws", + Provider: sourcev1.GenericOCIProvider, ServiceAccountName: "test", }, } @@ -3219,21 +3164,6 @@ func TestOCIRepository_objectLevelWorkloadIdentityFeatureGate(t *testing.T) { key := client.ObjectKey{Name: obj.Name, Namespace: obj.Namespace} resultobj := &sourcev1.OCIRepository{} - g.Eventually(func() bool { - if err := testEnv.Get(ctx, key, resultobj); err != nil { - return false - } - return conditions.IsStalled(resultobj) - }).Should(BeTrue()) - - stalledCondition := conditions.Get(resultobj, meta.StalledCondition) - g.Expect(stalledCondition).ToNot(BeNil()) - g.Expect(stalledCondition.Reason).Should(Equal(meta.FeatureGateDisabledReason)) - g.Expect(stalledCondition.Message).Should(Equal("to use spec.serviceAccountName for provider authentication please enable the ObjectLevelWorkloadIdentity feature gate in the controller")) - - auth.EnableObjectLevelWorkloadIdentity() - t.Cleanup(auth.DisableObjectLevelWorkloadIdentity) - g.Eventually(func() bool { if err := testEnv.Get(ctx, key, resultobj); err != nil { return false @@ -3251,7 +3181,7 @@ func TestOCIRepository_objectLevelWorkloadIdentityFeatureGate(t *testing.T) { logOCIRepoStatus(t, resultobj) return !conditions.IsReady(resultobj) && conditions.GetReason(resultobj, meta.ReadyCondition) == sourcev1.OCIPullFailedReason - }).Should(BeTrue()) + }, timeout).Should(BeTrue()) } func TestOCIRepository_reconcileStorage(t *testing.T) { @@ -3297,7 +3227,7 @@ func TestOCIRepository_reconcileStorage(t *testing.T) { Revision: "d", Digest: "sha256:18ac3e7343f016890c510e93f935261169d9e3f565436429830faf0934f4f8e4", URL: testStorage.Hostname + "/oci-reconcile-storage/d.txt", - Size: int64p(int64(len("d"))), + Size: new(int64(len("d"))), }, assertPaths: []string{ "/oci-reconcile-storage/d.txt", @@ -3426,7 +3356,7 @@ func TestOCIRepository_reconcileStorage(t *testing.T) { Revision: "f", Digest: "sha256:3b9c358f36f0a31b6ad3e14f309c7cf198ac9246e8316f9ce543d5b19ac02b80", URL: testStorage.Hostname + "/oci-reconcile-storage/hostname.txt", - Size: int64p(int64(len("file"))), + Size: new(int64(len("file"))), }, assertConditions: []metav1.Condition{ *conditions.TrueCondition(meta.ReadyCondition, "foo", "bar"), @@ -3450,10 +3380,8 @@ func TestOCIRepository_reconcileStorage(t *testing.T) { g := NewWithT(t) obj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-", - Generation: 1, - }, + GenerateName: "test-", + Generation: 1, } if tt.beforeFunc != nil { @@ -3510,12 +3438,10 @@ func TestOCIRepository_ReconcileDelete(t *testing.T) { } obj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{ - Name: "reconcile-delete-", - DeletionTimestamp: &metav1.Time{Time: time.Now()}, - Finalizers: []string{ - sourcev1.SourceFinalizer, - }, + Name: "reconcile-delete-", + DeletionTimestamp: &metav1.Time{Time: time.Now()}, + Finalizers: []string{ + sourcev1.SourceFinalizer, }, Status: sourcev1.OCIRepositoryStatus{}, } @@ -3565,10 +3491,8 @@ func TestOCIRepositoryReconciler_notify(t *testing.T) { } }, wantEvent: &corev1.Event{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - fmt.Sprintf("%s/%s", sourcev1.GroupVersion.Group, eventv1.MetaOriginRevisionKey): "6.1.8/b3b00fe35424a45d373bf4c7214178bc36fd7872", - }, + Annotations: map[string]string{ + fmt.Sprintf("%s/%s", sourcev1.GroupVersion.Group, eventv1.MetaOriginRevisionKey): "6.1.8/b3b00fe35424a45d373bf4c7214178bc36fd7872", }, Type: corev1.EventTypeNormal, Reason: sourcev1.NewArtifactReason, @@ -3597,10 +3521,8 @@ func TestOCIRepositoryReconciler_notify(t *testing.T) { conditions.MarkTrue(obj, meta.ReadyCondition, meta.SucceededReason, "ready") }, wantEvent: &corev1.Event{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - fmt.Sprintf("%s/%s", sourcev1.GroupVersion.Group, eventv1.MetaOriginRevisionKey): "6.1.8/b3b00fe35424a45d373bf4c7214178bc36fd7872", - }, + Annotations: map[string]string{ + fmt.Sprintf("%s/%s", sourcev1.GroupVersion.Group, eventv1.MetaOriginRevisionKey): "6.1.8/b3b00fe35424a45d373bf4c7214178bc36fd7872", }, Type: corev1.EventTypeNormal, Reason: meta.SucceededReason, @@ -3771,7 +3693,7 @@ func pushMultiplePodinfoImages(serverURL string, insecure bool, versions ...stri } opts = append(opts, crane.WithTransport(transport)) } - for i := 0; i < len(versions); i++ { + for i := range versions { pi, err := createPodinfoImageFromTar(fmt.Sprintf("podinfo-%s.tar", versions[i]), versions[i], serverURL, opts...) if err != nil { return nil, err @@ -3802,34 +3724,34 @@ func TestOCIContentConfigChanged(t *testing.T) { { name: "same ignore, no layer selector", spec: sourcev1.OCIRepositorySpec{ - Ignore: ptr.To("nnn"), + Ignore: new("nnn"), }, status: sourcev1.OCIRepositoryStatus{ - ObservedIgnore: ptr.To("nnn"), + ObservedIgnore: new("nnn"), }, want: false, }, { name: "different ignore, no layer selector", spec: sourcev1.OCIRepositorySpec{ - Ignore: ptr.To("nnn"), + Ignore: new("nnn"), }, status: sourcev1.OCIRepositoryStatus{ - ObservedIgnore: ptr.To("mmm"), + ObservedIgnore: new("mmm"), }, want: true, }, { name: "same ignore, same layer selector", spec: sourcev1.OCIRepositorySpec{ - Ignore: ptr.To("nnn"), + Ignore: new("nnn"), LayerSelector: &sourcev1.OCILayerSelector{ MediaType: "foo", Operation: sourcev1.OCILayerExtract, }, }, status: sourcev1.OCIRepositoryStatus{ - ObservedIgnore: ptr.To("nnn"), + ObservedIgnore: new("nnn"), ObservedLayerSelector: &sourcev1.OCILayerSelector{ MediaType: "foo", Operation: sourcev1.OCILayerExtract, @@ -3840,14 +3762,14 @@ func TestOCIContentConfigChanged(t *testing.T) { { name: "same ignore, different layer selector operation", spec: sourcev1.OCIRepositorySpec{ - Ignore: ptr.To("nnn"), + Ignore: new("nnn"), LayerSelector: &sourcev1.OCILayerSelector{ MediaType: "foo", Operation: sourcev1.OCILayerCopy, }, }, status: sourcev1.OCIRepositoryStatus{ - ObservedIgnore: ptr.To("nnn"), + ObservedIgnore: new("nnn"), ObservedLayerSelector: &sourcev1.OCILayerSelector{ MediaType: "foo", Operation: sourcev1.OCILayerExtract, @@ -3858,14 +3780,14 @@ func TestOCIContentConfigChanged(t *testing.T) { { name: "same ignore, different layer selector mediatype", spec: sourcev1.OCIRepositorySpec{ - Ignore: ptr.To("nnn"), + Ignore: new("nnn"), LayerSelector: &sourcev1.OCILayerSelector{ MediaType: "bar", Operation: sourcev1.OCILayerExtract, }, }, status: sourcev1.OCIRepositoryStatus{ - ObservedIgnore: ptr.To("nnn"), + ObservedIgnore: new("nnn"), ObservedLayerSelector: &sourcev1.OCILayerSelector{ MediaType: "foo", Operation: sourcev1.OCILayerExtract, @@ -3962,17 +3884,17 @@ func Test_verificationMaterialFingerprint(t *testing.T) { func TestOCIRepositoryReconciler_verificationPolicyChanged(t *testing.T) { secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "cosign-key", Namespace: "default"}, - Data: map[string][]byte{"cosign.pub": []byte("key")}, + Name: "cosign-key", Namespace: "default", + Data: map[string][]byte{"cosign.pub": []byte("key")}, } rotatedSecret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{Name: "cosign-key", Namespace: "default"}, - Data: map[string][]byte{"cosign.pub": []byte("rotated-key")}, + Name: "cosign-key", Namespace: "default", + Data: map[string][]byte{"cosign.pub": []byte("rotated-key")}, } newObj := func(secretRef *meta.LocalObjectReference, fingerprint string) *sourcev1.OCIRepository { obj := &sourcev1.OCIRepository{ - ObjectMeta: metav1.ObjectMeta{Name: "oci", Namespace: "default"}, + Name: "oci", Namespace: "default", Spec: sourcev1.OCIRepositorySpec{ Verify: &sourcev1.OCIRepositoryVerification{Provider: "cosign"}, }, diff --git a/internal/controller/ocirepository_verify_test.go b/internal/controller/ocirepository_verify_test.go index 1c347027e..7429eab3d 100644 --- a/internal/controller/ocirepository_verify_test.go +++ b/internal/controller/ocirepository_verify_test.go @@ -23,7 +23,6 @@ import ( "github.com/fluxcd/pkg/apis/meta" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client/fake" ) @@ -45,10 +44,8 @@ func TestReadTrustedRootFromSecret(t *testing.T) { namespace: "default", ref: &meta.LocalObjectReference{Name: "sigstore-root"}, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "sigstore-root", - Namespace: "default", - }, + Name: "sigstore-root", + Namespace: "default", Data: map[string][]byte{ "trusted_root.json": []byte(`{"mediaType":"application/vnd.dev.sigstore.trustedroot+json;version=0.1"}`), }, @@ -66,10 +63,8 @@ func TestReadTrustedRootFromSecret(t *testing.T) { namespace: "default", ref: &meta.LocalObjectReference{Name: "no-key-secret"}, secret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "no-key-secret", - Namespace: "default", - }, + Name: "no-key-secret", + Namespace: "default", Data: map[string][]byte{ "other-key": []byte("data"), }, diff --git a/internal/controller/suite_test.go b/internal/controller/suite_test.go index 91d999c60..56b9294a7 100644 --- a/internal/controller/suite_test.go +++ b/internal/controller/suite_test.go @@ -87,6 +87,8 @@ var ( testServer *testserver.ArtifactServer testMetricsH controller.Metrics ctx = ctrl.SetupSignalHandler() + + testOCIRepositoryReconciler *OCIRepositoryReconciler ) var ( @@ -167,7 +169,7 @@ func setupRegistryServer(ctx context.Context, workspaceDir string, opts registry config.HTTP.Addr = fmt.Sprintf(":%d", port) config.HTTP.DrainTimeout = time.Duration(10) * time.Second - config.Storage = map[string]configuration.Parameters{"inmemory": map[string]interface{}{}} + config.Storage = map[string]configuration.Parameters{"inmemory": map[string]any{}} if opts.withBasicAuth { // create htpasswd file (w BCrypt, which is required) @@ -345,12 +347,13 @@ func TestMain(m *testing.M) { testCache = cache.New(5, 1*time.Second) cacheRecorder := cache.MustMakeMetrics() - if err := (&OCIRepositoryReconciler{ + testOCIRepositoryReconciler = &OCIRepositoryReconciler{ Client: testEnv, Recorder: events.NewFakeRecorder(32, false), Metrics: testMetricsH, Storage: testStorage, - }).SetupWithManager(testEnv, OCIRepositoryReconcilerOptions{ + } + if err := testOCIRepositoryReconciler.SetupWithManager(testEnv, OCIRepositoryReconcilerOptions{ RateLimiter: controller.GetDefaultRateLimiter(), }); err != nil { panic(fmt.Sprintf("Failed to start OCIRepositoryReconciler: %v", err)) @@ -510,8 +513,9 @@ func randStringRunes(n int) string { return string(b) } +//go:fix inline func int64p(i int64) *int64 { - return &i + return new(i) } func logOCIRepoStatus(t *testing.T, obj *sourcev1.OCIRepository) { diff --git a/internal/error/error.go b/internal/error/error.go index cb3a8cd78..650ffb26a 100644 --- a/internal/error/error.go +++ b/internal/error/error.go @@ -80,13 +80,11 @@ func NewStalling(err error, reason string) *Stalling { // Stalling errors are not returned to the runtime. Log it explicitly. // Since this failure requires user interaction, send warning notification. return &Stalling{ - Reason: reason, - Err: err, - Config: Config{ - Event: corev1.EventTypeWarning, - Log: true, - Notification: true, - }, + Reason: reason, + Err: err, + Event: corev1.EventTypeWarning, + Log: true, + Notification: true, } } @@ -124,10 +122,8 @@ func NewWaiting(err error, reason string) *Waiting { return &Waiting{ Reason: reason, Err: err, - Config: Config{ - Event: corev1.EventTypeNormal, - Log: true, - }, + Event: corev1.EventTypeNormal, + Log: true, } } @@ -157,11 +153,9 @@ func NewGeneric(err error, reason string) *Generic { // Since it's a generic error, it'll be returned to the runtime and logged // automatically, do not log it. Send failure notification. return &Generic{ - Reason: reason, - Err: err, - Config: Config{ - Event: corev1.EventTypeWarning, - Notification: true, - }, + Reason: reason, + Err: err, + Event: corev1.EventTypeWarning, + Notification: true, } } diff --git a/internal/features/features.go b/internal/features/features.go index edb9beb17..bc4855075 100644 --- a/internal/features/features.go +++ b/internal/features/features.go @@ -38,14 +38,15 @@ var features = map[string]bool{ CacheSecretsAndConfigMaps: false, } -func init() { - auth.SetFeatureGates(features) +var retiredFeatures = map[string]string{ + // Retired in Flux 2.10. + auth.FeatureGateObjectLevelWorkloadIdentity: auth.FeatureGateObjectLevelWorkloadIdentityRetirementReason, } // FeatureGates contains a list of all supported feature gates and // their default values. -func FeatureGates() map[string]bool { - return features +func FeatureGates() (map[string]bool, map[string]string) { + return features, retiredFeatures } // Enabled verifies whether the feature is enabled or not. diff --git a/internal/helm/chart/builder_local.go b/internal/helm/chart/builder_local.go index 44399a80a..d9638c559 100644 --- a/internal/helm/chart/builder_local.go +++ b/internal/helm/chart/builder_local.go @@ -146,7 +146,7 @@ func (b *localChartBuilder) Build(ctx context.Context, ref Reference, p string, // Merge chart values, if instructed var ( - mergedValues map[string]interface{} + mergedValues map[string]any valuesFiles []string ) if len(opts.GetValuesFiles()) > 0 { @@ -199,8 +199,8 @@ func (b *localChartBuilder) Build(ctx context.Context, ref Reference, p string, // file is considered an error. If ignoreMissing is true, missing files are ignored. // It returns the merge result and the list of files that contributed to that result, // or an error. -func mergeFileValues(baseDir string, paths []string, ignoreMissing bool) (map[string]interface{}, []string, error) { - mergedValues := make(map[string]interface{}) +func mergeFileValues(baseDir string, paths []string, ignoreMissing bool) (map[string]any, []string, error) { + mergedValues := make(map[string]any) valuesFiles := make([]string, 0, len(paths)) for _, p := range paths { secureP, err := securejoin.SecureJoin(baseDir, p) @@ -222,7 +222,7 @@ func mergeFileValues(baseDir string, paths []string, ignoreMissing bool) (map[st if err != nil { return nil, nil, fmt.Errorf("could not read values from file '%s': %w", p, err) } - values := make(map[string]interface{}) + values := make(map[string]any) err = yaml.Unmarshal(b, &values) if err != nil { return nil, nil, fmt.Errorf("unmarshaling values from '%s' failed: %w", p, err) diff --git a/internal/helm/chart/builder_local_test.go b/internal/helm/chart/builder_local_test.go index cf9d6a742..9b8b5b512 100644 --- a/internal/helm/chart/builder_local_test.go +++ b/internal/helm/chart/builder_local_test.go @@ -285,7 +285,7 @@ func Test_mergeFileValues(t *testing.T) { files []*common.File paths []string ignoreMissing bool - wantValues map[string]interface{} + wantValues map[string]any wantFiles []string wantErr string }{ @@ -297,7 +297,7 @@ func Test_mergeFileValues(t *testing.T) { {Name: "c.yaml", Data: []byte("b: d")}, }, paths: []string{"a.yaml", "b.yaml", "c.yaml"}, - wantValues: map[string]interface{}{ + wantValues: map[string]any{ "a": "b", "b": "d", }, @@ -328,7 +328,7 @@ func Test_mergeFileValues(t *testing.T) { }, paths: []string{"a.yaml", "b.yaml"}, ignoreMissing: true, - wantValues: map[string]interface{}{ + wantValues: map[string]any{ "a": "b", }, wantFiles: []string{"a.yaml"}, @@ -337,7 +337,7 @@ func Test_mergeFileValues(t *testing.T) { name: "all files missing", paths: []string{"a.yaml"}, ignoreMissing: true, - wantValues: map[string]interface{}{}, + wantValues: map[string]any{}, wantFiles: []string{}, }, } diff --git a/internal/helm/chart/builder_remote.go b/internal/helm/chart/builder_remote.go index dbe3addca..2cd76c112 100644 --- a/internal/helm/chart/builder_remote.go +++ b/internal/helm/chart/builder_remote.go @@ -235,8 +235,8 @@ func setBuildMetaData(version, versionMetadata string) (*semver.Version, error) // missing files are ignored. // It returns the merge result and the list of files that contributed to that result, // or an error. -func mergeChartValues(chart *helmchart.Chart, paths []string, ignoreMissing bool) (map[string]interface{}, []string, error) { - mergedValues := make(map[string]interface{}) +func mergeChartValues(chart *helmchart.Chart, paths []string, ignoreMissing bool) (map[string]any, []string, error) { + mergedValues := make(map[string]any) valuesFiles := make([]string, 0, len(paths)) for _, p := range paths { cfn := filepath.Clean(p) @@ -258,7 +258,7 @@ func mergeChartValues(chart *helmchart.Chart, paths []string, ignoreMissing bool } return nil, nil, fmt.Errorf("no values file found at path '%s'", p) } - values := make(map[string]interface{}) + values := make(map[string]any) if err := yaml.Unmarshal(b, &values); err != nil { return nil, nil, fmt.Errorf("unmarshaling values from '%s' failed: %w", p, err) } diff --git a/internal/helm/chart/builder_remote_test.go b/internal/helm/chart/builder_remote_test.go index 4301f0630..bf6534606 100644 --- a/internal/helm/chart/builder_remote_test.go +++ b/internal/helm/chart/builder_remote_test.go @@ -456,7 +456,7 @@ func Test_mergeChartValues(t *testing.T) { chart *helmchart.Chart paths []string ignoreMissing bool - wantValues map[string]interface{} + wantValues map[string]any wantFiles []string wantErr string }{ @@ -470,7 +470,7 @@ func Test_mergeChartValues(t *testing.T) { }, }, paths: []string{"a.yaml", "b.yaml", "c.yaml"}, - wantValues: map[string]interface{}{ + wantValues: map[string]any{ "a": "b", "b": "d", }, @@ -482,12 +482,12 @@ func Test_mergeChartValues(t *testing.T) { Files: []*common.File{ {Name: "c.yaml", Data: []byte("b: d")}, }, - Values: map[string]interface{}{ + Values: map[string]any{ "a": "b", }, }, paths: []string{chartutil.ValuesfileName, "c.yaml"}, - wantValues: map[string]interface{}{ + wantValues: map[string]any{ "a": "b", "b": "d", }, @@ -518,7 +518,7 @@ func Test_mergeChartValues(t *testing.T) { }, paths: []string{"a.yaml", "b.yaml"}, ignoreMissing: true, - wantValues: map[string]interface{}{ + wantValues: map[string]any{ "a": "b", }, wantFiles: []string{"a.yaml"}, @@ -528,19 +528,19 @@ func Test_mergeChartValues(t *testing.T) { chart: &helmchart.Chart{}, paths: []string{"a.yaml"}, ignoreMissing: true, - wantValues: map[string]interface{}{}, + wantValues: map[string]any{}, wantFiles: []string{}, }, { name: "uses chart values ignoring missing file", chart: &helmchart.Chart{ - Values: map[string]interface{}{ + Values: map[string]any{ "a": "b", }, }, paths: []string{chartutil.ValuesfileName, "c.yaml"}, ignoreMissing: true, - wantValues: map[string]interface{}{ + wantValues: map[string]any{ "a": "b", }, wantFiles: []string{chartutil.ValuesfileName}, diff --git a/internal/helm/chart/secureloader/ignore/rules.go b/internal/helm/chart/secureloader/ignore/rules.go index d8054b44d..57858f432 100644 --- a/internal/helm/chart/secureloader/ignore/rules.go +++ b/internal/helm/chart/secureloader/ignore/rules.go @@ -170,10 +170,10 @@ func (r *Rules) parseRule(rule string) error { rule = strings.TrimSuffix(rule, "/") } - if strings.HasPrefix(rule, "/") { + if after, ok := strings.CutPrefix(rule, "/"); ok { // Require path matches the root path. p.match = func(n string, fi os.FileInfo) bool { - rule = strings.TrimPrefix(rule, "/") + rule = after ok, err := filepath.Match(rule, n) if err != nil { log.Printf("Failed to compile %q: %s", rule, err) diff --git a/internal/helm/getter/client_opts_test.go b/internal/helm/getter/client_opts_test.go index 87247b6a8..5d18b9873 100644 --- a/internal/helm/getter/client_opts_test.go +++ b/internal/helm/getter/client_opts_test.go @@ -51,17 +51,13 @@ func TestGetClientOpts(t *testing.T) { { name: "HelmRepository with certSecretRef discards TLS config in secretRef", certSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ca-file", - }, + Name: "ca-file", Data: map[string][]byte{ "ca.crt": tlsCA, }, }, authSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth", - }, + Name: "auth", Data: map[string][]byte{ "username": []byte("user"), "password": []byte("pass"), @@ -75,9 +71,7 @@ func TestGetClientOpts(t *testing.T) { { name: "HelmRepository with TLS config only in secretRef is marked as deprecated", authSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth-tls", - }, + Name: "auth-tls", Data: map[string][]byte{ "username": []byte("user"), "password": []byte("pass"), @@ -93,9 +87,7 @@ func TestGetClientOpts(t *testing.T) { { name: "OCI HelmRepository with secretRef has auth configured", authSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth-oci", - }, + Name: "auth-oci", Data: map[string][]byte{ "username": []byte("user"), "password": []byte("pass"), @@ -117,9 +109,7 @@ func TestGetClientOpts(t *testing.T) { { name: "OCI HelmRepository with insecure repository", authSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth-oci", - }, + Name: "auth-oci", Data: map[string][]byte{ "username": []byte("user"), "password": []byte("pass"), @@ -192,17 +182,13 @@ func TestGetClientOpts_registryTLSLoginOption(t *testing.T) { { name: "with valid caFile", certSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ca-file", - }, + Name: "ca-file", Data: map[string][]byte{ "ca.crt": tlsCA, }, }, authSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth-oci", - }, + Name: "auth-oci", Data: map[string][]byte{ "username": []byte("user"), "password": []byte("pass"), @@ -214,15 +200,11 @@ func TestGetClientOpts_registryTLSLoginOption(t *testing.T) { { name: "without caFile", certSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "ca-file", - }, + Name: "ca-file", Data: map[string][]byte{}, }, authSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth-oci", - }, + Name: "auth-oci", Data: map[string][]byte{ "username": []byte("user"), "password": []byte("pass"), @@ -234,9 +216,7 @@ func TestGetClientOpts_registryTLSLoginOption(t *testing.T) { name: "without cert secret", certSecret: nil, authSecret: &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: "auth-oci", - }, + Name: "auth-oci", Data: map[string][]byte{ "username": []byte("user"), "password": []byte("pass"), diff --git a/internal/helm/registry/auth_test.go b/internal/helm/registry/auth_test.go index c0e3ffde5..b61d1b523 100644 --- a/internal/helm/registry/auth_test.go +++ b/internal/helm/registry/auth_test.go @@ -123,10 +123,10 @@ func TestLoginOptionFromSecret(t *testing.T) { t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) - secret := corev1.Secret{} - secret.Name = "test-secret" - secret.Data = tt.secretData - secret.Type = tt.secretType + secret := corev1.Secret{ + Name: "test-secret", + Data: tt.secretData, + Type: tt.secretType} _, err := KeychainFromSecret(tt.url, secret) g.Expect(err != nil).To(Equal(tt.wantErr)) diff --git a/internal/helm/repository/chart_repository.go b/internal/helm/repository/chart_repository.go index 4e2f7dfc4..e82f93f90 100644 --- a/internal/helm/repository/chart_repository.go +++ b/internal/helm/repository/chart_repository.go @@ -27,6 +27,7 @@ import ( "net/url" "os" "path" + "slices" "sort" "strings" "sync" @@ -88,19 +89,19 @@ func IndexFromBytes(b []byte) (*repo.IndexFile, error) { } for name, cvs := range i.Entries { - for idx := len(cvs) - 1; idx >= 0; idx-- { - if cvs[idx] == nil { + for idx, cv := range slices.Backward(cvs) { + if cv == nil { cvs = append(cvs[:idx], cvs[idx+1:]...) continue } // When metadata section missing, initialize with no data - if cvs[idx].Metadata == nil { - cvs[idx].Metadata = &chart.Metadata{} + if cv.Metadata == nil { + cv.Metadata = &chart.Metadata{} } - if cvs[idx].APIVersion == "" { - cvs[idx].APIVersion = chart.APIVersionV1 + if cv.APIVersion == "" { + cv.APIVersion = chart.APIVersionV1 } - if err := cvs[idx].Validate(); ignoreSkippableChartValidationError(err) != nil { + if err := cv.Validate(); ignoreSkippableChartValidationError(err) != nil { cvs = append(cvs[:idx], cvs[idx+1:]...) } } @@ -503,7 +504,7 @@ func (r *ChartRepository) VerifyChart(_ context.Context, _ *repo.ChartVersion) ( // // Can potentially be replaced when Helm PR for JSON support has been merged. // xref: https://github.com/helm/helm/pull/12245 -func jsonOrYamlUnmarshal(b []byte, i interface{}) error { +func jsonOrYamlUnmarshal(b []byte, i any) error { if json.Valid(b) { return json.Unmarshal(b, i) } diff --git a/internal/helm/repository/chart_repository_test.go b/internal/helm/repository/chart_repository_test.go index c9aadf8a2..cb41acf68 100644 --- a/internal/helm/repository/chart_repository_test.go +++ b/internal/helm/repository/chart_repository_test.go @@ -94,7 +94,6 @@ func TestIndexFromFile(t *testing.T) { } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) @@ -158,7 +157,6 @@ entries: }, } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) t.Parallel() @@ -366,7 +364,6 @@ func TestChartRepository_DownloadChart(t *testing.T) { }, } for _, tt := range tests { - tt := tt t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) t.Parallel() diff --git a/internal/helm/repository/oci_chart_repository_test.go b/internal/helm/repository/oci_chart_repository_test.go index b63aef8da..b10f25be6 100644 --- a/internal/helm/repository/oci_chart_repository_test.go +++ b/internal/helm/repository/oci_chart_repository_test.go @@ -255,7 +255,6 @@ func TestOCIChartRepository_DownloadChart(t *testing.T) { } for _, tc := range testCases { - tc := tc t.Run(tc.name, func(t *testing.T) { t.Parallel() diff --git a/internal/index/digest.go b/internal/index/digest.go index 1f7bd642f..de9ea1f8e 100644 --- a/internal/index/digest.go +++ b/internal/index/digest.go @@ -19,6 +19,7 @@ package index import ( "fmt" "io" + "maps" "sort" "strings" "sync" @@ -54,9 +55,7 @@ func WithIndex(i map[string]string) DigesterOption { if d.index == nil { d.index = make(map[string]string, len(i)) } - for k, v := range i { - d.index[k] = v - } + maps.Copy(d.index, i) d.reset() } } @@ -118,9 +117,7 @@ func (i *Digester) Index() map[string]string { defer i.mu.RUnlock() index := make(map[string]string, len(i.index)) - for k, v := range i.index { - index[k] = v - } + maps.Copy(index, i.index) return index } diff --git a/internal/mock/s3/server.go b/internal/mock/s3/server.go index ae5835308..b5e0644fa 100644 --- a/internal/mock/s3/server.go +++ b/internal/mock/s3/server.go @@ -103,17 +103,17 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) { return } - contents := "" + var contents strings.Builder for _, o := range s.Objects { etag := md5.Sum(o.Content) - contents += fmt.Sprintf(` + contents.WriteString(fmt.Sprintf(` %s %s %d "%x" STANDARD - `, o.Key, o.LastModified.UTC().Format(time.RFC3339), len(o.Content), etag) + `, o.Key, o.LastModified.UTC().Format(time.RFC3339), len(o.Content), etag)) } fmt.Fprintf(w, ` @@ -127,7 +127,7 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) { false %s - `, s.BucketName, len(s.Objects), contents) + `, s.BucketName, len(s.Objects), contents.String()) default: if !strings.HasPrefix(r.URL.Path, "/"+s.BucketName+"/") { writeError(w, r, http.StatusNotFound, "NoSuchBucket", "The specified bucket does not exist") diff --git a/internal/oci/notation/notation_test.go b/internal/oci/notation/notation_test.go index 1a547617d..3a3b2fa61 100644 --- a/internal/oci/notation/notation_test.go +++ b/internal/oci/notation/notation_test.go @@ -654,7 +654,7 @@ func TestRemoteRepoAuthTokenReuse(t *testing.T) { // setting the scope hint on the context exactly like the ORAS repository // methods do during verification. doRequests := func(client *oauth.Client, n int) { - for i := 0; i < n; i++ { + for range n { ctx := oauth.WithScopesForHost(context.Background(), host, scope) req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s/v2/%s/manifests/latest", baseURL, repository), nil) @@ -787,15 +787,15 @@ func (t *testLogger) Enabled(level int) bool { return true } -func (t *testLogger) Info(level int, msg string, keysAndValues ...interface{}) { +func (t *testLogger) Info(level int, msg string, keysAndValues ...any) { t.doLog(msg) } -func (t *testLogger) Error(err error, msg string, keysAndValues ...interface{}) { +func (t *testLogger) Error(err error, msg string, keysAndValues ...any) { t.doLog(msg) } -func (t *testLogger) WithValues(keysAndValues ...interface{}) logr.LogSink { +func (t *testLogger) WithValues(keysAndValues ...any) logr.LogSink { return t } diff --git a/internal/reconcile/event.go b/internal/reconcile/event.go index 57ebf4eb4..cfb800b27 100644 --- a/internal/reconcile/event.go +++ b/internal/reconcile/event.go @@ -39,7 +39,7 @@ import ( // The action identifies the reconcile stage that produced the event and is // recorded in the event's action field. Callers pass the relevant // sourcev1.Action for the stage. -func EventLogf(ctx context.Context, rec events.Recorder, obj runtime.Object, eventType string, reason string, action sourcev1.Action, messageFmt string, args ...interface{}) { +func EventLogf(ctx context.Context, rec events.Recorder, obj runtime.Object, eventType string, reason string, action sourcev1.Action, messageFmt string, args ...any) { msg := fmt.Sprintf(messageFmt, args...) if eventType == corev1.EventTypeWarning { ctrl.LoggerFrom(ctx).Error(errors.New(reason), msg) diff --git a/internal/reconcile/reconcile_test.go b/internal/reconcile/reconcile_test.go index e22f370b5..ef3978d4c 100644 --- a/internal/reconcile/reconcile_test.go +++ b/internal/reconcile/reconcile_test.go @@ -174,9 +174,7 @@ func TestComputeReconcileResult(t *testing.T) { }, recErr: &serror.Generic{ Err: fmt.Errorf("some error"), Reason: "some reason", - Config: serror.Config{ - Ignore: true, - }, + Ignore: true, }, wantResult: ctrl.Result{RequeueAfter: testSuccessInterval}, afterFunc: func(t *WithT, obj conditions.Setter, patchOpts *patch.HelperOptions) { diff --git a/internal/reconcile/summarize/matchers_test.go b/internal/reconcile/summarize/matchers_test.go index b71aa99c8..ea09a82ea 100644 --- a/internal/reconcile/summarize/matchers_test.go +++ b/internal/reconcile/summarize/matchers_test.go @@ -39,7 +39,7 @@ type haveStatusObservedGeneration struct { actual int64 } -func (m *haveStatusObservedGeneration) Match(actual interface{}) (success bool, err error) { +func (m *haveStatusObservedGeneration) Match(actual any) (success bool, err error) { obj, ok := actual.(runtime.Object) if !ok { return false, fmt.Errorf("actual should be a runtime object") @@ -54,11 +54,11 @@ func (m *haveStatusObservedGeneration) Match(actual interface{}) (success bool, return Equal(m.expected).Match(og) } -func (m *haveStatusObservedGeneration) FailureMessage(actual interface{}) (message string) { +func (m *haveStatusObservedGeneration) FailureMessage(actual any) (message string) { return fmt.Sprintf("expected\n\t%d\nto match\n\t%d\n", m.actual, m.expected) } -func (m *haveStatusObservedGeneration) NegatedFailureMessage(actual interface{}) (message string) { +func (m *haveStatusObservedGeneration) NegatedFailureMessage(actual any) (message string) { return fmt.Sprintf("expected\n\t%d\nto not match\n\t%d\n", m.actual, m.expected) } @@ -75,7 +75,7 @@ type haveStatusLastHandledReconcileAt struct { actual string } -func (m *haveStatusLastHandledReconcileAt) Match(actual interface{}) (success bool, err error) { +func (m *haveStatusLastHandledReconcileAt) Match(actual any) (success bool, err error) { obj, ok := actual.(runtime.Object) if !ok { return false, fmt.Errorf("actual should be a runtime object") @@ -90,10 +90,10 @@ func (m *haveStatusLastHandledReconcileAt) Match(actual interface{}) (success bo return Equal(m.expected).Match(ra) } -func (m *haveStatusLastHandledReconcileAt) FailureMessage(actual interface{}) (message string) { +func (m *haveStatusLastHandledReconcileAt) FailureMessage(actual any) (message string) { return fmt.Sprintf("expected\n\t%s\nto match\n\t%s\n", m.actual, m.expected) } -func (m *haveStatusLastHandledReconcileAt) NegatedFailureMessage(actual interface{}) (message string) { +func (m *haveStatusLastHandledReconcileAt) NegatedFailureMessage(actual any) (message string) { return fmt.Sprintf("expected\n\t%s\nto not match\n\t%s\n", m.actual, m.expected) } diff --git a/internal/reconcile/summarize/processor.go b/internal/reconcile/summarize/processor.go index 2b1fdddb6..4dda3c508 100644 --- a/internal/reconcile/summarize/processor.go +++ b/internal/reconcile/summarize/processor.go @@ -72,7 +72,7 @@ func ErrorActionHandler(ctx context.Context, recorder events.Recorder, obj clien } // logError logs error based on the passed error configurations. -func logError(ctx context.Context, eventType string, err error, msg string, keysAndValues ...interface{}) { +func logError(ctx context.Context, eventType string, err error, msg string, keysAndValues ...any) { switch eventType { case corev1.EventTypeNormal, serror.EventTypeNone: ctrl.LoggerFrom(ctx).Info(msg, keysAndValues...) diff --git a/internal/reconcile/summarize/processor_test.go b/internal/reconcile/summarize/processor_test.go index d7af0a00f..652516b14 100644 --- a/internal/reconcile/summarize/processor_test.go +++ b/internal/reconcile/summarize/processor_test.go @@ -21,7 +21,6 @@ import ( "testing" . "github.com/onsi/gomega" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/fluxcd/pkg/apis/meta" @@ -109,9 +108,7 @@ func TestRecordReconcileReq(t *testing.T) { g := NewWithT(t) obj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-obj", - }, + GenerateName: "test-obj", } if tt.beforeFunc != nil { diff --git a/internal/reconcile/summarize/summary_test.go b/internal/reconcile/summarize/summary_test.go index c9acbca38..afbf9312c 100644 --- a/internal/reconcile/summarize/summary_test.go +++ b/internal/reconcile/summarize/summary_test.go @@ -328,10 +328,8 @@ func TestSummarizeAndPatch(t *testing.T) { g.Expect(sourcev1.AddToScheme(scheme)).To(Succeed()) obj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-", - Generation: tt.generation, - }, + GenerateName: "test-", + Generation: tt.generation, Spec: sourcev1.GitRepositorySpec{ Interval: metav1.Duration{Duration: 5 * time.Second}, }, @@ -458,9 +456,7 @@ func TestSummarizeAndPatch_Intermediate(t *testing.T) { Build() obj := &sourcev1.GitRepository{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "test-", - }, + GenerateName: "test-", Spec: sourcev1.GitRepositorySpec{ Interval: metav1.Duration{Duration: interval}, }, diff --git a/internal/util/temp_root.go b/internal/util/temp_root.go new file mode 100644 index 000000000..4c3df7fef --- /dev/null +++ b/internal/util/temp_root.go @@ -0,0 +1,113 @@ +/* +Copyright 2026 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 util + +import ( + "context" + "errors" + "fmt" + "io/fs" + "os" + "path/filepath" + "strings" + "time" + + "github.com/go-logr/logr" +) + +// staleSuffix is appended to a temp root that a previous process left behind, +// when it is moved aside to be purged. +const staleSuffix = ".stale-" + +// PrepareTempRoot gives the process an empty directory at root for its +// temporary files. Anything a previous process left in root, because it +// exited without running its deferred cleanup, is moved aside first. It +// returns the paths that hold leftovers, including those a previous purge +// did not finish, so that the caller can remove them with PurgeTempRoots. +// It must be called before the reconcilers start. +func PrepareTempRoot(root string) ([]string, error) { + _, err := os.Lstat(root) + switch { + case err == nil: + stale := fmt.Sprintf("%s%s%d", root, staleSuffix, time.Now().UnixNano()) + if err := os.Rename(root, stale); err != nil { + return nil, fmt.Errorf("failed to move aside %s: %w", root, err) + } + case !errors.Is(err, fs.ErrNotExist): + return nil, fmt.Errorf("failed to stat %s: %w", root, err) + } + + if err := os.MkdirAll(root, 0o700); err != nil { + return nil, fmt.Errorf("failed to create %s: %w", root, err) + } + + parent, base := filepath.Split(root) + entries, err := os.ReadDir(parent) + if err != nil { + return nil, fmt.Errorf("failed to list %s: %w", parent, err) + } + var stale []string + for _, entry := range entries { + if strings.HasPrefix(entry.Name(), base+staleSuffix) { + stale = append(stale, filepath.Join(parent, entry.Name())) + } + } + return stale, nil +} + +// PurgeTempRoots removes the given paths. A directory is emptied one entry +// at a time, so that ctx is honoured between entries, and then removed. +// Every removed path is logged at info level and every failure at error +// level. It stops as soon as ctx is done and returns the number of paths +// that were removed. Paths it did not remove are picked up by +// PrepareTempRoot on the next start. +func PurgeTempRoots(ctx context.Context, log logr.Logger, paths []string) int { + purged := 0 + for i, path := range paths { + if err := purgeTempRoot(ctx, path); err != nil { + if ctx.Err() != nil { + log.Error(err, "aborted purge of stale tmp dirs", + "path", path, "purged", purged, "remaining", len(paths)-i) + return purged + } + log.Error(err, "failed to remove stale tmp dir", "path", path) + continue + } + log.Info("removed stale tmp dir", "path", path) + purged++ + } + return purged +} + +// purgeTempRoot removes the entries of path one by one, checking ctx before +// each, and then path itself. +func purgeTempRoot(ctx context.Context, path string) error { + // If path is not a readable directory, RemoveAll below deals with it. + entries, _ := os.ReadDir(path) + for _, entry := range entries { + if err := ctx.Err(); err != nil { + return err + } + if err := os.RemoveAll(filepath.Join(path, entry.Name())); err != nil { + return err + } + } + if err := ctx.Err(); err != nil { + return err + } + return os.RemoveAll(path) +} diff --git a/internal/util/temp_root_test.go b/internal/util/temp_root_test.go new file mode 100644 index 000000000..1711cdcbf --- /dev/null +++ b/internal/util/temp_root_test.go @@ -0,0 +1,126 @@ +/* +Copyright 2026 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 util + +import ( + "context" + "os" + "path/filepath" + "testing" + + "github.com/go-logr/logr" + . "github.com/onsi/gomega" +) + +func TestPrepareTempRoot(t *testing.T) { + t.Run("creates the root when it does not exist", func(t *testing.T) { + g := NewWithT(t) + root := filepath.Join(t.TempDir(), "source-controller") + + stale, err := PrepareTempRoot(root) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(stale).To(BeEmpty()) + g.Expect(root).To(BeADirectory()) + }) + + t.Run("moves a previous process's leftovers out of the root", func(t *testing.T) { + g := NewWithT(t) + root := filepath.Join(t.TempDir(), "source-controller") + leftover := filepath.Join(root, "gitrepository-default-foo-123", "repo") + g.Expect(os.MkdirAll(leftover, 0o700)).To(Succeed()) + g.Expect(os.WriteFile(filepath.Join(root, "chart-index-1.yaml"), []byte("x"), 0o600)).To(Succeed()) + + stale, err := PrepareTempRoot(root) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(stale).To(HaveLen(1)) + g.Expect(filepath.Join(stale[0], "gitrepository-default-foo-123", "repo")).To(BeADirectory()) + g.Expect(filepath.Join(stale[0], "chart-index-1.yaml")).To(BeARegularFile()) + + entries, err := os.ReadDir(root) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(entries).To(BeEmpty()) + }) + + t.Run("lists leftovers a previous purge did not finish", func(t *testing.T) { + g := NewWithT(t) + parent := t.TempDir() + root := filepath.Join(parent, "source-controller") + unfinished := filepath.Join(parent, "source-controller"+staleSuffix+"1") + unrelated := filepath.Join(parent, "other-controller"+staleSuffix+"1") + for _, dir := range []string{root, unfinished, unrelated} { + g.Expect(os.MkdirAll(dir, 0o700)).To(Succeed()) + } + + stale, err := PrepareTempRoot(root) + g.Expect(err).ToNot(HaveOccurred()) + g.Expect(stale).To(ContainElement(unfinished)) + g.Expect(stale).ToNot(ContainElement(unrelated)) + g.Expect(stale).To(HaveLen(2)) + }) +} + +func TestPurgeTempRoots(t *testing.T) { + t.Run("removes every path", func(t *testing.T) { + g := NewWithT(t) + parent := t.TempDir() + a := filepath.Join(parent, "a", "nested") + b := filepath.Join(parent, "b") + g.Expect(os.MkdirAll(a, 0o700)).To(Succeed()) + g.Expect(os.MkdirAll(b, 0o700)).To(Succeed()) + + purged := PurgeTempRoots(context.Background(), logr.Discard(), []string{filepath.Join(parent, "a"), b}) + g.Expect(purged).To(Equal(2)) + g.Expect(filepath.Join(parent, "a")).ToNot(BeADirectory()) + g.Expect(b).ToNot(BeADirectory()) + }) + + t.Run("stops when the context is done", func(t *testing.T) { + g := NewWithT(t) + dir := t.TempDir() + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + purged := PurgeTempRoots(ctx, logr.Discard(), []string{dir}) + g.Expect(purged).To(Equal(0)) + g.Expect(dir).To(BeADirectory()) + }) + + t.Run("removes a root with many entries", func(t *testing.T) { + g := NewWithT(t) + root := filepath.Join(t.TempDir(), "source-controller"+staleSuffix+"1") + for _, name := range []string{"gitrepository-default-a-1", "helmchart-default-b-2", "chart-build-3"} { + g.Expect(os.MkdirAll(filepath.Join(root, name, "nested"), 0o700)).To(Succeed()) + } + g.Expect(os.WriteFile(filepath.Join(root, "chart-index-4.yaml"), []byte("x"), 0o600)).To(Succeed()) + + purged := PurgeTempRoots(context.Background(), logr.Discard(), []string{root}) + g.Expect(purged).To(Equal(1)) + g.Expect(root).ToNot(BeAnExistingFile()) + }) + + t.Run("removes files and tolerates missing paths", func(t *testing.T) { + g := NewWithT(t) + parent := t.TempDir() + file := filepath.Join(parent, "source-controller"+staleSuffix+"1") + missing := filepath.Join(parent, "source-controller"+staleSuffix+"2") + g.Expect(os.WriteFile(file, []byte("x"), 0o600)).To(Succeed()) + + purged := PurgeTempRoots(context.Background(), logr.Discard(), []string{file, missing}) + g.Expect(purged).To(Equal(2)) + g.Expect(file).ToNot(BeAnExistingFile()) + }) +} diff --git a/internal/util/temp_test.go b/internal/util/temp_test.go index 2f98079c6..a5e85fc38 100644 --- a/internal/util/temp_test.go +++ b/internal/util/temp_test.go @@ -23,7 +23,6 @@ import ( . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" ) @@ -75,12 +74,8 @@ func Test_pattern(t *testing.T) { func mockObj() client.Object { return &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "foo", - Namespace: "default", - }, + Kind: "Secret", + Name: "foo", + Namespace: "default", } } diff --git a/main.go b/main.go index 3fbef9181..7720169f7 100644 --- a/main.go +++ b/main.go @@ -17,18 +17,21 @@ limitations under the License. package main import ( + "context" + "crypto/tls" "fmt" "os" + "path/filepath" "time" flag "github.com/spf13/pflag" + "github.com/spiffe/go-spiffe/v2/workloadapi" "helm.sh/helm/v4/pkg/getter" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" - "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" ctrlcache "sigs.k8s.io/controller-runtime/pkg/cache" ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" @@ -53,6 +56,8 @@ import ( "github.com/fluxcd/pkg/runtime/metrics" "github.com/fluxcd/pkg/runtime/pprof" "github.com/fluxcd/pkg/runtime/probes" + fluxspiffe "github.com/fluxcd/pkg/spiffe" + fluxbroker "github.com/fluxcd/pkg/spiffe/broker" sourcev1 "github.com/fluxcd/source-controller/api/v1" @@ -63,6 +68,7 @@ import ( "github.com/fluxcd/source-controller/internal/features" "github.com/fluxcd/source-controller/internal/helm" scosign "github.com/fluxcd/source-controller/internal/oci/cosign" + "github.com/fluxcd/source-controller/internal/util" ) const controllerName = "source-controller" @@ -95,27 +101,31 @@ func main() { ) var ( - metricsAddr string - eventsAddr string - healthAddr string - concurrent int - requeueDependency time.Duration - helmIndexLimit int64 - helmChartLimit int64 - helmChartFileLimit int64 - artifactOptions artcfg.Options - clientOptions client.Options - logOptions logger.Options - leaderElectionOptions leaderelection.Options - rateLimiterOptions helper.RateLimiterOptions - featureGates feathelper.FeatureGates - watchOptions helper.WatchOptions - intervalJitterOptions jitter.IntervalOptions - helmCacheMaxSize int - helmCacheTTL string - helmCachePurgeInterval string - tokenCacheOptions pkgcache.TokenFlags - defaultServiceAccount string + metricsAddr string + eventsAddr string + healthAddr string + concurrent int + requeueDependency time.Duration + helmIndexLimit int64 + helmChartLimit int64 + helmChartFileLimit int64 + artifactOptions artcfg.Options + clientOptions client.Options + logOptions logger.Options + leaderElectionOptions leaderelection.Options + rateLimiterOptions helper.RateLimiterOptions + featureGates feathelper.FeatureGates + watchOptions helper.WatchOptions + intervalJitterOptions jitter.IntervalOptions + helmCacheMaxSize int + helmCacheTTL string + helmCachePurgeInterval string + tokenCacheOptions pkgcache.TokenFlags + defaultServiceAccount string + authorizedArtifactClients []string + authorizedEventServers []string + spiffeBrokerEndpoint string + spiffeBrokerIDs []string ) flag.StringVar(&metricsAddr, "metrics-addr", envOrDefault("METRICS_ADDR", ":8080"), @@ -144,6 +154,14 @@ func main() { "The list of hostkey algorithms to use for ssh connections, arranged from most preferred to the least.") flag.StringVar(&defaultServiceAccount, auth.ControllerFlagDefaultServiceAccount, "", "Default service account to use for workload identity when not specified in resources.") + flag.StringArrayVar(&authorizedArtifactClients, fluxspiffe.ControllerFlagAuthorizedArtifactClientSPIFFEID, nil, + "SPIFFE ID authorized to fetch artifacts from the artifact server. Can be repeated.") + flag.StringArrayVar(&authorizedEventServers, fluxspiffe.ControllerFlagAuthorizedEventServerSPIFFEID, nil, + "SPIFFE ID of an event server authorized to receive events. Can be repeated.") + flag.StringVar(&spiffeBrokerEndpoint, fluxspiffe.ControllerFlagSPIFFEBrokerEndpoint, "", + "The gRPC URL of the SPIFFE Broker endpoint.") + flag.StringArrayVar(&spiffeBrokerIDs, fluxspiffe.ControllerFlagSPIFFEBrokerID, nil, + "SPIFFE ID of the SPIFFE Broker endpoint. Can be repeated.") artifactOptions.BindFlags(flag.CommandLine) clientOptions.BindFlags(flag.CommandLine) @@ -159,28 +177,15 @@ func main() { logger.SetLogger(logger.NewLogger(logOptions)) - if defaultServiceAccount != "" { - auth.SetDefaultServiceAccount(defaultServiceAccount) - } + staleTempDirs := setupTempRoot() + + ctx := ctrl.SetupSignalHandler() if err := featureGates.WithLogger(setupLog).SupportedFeatures(features.FeatureGates()); err != nil { setupLog.Error(err, "unable to load feature gates") os.Exit(1) } - switch enabled, err := features.Enabled(auth.FeatureGateObjectLevelWorkloadIdentity); { - case err != nil: - setupLog.Error(err, "unable to check feature gate "+auth.FeatureGateObjectLevelWorkloadIdentity) - os.Exit(1) - case enabled: - auth.EnableObjectLevelWorkloadIdentity() - } - - if auth.InconsistentObjectLevelConfiguration() { - setupLog.Error(auth.ErrInconsistentObjectLevelConfiguration, "invalid configuration") - os.Exit(1) - } - if err := intervalJitterOptions.SetGlobalJitter(nil); err != nil { setupLog.Error(err, "unable to set global jitter") os.Exit(1) @@ -191,11 +196,69 @@ func main() { mgr := mustSetupManager(metricsAddr, healthAddr, concurrent, watchOptions, clientOptions, leaderElectionOptions) probes.SetupChecks(mgr, setupLog) + if err := mgr.AddReadyzCheck("leader-election", probes.LeaderElectedReadyz(mgr.Elected())); err != nil { + setupLog.Error(err, "unable to create leader election readiness check") + os.Exit(1) + } metrics := helper.NewMetrics(mgr, metrics.MustMakeRecorder(), sourcev1.SourceFinalizer) cacheRecorder := cache.MustMakeMetrics() - eventRecorder, err := events.NewRecorder(ctrl.Log, eventsAddr, controllerName, events.WithManager(mgr)) + var eventTLSConfig *tls.Config + var spiffeBroker fluxbroker.Client + if len(authorizedArtifactClients) > 0 || len(authorizedEventServers) > 0 || + spiffeBrokerEndpoint != "" || len(spiffeBrokerIDs) > 0 { + source, err := workloadapi.NewX509Source(ctx) + if err != nil { + setupLog.Error(err, "unable to create SPIFFE X.509 source") + os.Exit(1) + } + defer func() { + if err := source.Close(); err != nil { + setupLog.Error(err, "unable to close SPIFFE X.509 source") + } + }() + + if len(authorizedArtifactClients) > 0 { + artifactTLSConfig, err := fluxspiffe.BuildTLSConfig(source, authorizedArtifactClients...) + if err != nil { + setupLog.Error(err, "unable to configure SPIFFE mTLS for the artifact server") + os.Exit(1) + } + setupLog.Info("artifact server will use inter-controller mTLS via SPIFFE", "spiffeIDs", authorizedArtifactClients) + artifactOptions.TLSConfig = artifactTLSConfig + } + + if len(authorizedEventServers) > 0 { + eventTLSConfig, err = fluxspiffe.BuildTLSConfig(source, authorizedEventServers...) + if err != nil { + setupLog.Error(err, "unable to configure SPIFFE mTLS for the event server") + os.Exit(1) + } + setupLog.Info("event server will use inter-controller mTLS via SPIFFE", "spiffeIDs", authorizedEventServers) + } + + if spiffeBrokerEndpoint != "" || len(spiffeBrokerIDs) > 0 { + spiffeBroker, err = fluxbroker.New(spiffeBrokerEndpoint, spiffeBrokerIDs, source) + if err != nil { + setupLog.Error(err, "unable to create SPIFFE Broker client") + os.Exit(1) + } + defer func() { + if err := spiffeBroker.Close(); err != nil { + setupLog.Error(err, "unable to close SPIFFE Broker client") + } + }() + setupLog.Info("object-level workload identity will use the SPIFFE Broker API", + "endpoint", spiffeBrokerEndpoint, "spiffeIDs", spiffeBrokerIDs) + } + } + + eventRecorderOpts := []events.RecorderOption{events.WithManager(mgr)} + if eventTLSConfig != nil { + eventRecorderOpts = append(eventRecorderOpts, events.WithTLSConfig(eventTLSConfig)) + } + eventRecorder, err := events.NewRecorder(ctrl.Log, eventsAddr, controllerName, eventRecorderOpts...) if err != nil { setupLog.Error(err, "unable to create event recorder") os.Exit(1) @@ -230,15 +293,15 @@ func main() { } } - ctx := ctrl.SetupSignalHandler() - if err := (&controller.GitRepositoryReconciler{ - Client: mgr.GetClient(), - Recorder: eventRecorder, - Metrics: metrics, - Storage: storage, - ControllerName: controllerName, - TokenCache: tokenCache, + Client: mgr.GetClient(), + Recorder: eventRecorder, + Metrics: metrics, + Storage: storage, + ControllerName: controllerName, + TokenCache: tokenCache, + DefaultServiceAccount: defaultServiceAccount, + SPIFFEBroker: spiffeBroker, }).SetupWithManager(mgr, controller.GitRepositoryReconcilerOptions{ DependencyRequeueInterval: requeueDependency, RateLimiter: helper.GetRateLimiter(rateLimiterOptions), @@ -283,12 +346,14 @@ func main() { } if err := (&controller.BucketReconciler{ - Client: mgr.GetClient(), - Recorder: eventRecorder, - Metrics: metrics, - Storage: storage, - ControllerName: controllerName, - TokenCache: tokenCache, + Client: mgr.GetClient(), + Recorder: eventRecorder, + Metrics: metrics, + Storage: storage, + ControllerName: controllerName, + TokenCache: tokenCache, + DefaultServiceAccount: defaultServiceAccount, + SPIFFEBroker: spiffeBroker, }).SetupWithManager(mgr, controller.BucketReconcilerOptions{ RateLimiter: helper.GetRateLimiter(rateLimiterOptions), }); err != nil { @@ -304,6 +369,8 @@ func main() { TokenCache: tokenCache, CosignVerifierFactory: CosignVerifierFactory, Metrics: metrics, + DefaultServiceAccount: defaultServiceAccount, + SPIFFEBroker: spiffeBroker, }).SetupWithManager(mgr, controller.OCIRepositoryReconcilerOptions{ RateLimiter: helper.GetRateLimiter(rateLimiterOptions), }); err != nil { @@ -325,6 +392,8 @@ func main() { } }() + purgeStaleTempDirs(ctx, staleTempDirs) + setupLog.Info("starting manager") if err := mgr.Start(ctx); err != nil { setupLog.Error(err, "problem running manager") @@ -391,7 +460,7 @@ func mustSetupManager(metricsAddr, healthAddr string, maxConcurrent int, ExtraHandlers: pprof.GetHandlers(), }, Controller: ctrlcfg.Controller{ - RecoverPanic: ptr.To(true), + RecoverPanic: new(true), MaxConcurrentReconciles: maxConcurrent, }, } @@ -445,3 +514,40 @@ func envOrDefault(envName, defaultValue string) string { return defaultValue } + +// setupTempRoot points TMPDIR at a directory of this controller's own, so +// that every temporary file and directory it creates, including those of the +// Helm and Git libraries, lands in one place. Whatever a previous process +// left there, because it exited without running its deferred cleanup, is +// moved aside and returned for purgeStaleTempDirs to remove. A failure is +// logged and leaves TMPDIR unchanged, so that it does not prevent startup. +func setupTempRoot() []string { + root := filepath.Join(os.TempDir(), controllerName) + stale, err := util.PrepareTempRoot(root) + if err != nil { + setupLog.Error(err, "unable to set up tmp dir, stale tmp dirs will not be purged") + return nil + } + if err := os.Setenv("TMPDIR", root); err != nil { + setupLog.Error(err, "unable to set TMPDIR, stale tmp dirs will not be purged") + return nil + } + return stale +} + +// purgeStaleTempDirs removes the leftovers found by setupTempRoot in the +// background, so that the manager startup is not delayed. +func purgeStaleTempDirs(ctx context.Context, dirs []string) { + const purgeTimeout = 2 * time.Minute + + if len(dirs) == 0 { + return + } + + setupLog.Info("purging stale tmp dirs", "count", len(dirs)) + go func() { + ctx, cancel := context.WithTimeout(ctx, purgeTimeout) + defer cancel() + util.PurgeTempRoots(ctx, setupLog, dirs) + }() +} diff --git a/tests/registry/registry.go b/tests/registry/registry.go index 28b36fd20..b8a550d17 100644 --- a/tests/registry/registry.go +++ b/tests/registry/registry.go @@ -51,7 +51,7 @@ func New(t *testing.T) string { config := &configuration.Configuration{} config.HTTP.Addr = addr config.HTTP.DrainTimeout = time.Duration(10) * time.Second - config.Storage = map[string]configuration.Parameters{"inmemory": map[string]interface{}{}} + config.Storage = map[string]configuration.Parameters{"inmemory": map[string]any{}} config.Log.AccessLog.Disabled = true config.Log.Level = "error" logrus.SetOutput(io.Discard)