Compare commits
64 Commits
api/v1.5.0
...
main
Author | SHA1 | Date |
---|---|---|
|
a22d67edbb | |
|
955d24142c | |
|
e8a909fac7 | |
|
febff88be7 | |
|
5bea87e16b | |
|
a8c2fc0759 | |
|
6e18c487d7 | |
|
d1c85df902 | |
|
9e150256c7 | |
|
e4160c509c | |
|
fc4adfd030 | |
|
3f4e962c83 | |
|
98ecf2de79 | |
|
a3e6dd6a10 | |
|
8858332c27 | |
|
2503bda903 | |
|
fc126284ab | |
|
8f207d65db | |
|
61f24e3376 | |
|
3f4ba79594 | |
|
d65c81d035 | |
|
47b95d86a3 | |
|
3a6de1fcef | |
|
a52f071bb9 | |
|
0e0e912182 | |
|
7383fe8be5 | |
|
2284a0db5a | |
|
d32f5e26cc | |
|
b416d68479 | |
|
3842f0c471 | |
|
f2e2340807 | |
|
01dfe1208a | |
|
0464717037 | |
|
f3065c67b3 | |
|
882383e44c | |
|
8dd496a132 | |
|
de484cd447 | |
|
b906831c5e | |
|
e95f8d5b38 | |
|
d78779e80b | |
|
637d55d0b9 | |
|
99c6dbb70f | |
|
c32f9e1559 | |
|
f5ddc97108 | |
|
0beb3d02f7 | |
|
5dfaa1a5ef | |
|
4d3a53ac86 | |
|
5b72532bb1 | |
|
3f7486168e | |
|
e970526b1e | |
|
0338a311f4 | |
|
671f7dd377 | |
|
fdeab17bff | |
|
f313afa287 | |
|
30d8d01687 | |
|
d62be26a3f | |
|
bc7166d419 | |
|
c476965793 | |
|
1a9858d725 | |
|
e2eac40c33 | |
|
1967bc0c74 | |
|
2763e548bd | |
|
cc51c36926 | |
|
543a2b0da2 |
|
@ -5,7 +5,7 @@ updates:
|
|||
directory: "/"
|
||||
labels: ["dependencies"]
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: "monthly"
|
||||
groups:
|
||||
go-deps:
|
||||
patterns:
|
||||
|
@ -27,4 +27,4 @@ updates:
|
|||
patterns:
|
||||
- "*"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: "monthly"
|
||||
|
|
|
@ -22,3 +22,9 @@
|
|||
- name: backport:release/v1.4.x
|
||||
description: To be backported to release/v1.4.x
|
||||
color: '#ffd700'
|
||||
- name: backport:release/v1.5.x
|
||||
description: To be backported to release/v1.5.x
|
||||
color: '#ffd700'
|
||||
- name: backport:release/v1.6.x
|
||||
description: To be backported to release/v1.6.x
|
||||
color: '#ffd700'
|
||||
|
|
|
@ -17,7 +17,7 @@ jobs:
|
|||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Create backport PRs
|
||||
uses: korthout/backport-action@be567af183754f6a5d831ae90f648954763f17f5 # v3.1.0
|
||||
uses: korthout/backport-action@436145e922f9561fc5ea157ff406f21af2d6b363 # v3.2.0
|
||||
# xref: https://github.com/korthout/backport-action#inputs
|
||||
with:
|
||||
# Use token to allow workflows to be triggered for the created PR
|
||||
|
|
|
@ -13,9 +13,9 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: 1.23.x
|
||||
go-version: 1.24.x
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
|
|
|
@ -14,12 +14,12 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@4574d27a4764455b42196d70a065bc6853246a25 # v3.4.0
|
||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||
- name: Setup Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca # v3.9.0
|
||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
|
||||
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
|
||||
id: cache
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
|
@ -27,9 +27,9 @@ jobs:
|
|||
restore-keys: |
|
||||
${{ runner.os }}-buildx-ghcache-
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: 1.23.x
|
||||
go-version: 1.24.x
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
|
|
|
@ -42,24 +42,24 @@ jobs:
|
|||
echo "BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')" >> $GITHUB_OUTPUT
|
||||
echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT
|
||||
- name: Setup QEMU
|
||||
uses: docker/setup-qemu-action@4574d27a4764455b42196d70a065bc6853246a25 # v3.4.0
|
||||
uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 # v3.6.0
|
||||
- name: Setup Docker Buildx
|
||||
id: buildx
|
||||
uses: docker/setup-buildx-action@f7ce87c1d6bead3e36075b2ce75da1f6cc28aaca # v3.9.0
|
||||
uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: fluxcdbot
|
||||
password: ${{ secrets.GHCR_TOKEN }}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
username: fluxcdbot
|
||||
password: ${{ secrets.DOCKER_FLUXCD_PASSWORD }}
|
||||
- name: Generate images meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@369eb591f429131d6889c46b94e711f089e6ca96 # v5.6.1
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
with:
|
||||
images: |
|
||||
fluxcd/${{ env.CONTROLLER }}
|
||||
|
@ -68,7 +68,7 @@ jobs:
|
|||
type=raw,value=${{ steps.prep.outputs.VERSION }}
|
||||
- name: Publish images
|
||||
id: build-push
|
||||
uses: docker/build-push-action@ca877d9245402d1537745e0e356eab47c3520991 # v6.13.0
|
||||
uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0
|
||||
with:
|
||||
sbom: true
|
||||
provenance: true
|
||||
|
@ -79,7 +79,7 @@ jobs:
|
|||
platforms: linux/amd64,linux/arm/v7,linux/arm64
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
- uses: sigstore/cosign-installer@c56c2d3e59e4281cc41dea2217323ba5694b171e # v3.8.0
|
||||
- uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb # v3.8.2
|
||||
- name: Sign images
|
||||
env:
|
||||
COSIGN_EXPERIMENTAL: 1
|
||||
|
@ -92,11 +92,11 @@ jobs:
|
|||
mkdir -p config/release
|
||||
kustomize build ./config/crd > ./config/release/${{ env.CONTROLLER }}.crds.yaml
|
||||
kustomize build ./config/manager > ./config/release/${{ env.CONTROLLER }}.deployment.yaml
|
||||
- uses: anchore/sbom-action/download-syft@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 # v0.18.0
|
||||
- uses: anchore/sbom-action/download-syft@e11c554f704a0b820cbf8c51673f6945e0731532 # v0.20.0
|
||||
- name: Create release and SBOM
|
||||
id: run-goreleaser
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0
|
||||
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
|
||||
with:
|
||||
version: latest
|
||||
args: release --clean --skip=validate
|
||||
|
@ -123,7 +123,7 @@ jobs:
|
|||
id-token: write # for creating OIDC tokens for signing.
|
||||
contents: write # for uploading attestations to GitHub releases.
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.1.0
|
||||
with:
|
||||
provenance-name: "provenance.intoto.jsonl"
|
||||
base64-subjects: "${{ needs.release.outputs.hashes }}"
|
||||
|
@ -136,7 +136,7 @@ jobs:
|
|||
id-token: write # for creating OIDC tokens for signing.
|
||||
packages: write # for uploading attestations.
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
|
||||
with:
|
||||
image: ${{ needs.release.outputs.image_url }}
|
||||
digest: ${{ needs.release.outputs.image_digest }}
|
||||
|
@ -151,7 +151,7 @@ jobs:
|
|||
id-token: write # for creating OIDC tokens for signing.
|
||||
packages: write # for uploading attestations.
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.1.0
|
||||
with:
|
||||
image: ghcr.io/${{ needs.release.outputs.image_url }}
|
||||
digest: ${{ needs.release.outputs.image_digest }}
|
||||
|
|
|
@ -20,7 +20,7 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- name: Run FOSSA scan and upload build data
|
||||
uses: fossa-contrib/fossa-action@cdc5065bcdee31a32e47d4585df72d66e8e941c2 # v3.0.0
|
||||
uses: fossa-contrib/fossa-action@3d2ef181b1820d6dcd1972f86a767d18167fa19b # v3.0.1
|
||||
with:
|
||||
# FOSSA Push-Only API Token
|
||||
fossa-api-key: 5ee8bf422db1471e0bcf2bcb289185de
|
||||
|
@ -33,20 +33,20 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
|
||||
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
|
||||
with:
|
||||
go-version: 1.23.x
|
||||
go-version: 1.24.x
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8
|
||||
uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||
with:
|
||||
languages: go
|
||||
# xref: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# xref: https://codeql.github.com/codeql-query-help/go/
|
||||
queries: security-and-quality
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8
|
||||
uses: github/codeql-action/autobuild@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@dd746615b3b9d728a6a37ca2045b68ca76d4841a # v3.28.8
|
||||
uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||
|
|
65
CHANGELOG.md
65
CHANGELOG.md
|
@ -2,6 +2,71 @@
|
|||
|
||||
All notable changes to this project are documented in this file.
|
||||
|
||||
## 1.6.0
|
||||
|
||||
**Release date:** 2025-05-27
|
||||
|
||||
This minor release comes with various bug fixes and improvements.
|
||||
|
||||
### Provider
|
||||
|
||||
The `azureeventhub` provider now supports workload identity both
|
||||
at the controller and object levels. For object level, the
|
||||
`.spec.serviceAccountName` field can be set to the name of a
|
||||
service account in the same namespace that was configured with
|
||||
a Managed Identity.
|
||||
For object level to work, the controller feature gate
|
||||
`ObjectLevelWorkloadIdentity` must be enabled. See a complete guide
|
||||
[here](https://fluxcd.io/flux/integrations/azure/).
|
||||
|
||||
The `github` and `githubdispatch` providers now support authenticating
|
||||
with a GitHub App. See docs
|
||||
[here](https://fluxcd.io/flux/components/notification/providers/#github)
|
||||
and
|
||||
[here](https://fluxcd.io/flux/components/notification/providers/#github-dispatch).
|
||||
|
||||
For commit status providers it is now possible to define a custom
|
||||
status string by defining a CEL expression in the `.spec.commitStatusExpr`
|
||||
field. The variables `event`, `alert` and `provider` are available
|
||||
for the CEL expression. See
|
||||
[docs](https://fluxcd.io/flux/components/notification/providers/#custom-commit-status-messages).
|
||||
|
||||
### General updates
|
||||
|
||||
In addition, the Kubernetes dependencies have been updated to v1.33 and
|
||||
various other controller dependencies have been updated to their latest
|
||||
version. The controller is now built with Go 1.24.
|
||||
|
||||
Fixes:
|
||||
- Fix Slack chat.postMessage error handling
|
||||
[#1086](https://github.com/fluxcd/notification-controller/pull/1086)
|
||||
- Fix pass 'certPool' to Gitea client on creation
|
||||
[#1084](https://github.com/fluxcd/notification-controller/pull/1084)
|
||||
- CrossNamespaceObjectReference: Fix MaxLength validation to kubernetes max size of 253
|
||||
[#1108](https://github.com/fluxcd/notification-controller/pull/1108)
|
||||
- Sanitize proxy error logging
|
||||
[#1093](https://github.com/fluxcd/notification-controller/pull/1093)
|
||||
|
||||
Improvements:
|
||||
- [RFC-0010] Workload Identity support for `azureeventhub` provider
|
||||
[#1106](https://github.com/fluxcd/notification-controller/pull/1106)
|
||||
[#1116](https://github.com/fluxcd/notification-controller/pull/1116)
|
||||
[#1120](https://github.com/fluxcd/notification-controller/pull/1120)
|
||||
[#1109](https://github.com/fluxcd/notification-controller/pull/1109)
|
||||
[#1112](https://github.com/fluxcd/notification-controller/pull/1112)
|
||||
- GitHub App authentication support for `github` and `githubdispatch`
|
||||
[#1058](https://github.com/fluxcd/notification-controller/pull/1058)
|
||||
- Support CEL expressions to construct commit statuses
|
||||
[#1068](https://github.com/fluxcd/notification-controller/pull/1068)
|
||||
- Add proxy support to `gitea` provider
|
||||
[#1087](https://github.com/fluxcd/notification-controller/pull/1087)
|
||||
- Various dependency updates
|
||||
[#1101](https://github.com/fluxcd/notification-controller/pull/1101)
|
||||
[#1119](https://github.com/fluxcd/notification-controller/pull/1119)
|
||||
[#1118](https://github.com/fluxcd/notification-controller/pull/1118)
|
||||
[#1113](https://github.com/fluxcd/notification-controller/pull/1113)
|
||||
[#1104](https://github.com/fluxcd/notification-controller/pull/1104)
|
||||
|
||||
## 1.5.0
|
||||
|
||||
**Release date:** 2025-02-13
|
||||
|
|
|
@ -24,7 +24,7 @@ If any of the above dependencies are not present on your system, the first invoc
|
|||
## How to run the test suite
|
||||
|
||||
Prerequisites:
|
||||
- Go >= 1.23
|
||||
- Go >= 1.24
|
||||
|
||||
You can run the test suite by simply doing:
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
ARG GO_VERSION=1.23
|
||||
ARG GO_VERSION=1.24
|
||||
ARG XX_VERSION=1.6.1
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
|
|
4
Makefile
4
Makefile
|
@ -93,8 +93,8 @@ api-docs: gen-crd-api-reference-docs
|
|||
|
||||
# Run go mod tidy
|
||||
tidy:
|
||||
cd api; rm -f go.sum; go mod tidy -compat=1.23
|
||||
rm -f go.sum; go mod tidy -compat=1.23
|
||||
cd api; rm -f go.sum; go mod tidy -compat=1.24
|
||||
rm -f go.sum; go mod tidy -compat=1.24
|
||||
|
||||
# Run go fmt against code
|
||||
fmt:
|
||||
|
|
27
api/go.mod
27
api/go.mod
|
@ -1,34 +1,35 @@
|
|||
module github.com/fluxcd/notification-controller/api
|
||||
|
||||
go 1.23.0
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/fluxcd/pkg/apis/meta v1.10.0
|
||||
k8s.io/apimachinery v0.32.1
|
||||
sigs.k8s.io/controller-runtime v0.20.1
|
||||
github.com/fluxcd/pkg/apis/meta v1.17.0
|
||||
k8s.io/apimachinery v0.33.2
|
||||
sigs.k8s.io/controller-runtime v0.21.0
|
||||
)
|
||||
|
||||
// Fix CVE-2022-28948
|
||||
replace gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1
|
||||
|
||||
require (
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
|
||||
sigs.k8s.io/yaml v1.5.0 // indirect
|
||||
)
|
||||
|
|
88
api/go.sum
88
api/go.sum
|
@ -1,12 +1,11 @@
|
|||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fluxcd/pkg/apis/meta v1.10.0 h1:rqbAuyl5ug7A5jjRf/rNwBXmNl6tJ9wG2iIsriwnQUk=
|
||||
github.com/fluxcd/pkg/apis/meta v1.10.0/go.mod h1:n7NstXHDaleAUMajcXTVkhz0MYkvEXy1C/eLI/t1xoI=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/fluxcd/pkg/apis/meta v1.17.0 h1:KVMDyJQj1NYCsppsFUkbJGMnKxsqJVpnKBFolHf/q8E=
|
||||
github.com/fluxcd/pkg/apis/meta v1.17.0/go.mod h1:97l3hTwBpJbXBY+wetNbqrUsvES8B1jGioKcBUxmqd8=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
|
@ -14,11 +13,9 @@ github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZ
|
|||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
|
@ -37,27 +34,28 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM=
|
||||
github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4=
|
||||
github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg=
|
||||
github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
|
||||
github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
|
||||
github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
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=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
|
@ -67,26 +65,26 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -98,19 +96,23 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
|||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE=
|
||||
k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0=
|
||||
k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs=
|
||||
k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU=
|
||||
k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM=
|
||||
k8s.io/apimachinery v0.33.2 h1:IHFVhqg59mb8PJWTLi8m1mAoepkUNYmptHsV+Z1m5jY=
|
||||
k8s.io/apimachinery v0.33.2/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/controller-runtime v0.20.1 h1:JbGMAG/X94NeM3xvjenVUaBjy6Ui4Ogd/J5ZtjZnHaE=
|
||||
sigs.k8s.io/controller-runtime v0.20.1/go.mod h1:BrP3w158MwvB3ZbNpaAcIKkHQ7YGpYnzpoSTZ8E14WU=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e h1:KqK5c/ghOm8xkHYhlodbp6i6+r+ChV2vuAuVRdFbLro=
|
||||
k8s.io/utils v0.0.0-20250321185631-1f6e0b77f77e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8=
|
||||
sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
|
||||
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
|
||||
|
|
|
@ -31,13 +31,13 @@ type CrossNamespaceObjectReference struct {
|
|||
// Name of the referent
|
||||
// If multiple resources are targeted `*` may be set.
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=53
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +required
|
||||
Name string `json:"name"`
|
||||
|
||||
// Namespace of the referent
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=53
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +kubebuilder:validation:Optional
|
||||
// +optional
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
|
|
|
@ -55,6 +55,7 @@ const (
|
|||
)
|
||||
|
||||
// ProviderSpec defines the desired state of the Provider.
|
||||
// +kubebuilder:validation:XValidation:rule="self.type == 'github' || self.type == 'gitlab' || self.type == 'gitea' || self.type == 'bitbucketserver' || self.type == 'bitbucket' || self.type == 'azuredevops' || !has(self.commitStatusExpr)", message="spec.commitStatusExpr is only supported for the 'github', 'gitlab', 'gitea', 'bitbucketserver', 'bitbucket', 'azuredevops' provider types"
|
||||
type ProviderSpec struct {
|
||||
// Type specifies which Provider implementation to use.
|
||||
// +kubebuilder:validation:Enum=slack;discord;msteams;rocket;generic;generic-hmac;github;gitlab;gitea;bitbucketserver;bitbucket;azuredevops;googlechat;googlepubsub;webex;sentry;azureeventhub;telegram;lark;matrix;opsgenie;alertmanager;grafana;githubdispatch;pagerduty;datadog;nats
|
||||
|
@ -96,29 +97,57 @@ type ProviderSpec struct {
|
|||
Timeout *metav1.Duration `json:"timeout,omitempty"`
|
||||
|
||||
// Proxy the HTTP/S address of the proxy server.
|
||||
// Deprecated: Use ProxySecretRef instead. Will be removed in v1.
|
||||
// +kubebuilder:validation:Pattern="^(http|https)://.*$"
|
||||
// +kubebuilder:validation:MaxLength:=2048
|
||||
// +kubebuilder:validation:Optional
|
||||
// +optional
|
||||
Proxy string `json:"proxy,omitempty"`
|
||||
|
||||
// ProxySecretRef specifies the Secret containing the proxy configuration
|
||||
// for this Provider. The Secret should contain an 'address' key with the
|
||||
// HTTP/S address of the proxy server. Optional 'username' and 'password'
|
||||
// keys can be provided for proxy authentication.
|
||||
// +optional
|
||||
ProxySecretRef *meta.LocalObjectReference `json:"proxySecretRef,omitempty"`
|
||||
|
||||
// SecretRef specifies the Secret containing the authentication
|
||||
// credentials for this Provider.
|
||||
// +optional
|
||||
SecretRef *meta.LocalObjectReference `json:"secretRef,omitempty"`
|
||||
|
||||
// CertSecretRef specifies the Secret containing
|
||||
// a PEM-encoded CA certificate (in the `ca.crt` key).
|
||||
// ServiceAccountName is the name of the service account used to
|
||||
// authenticate with services from cloud providers. An error is thrown if a
|
||||
// static credential is also defined inside the Secret referenced by the
|
||||
// SecretRef.
|
||||
// +optional
|
||||
ServiceAccountName string `json:"serviceAccountName,omitempty"`
|
||||
|
||||
// CertSecretRef specifies the Secret containing TLS certificates
|
||||
// for secure communication.
|
||||
//
|
||||
// Note: Support for the `caFile` key has
|
||||
// been deprecated.
|
||||
// Supported configurations:
|
||||
// - CA-only: Server authentication (provide ca.crt only)
|
||||
// - mTLS: Mutual authentication (provide ca.crt + tls.crt + tls.key)
|
||||
// - Client-only: Client authentication with system CA (provide tls.crt + tls.key only)
|
||||
//
|
||||
// Legacy keys "caFile", "certFile", "keyFile" are supported but deprecated. Use "ca.crt", "tls.crt", "tls.key" instead.
|
||||
//
|
||||
// +optional
|
||||
CertSecretRef *meta.LocalObjectReference `json:"certSecretRef,omitempty"`
|
||||
|
||||
// Suspend tells the controller to suspend subsequent
|
||||
// events handling for this Provider.
|
||||
// +optional
|
||||
Suspend bool `json:"suspend,omitempty"`
|
||||
|
||||
// CommitStatusExpr is a CEL expression that evaluates to a string value
|
||||
// that can be used to generate a custom commit status message for use
|
||||
// with eligible Provider types (github, gitlab, gitea, bitbucketserver,
|
||||
// bitbucket, azuredevops). Supported variables are: event, provider,
|
||||
// and alert.
|
||||
// +optional
|
||||
CommitStatusExpr string `json:"commitStatusExpr,omitempty"`
|
||||
}
|
||||
|
||||
// +genclient
|
||||
|
|
|
@ -196,6 +196,11 @@ func (in *ProviderSpec) DeepCopyInto(out *ProviderSpec) {
|
|||
*out = new(metav1.Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.ProxySecretRef != nil {
|
||||
in, out := &in.ProxySecretRef, &out.ProxySecretRef
|
||||
*out = new(meta.LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(meta.LocalObjectReference)
|
||||
|
|
|
@ -302,12 +302,12 @@ spec:
|
|||
description: |-
|
||||
Name of the referent
|
||||
If multiple resources are targeted `*` may be set.
|
||||
maxLength: 53
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace of the referent
|
||||
maxLength: 53
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
|
@ -519,12 +519,12 @@ spec:
|
|||
description: |-
|
||||
Name of the referent
|
||||
If multiple resources are targeted `*` may be set.
|
||||
maxLength: 53
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace of the referent
|
||||
maxLength: 53
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
|
|
|
@ -443,11 +443,15 @@ spec:
|
|||
type: string
|
||||
certSecretRef:
|
||||
description: |-
|
||||
CertSecretRef specifies the Secret containing
|
||||
a PEM-encoded CA certificate (in the `ca.crt` key).
|
||||
CertSecretRef specifies the Secret containing TLS certificates
|
||||
for secure communication.
|
||||
|
||||
Note: Support for the `caFile` key has
|
||||
been deprecated.
|
||||
Supported configurations:
|
||||
- CA-only: Server authentication (provide ca.crt only)
|
||||
- mTLS: Mutual authentication (provide ca.crt + tls.crt + tls.key)
|
||||
- Client-only: Client authentication with system CA (provide tls.crt + tls.key only)
|
||||
|
||||
Legacy keys "caFile", "certFile", "keyFile" are supported but deprecated. Use "ca.crt", "tls.crt", "tls.key" instead.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the referent.
|
||||
|
@ -460,6 +464,14 @@ spec:
|
|||
should be posted.
|
||||
maxLength: 2048
|
||||
type: string
|
||||
commitStatusExpr:
|
||||
description: |-
|
||||
CommitStatusExpr is a CEL expression that evaluates to a string value
|
||||
that can be used to generate a custom commit status message for use
|
||||
with eligible Provider types (github, gitlab, gitea, bitbucketserver,
|
||||
bitbucket, azuredevops). Supported variables are: event, provider,
|
||||
and alert.
|
||||
type: string
|
||||
interval:
|
||||
description: |-
|
||||
Interval at which to reconcile the Provider with its Secret references.
|
||||
|
@ -467,10 +479,25 @@ spec:
|
|||
pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$
|
||||
type: string
|
||||
proxy:
|
||||
description: Proxy the HTTP/S address of the proxy server.
|
||||
description: |-
|
||||
Proxy the HTTP/S address of the proxy server.
|
||||
Deprecated: Use ProxySecretRef instead. Will be removed in v1.
|
||||
maxLength: 2048
|
||||
pattern: ^(http|https)://.*$
|
||||
type: string
|
||||
proxySecretRef:
|
||||
description: |-
|
||||
ProxySecretRef specifies the Secret containing the proxy configuration
|
||||
for this Provider. The Secret should contain an 'address' key with the
|
||||
HTTP/S address of the proxy server. Optional 'username' and 'password'
|
||||
keys can be provided for proxy authentication.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the referent.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
type: object
|
||||
secretRef:
|
||||
description: |-
|
||||
SecretRef specifies the Secret containing the authentication
|
||||
|
@ -482,6 +509,13 @@ spec:
|
|||
required:
|
||||
- name
|
||||
type: object
|
||||
serviceAccountName:
|
||||
description: |-
|
||||
ServiceAccountName is the name of the service account used to
|
||||
authenticate with services from cloud providers. An error is thrown if a
|
||||
static credential is also defined inside the Secret referenced by the
|
||||
SecretRef.
|
||||
type: string
|
||||
suspend:
|
||||
description: |-
|
||||
Suspend tells the controller to suspend subsequent
|
||||
|
@ -529,6 +563,12 @@ spec:
|
|||
required:
|
||||
- type
|
||||
type: object
|
||||
x-kubernetes-validations:
|
||||
- message: spec.commitStatusExpr is only supported for the 'github', 'gitlab',
|
||||
'gitea', 'bitbucketserver', 'bitbucket', 'azuredevops' provider types
|
||||
rule: self.type == 'github' || self.type == 'gitlab' || self.type ==
|
||||
'gitea' || self.type == 'bitbucketserver' || self.type == 'bitbucket'
|
||||
|| self.type == 'azuredevops' || !has(self.commitStatusExpr)
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
|
|
|
@ -109,12 +109,12 @@ spec:
|
|||
description: |-
|
||||
Name of the referent
|
||||
If multiple resources are targeted `*` may be set.
|
||||
maxLength: 53
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace of the referent
|
||||
maxLength: 53
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
|
@ -536,12 +536,12 @@ spec:
|
|||
description: |-
|
||||
Name of the referent
|
||||
If multiple resources are targeted `*` may be set.
|
||||
maxLength: 53
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace of the referent
|
||||
maxLength: 53
|
||||
maxLength: 253
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
|
|
|
@ -6,4 +6,4 @@ resources:
|
|||
images:
|
||||
- name: fluxcd/notification-controller
|
||||
newName: fluxcd/notification-controller
|
||||
newTag: v1.5.0
|
||||
newTag: v1.6.0
|
||||
|
|
|
@ -19,6 +19,12 @@ rules:
|
|||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- serviceaccounts/token
|
||||
verbs:
|
||||
- create
|
||||
- apiGroups:
|
||||
- image.fluxcd.io
|
||||
resources:
|
||||
|
|
|
@ -330,7 +330,25 @@ string
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>Proxy the HTTP/S address of the proxy server.</p>
|
||||
<p>Proxy the HTTP/S address of the proxy server.
|
||||
Deprecated: Use ProxySecretRef instead. Will be removed in v1.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>proxySecretRef</code><br>
|
||||
<em>
|
||||
<a href="https://pkg.go.dev/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
|
||||
github.com/fluxcd/pkg/apis/meta.LocalObjectReference
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>ProxySecretRef specifies the Secret containing the proxy configuration
|
||||
for this Provider. The Secret should contain an ‘address’ key with the
|
||||
HTTP/S address of the proxy server. Optional ‘username’ and ‘password’
|
||||
keys can be provided for proxy authentication.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -350,6 +368,21 @@ credentials for this Provider.</p>
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>serviceAccountName</code><br>
|
||||
<em>
|
||||
string
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>ServiceAccountName is the name of the service account used to
|
||||
authenticate with services from cloud providers. An error is thrown if a
|
||||
static credential is also defined inside the Secret referenced by the
|
||||
SecretRef.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>certSecretRef</code><br>
|
||||
<em>
|
||||
<a href="https://pkg.go.dev/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
|
||||
|
@ -359,10 +392,13 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>CertSecretRef specifies the Secret containing
|
||||
a PEM-encoded CA certificate (in the <code>ca.crt</code> key).</p>
|
||||
<p>Note: Support for the <code>caFile</code> key has
|
||||
been deprecated.</p>
|
||||
<p>CertSecretRef specifies the Secret containing TLS certificates
|
||||
for secure communication.</p>
|
||||
<p>Supported configurations:
|
||||
- CA-only: Server authentication (provide ca.crt only)
|
||||
- mTLS: Mutual authentication (provide ca.crt + tls.crt + tls.key)
|
||||
- Client-only: Client authentication with system CA (provide tls.crt + tls.key only)</p>
|
||||
<p>Legacy keys “caFile”, “certFile”, “keyFile” are supported but deprecated. Use “ca.crt”, “tls.crt”, “tls.key” instead.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -378,6 +414,22 @@ bool
|
|||
events handling for this Provider.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>commitStatusExpr</code><br>
|
||||
<em>
|
||||
string
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>CommitStatusExpr is a CEL expression that evaluates to a string value
|
||||
that can be used to generate a custom commit status message for use
|
||||
with eligible Provider types (github, gitlab, gitea, bitbucketserver,
|
||||
bitbucket, azuredevops). Supported variables are: event, provider,
|
||||
and alert.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -619,7 +671,25 @@ string
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>Proxy the HTTP/S address of the proxy server.</p>
|
||||
<p>Proxy the HTTP/S address of the proxy server.
|
||||
Deprecated: Use ProxySecretRef instead. Will be removed in v1.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>proxySecretRef</code><br>
|
||||
<em>
|
||||
<a href="https://pkg.go.dev/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
|
||||
github.com/fluxcd/pkg/apis/meta.LocalObjectReference
|
||||
</a>
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>ProxySecretRef specifies the Secret containing the proxy configuration
|
||||
for this Provider. The Secret should contain an ‘address’ key with the
|
||||
HTTP/S address of the proxy server. Optional ‘username’ and ‘password’
|
||||
keys can be provided for proxy authentication.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -639,6 +709,21 @@ credentials for this Provider.</p>
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>serviceAccountName</code><br>
|
||||
<em>
|
||||
string
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>ServiceAccountName is the name of the service account used to
|
||||
authenticate with services from cloud providers. An error is thrown if a
|
||||
static credential is also defined inside the Secret referenced by the
|
||||
SecretRef.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>certSecretRef</code><br>
|
||||
<em>
|
||||
<a href="https://pkg.go.dev/github.com/fluxcd/pkg/apis/meta#LocalObjectReference">
|
||||
|
@ -648,10 +733,13 @@ github.com/fluxcd/pkg/apis/meta.LocalObjectReference
|
|||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>CertSecretRef specifies the Secret containing
|
||||
a PEM-encoded CA certificate (in the <code>ca.crt</code> key).</p>
|
||||
<p>Note: Support for the <code>caFile</code> key has
|
||||
been deprecated.</p>
|
||||
<p>CertSecretRef specifies the Secret containing TLS certificates
|
||||
for secure communication.</p>
|
||||
<p>Supported configurations:
|
||||
- CA-only: Server authentication (provide ca.crt only)
|
||||
- mTLS: Mutual authentication (provide ca.crt + tls.crt + tls.key)
|
||||
- Client-only: Client authentication with system CA (provide tls.crt + tls.key only)</p>
|
||||
<p>Legacy keys “caFile”, “certFile”, “keyFile” are supported but deprecated. Use “ca.crt”, “tls.crt”, “tls.key” instead.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -667,6 +755,22 @@ bool
|
|||
events handling for this Provider.</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code>commitStatusExpr</code><br>
|
||||
<em>
|
||||
string
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
<em>(Optional)</em>
|
||||
<p>CommitStatusExpr is a CEL expression that evaluates to a string value
|
||||
that can be used to generate a custom commit status message for use
|
||||
with eligible Provider types (github, gitlab, gitea, bitbucketserver,
|
||||
bitbucket, azuredevops). Supported variables are: event, provider,
|
||||
and alert.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
|
|
@ -368,7 +368,8 @@ and use `https://api.telegram.org/` as the api url.
|
|||
--from-literal=address=https://api.telegram.org
|
||||
```
|
||||
|
||||
Also note that `spec.channel` can be a unique identifier for the target chat
|
||||
Also note that `spec.channel` can be a unique identifier for the target chat,
|
||||
a unique identifier with the topic identifier for the forum chat
|
||||
or username of the target channel (in the format @channelusername)
|
||||
|
||||
```yaml
|
||||
|
@ -379,7 +380,7 @@ metadata:
|
|||
namespace: flux-system
|
||||
spec:
|
||||
type: telegram
|
||||
channel: "@fluxtest" # or "-1557265138" (channel id)
|
||||
channel: "@fluxtest" # or "-1557265138" (channel id) or "-1552289257:1" (forum chat id with topic id)
|
||||
secretRef:
|
||||
name: telegram-token
|
||||
```
|
||||
|
|
|
@ -563,8 +563,9 @@ The Event will be formatted into a message string, with the metadata attached
|
|||
as a list of key-value pairs.
|
||||
|
||||
The Provider's [Channel](#channel) is used to set the receiver of the message.
|
||||
This can be a unique identifier (`-1234567890`) for the target chat, or
|
||||
the username (`@username`) of the target channel.
|
||||
This can be a unique identifier (`-1234567890`) for the target chat,
|
||||
a unique identifier with the topic identifier (`-1234567890:1`) for the forum chat,
|
||||
or the username (`@username`) of the target channel.
|
||||
|
||||
This Provider type does not support the configuration of a [proxy URL](#https-proxy)
|
||||
or [TLS certificates](#tls-certificates).
|
||||
|
@ -586,7 +587,7 @@ metadata:
|
|||
spec:
|
||||
type: telegram
|
||||
address: https://api.telegram.org
|
||||
channel: "@fluxcd" # or "-1557265138" (channel id)
|
||||
channel: "@fluxcd" # or "-1557265138" (channel id) or "-1552289257:1" (forum chat id with topic id)
|
||||
secretRef:
|
||||
name: telegram-token
|
||||
```
|
||||
|
|
|
@ -284,7 +284,7 @@ field](https://api.slack.com/methods/chat.postMessage#arg_username) to the
|
|||
payload, defaulting to the name of the reporting controller.
|
||||
|
||||
This Provider type supports the configuration of a [proxy URL](#https-proxy)
|
||||
and/or [TLS certificates](#tls-certificates).
|
||||
and/or [certificate secret reference](#certificate-secret-reference).
|
||||
|
||||
###### Slack example
|
||||
|
||||
|
@ -363,7 +363,7 @@ In both cases the Event metadata is attached as facts, and the involved object a
|
|||
The severity of the Event is used to set the color of the message.
|
||||
|
||||
This Provider type supports the configuration of a [proxy URL](#https-proxy)
|
||||
and/or [TLS certificates](#tls-certificates), but lacks support for
|
||||
and/or [certificate secret reference](#certificate-secret-reference), but lacks support for
|
||||
configuring a [Channel](#channel). This can be configured during the
|
||||
creation of the Incoming Webhook Workflow in Microsoft Teams.
|
||||
|
||||
|
@ -403,7 +403,7 @@ The Event will be formatted into a [DataDog Event](https://docs.datadoghq.com/ap
|
|||
API endpoint of the provided DataDog [Address](#address).
|
||||
|
||||
This Provider type supports the configuration of a [proxy URL](#https-proxy)
|
||||
and/or [TLS certificates](#tls-certificates).
|
||||
and/or [certificate secret reference](#certificate-secret-reference).
|
||||
|
||||
The metadata of the Event is included in the DataDog event as extra tags.
|
||||
|
||||
|
@ -459,7 +459,7 @@ The Event will be formatted into a [Slack message](#slack) and send to the
|
|||
`/slack` endpoint of the provided Discord [Address](#address).
|
||||
|
||||
This Provider type supports the configuration of a [proxy URL](#https-proxy)
|
||||
and/or [TLS certificates](#tls-certificates), but lacks support for
|
||||
and/or [certificate secret reference](#certificate-secret-reference), but lacks support for
|
||||
configuring a [Channel](#channel). This can be configured [during the creation
|
||||
of the address](https://discord.com/developers/docs/resources/webhook#create-webhook)
|
||||
|
||||
|
@ -507,7 +507,7 @@ The Provider's [Channel](#channel) is used to set the `environment` on the
|
|||
Sentry client.
|
||||
|
||||
This Provider type supports the configuration of
|
||||
[TLS certificates](#tls-certificates).
|
||||
[certificate secret reference](#certificate-secret-reference).
|
||||
|
||||
###### Sentry example
|
||||
|
||||
|
@ -544,24 +544,26 @@ stringData:
|
|||
##### Telegram
|
||||
|
||||
When `.spec.type` is set to `telegram`, the controller will send a payload for
|
||||
an [Event](events.md#event-structure) to the provided Telegram [Address](#address).
|
||||
an [Event](events.md#event-structure) to the Telegram Bot API.
|
||||
|
||||
The Event will be formatted into a message string, with the metadata attached
|
||||
as a list of key-value pairs.
|
||||
|
||||
The Provider's [Channel](#channel) is used to set the receiver of the message.
|
||||
This can be a unique identifier (`-1234567890`) for the target chat, or
|
||||
the username (`@username`) of the target channel.
|
||||
This can be a unique identifier (`-1234567890`) for the target chat,
|
||||
a unique identifier with the topic identifier (`-1234567890:1`) for the forum chat,
|
||||
or the username (`@username`) of the target channel.
|
||||
|
||||
This Provider type does not support the configuration of a [proxy URL](#https-proxy)
|
||||
or [TLS certificates](#tls-certificates).
|
||||
This Provider type does not support the configuration of a [certificate secret reference](#certificate-secret-reference).
|
||||
|
||||
**Note:** The Telegram provider always ignores the `address` field and uses the default
|
||||
Telegram Bot API endpoint (`https://api.telegram.org`).
|
||||
|
||||
###### Telegram example
|
||||
|
||||
To configure a Provider for Telegram, create a Secret with [the `token`](#token-example)
|
||||
obtained from [the BotFather](https://core.telegram.org/bots#how-do-i-create-a-bot),
|
||||
and a `telegram` Provider with a [Secret reference](#secret-reference), and the
|
||||
`address` set to `https://api.telegram.org`.
|
||||
and a `telegram` Provider with a [Secret reference](#secret-reference).
|
||||
|
||||
```yaml
|
||||
---
|
||||
|
@ -572,8 +574,7 @@ metadata:
|
|||
namespace: default
|
||||
spec:
|
||||
type: telegram
|
||||
address: https://api.telegram.org
|
||||
channel: "@fluxcd" # or "-1557265138" (channel id)
|
||||
channel: "@fluxcd" # or "-1557265138" (channel id) or "-1552289257:1" (forum chat id with topic id)
|
||||
secretRef:
|
||||
name: telegram-token
|
||||
```
|
||||
|
@ -622,7 +623,7 @@ The Event will be formatted into a [Lark Message card](https://open.larksuite.co
|
|||
with the metadata written to the message string.
|
||||
|
||||
This Provider type does not support the configuration of a [proxy URL](#https-proxy)
|
||||
or [TLS certificates](#tls-certificates).
|
||||
or [certificate secret reference](#certificate-secret-reference).
|
||||
|
||||
###### Lark example
|
||||
|
||||
|
@ -659,7 +660,7 @@ The Event will be formatted into a [Slack message](#slack) and send as a
|
|||
payload the provided Rocket [Address](#address).
|
||||
|
||||
This Provider type does support the configuration of a [proxy URL](#https-proxy)
|
||||
and [TLS certificates](#tls-certificates).
|
||||
and [certificate secret reference](#certificate-secret-reference).
|
||||
|
||||
###### Rocket example
|
||||
|
||||
|
@ -741,7 +742,7 @@ You can optionally add [attributes](https://cloud.google.com/pubsub/docs/samples
|
|||
to the Pub/Sub message using a [`headers` key in the referenced Secret](#http-headers-example).
|
||||
|
||||
This Provider type does not support the configuration of a [proxy URL](#https-proxy)
|
||||
or [TLS certificates](#tls-certificates).
|
||||
or [certificate secret reference](#certificate-secret-reference).
|
||||
|
||||
###### Google Pub/Sub with JSON Credentials and Custom Headers Example
|
||||
|
||||
|
@ -787,7 +788,7 @@ with the metadata added to the [`details` field](https://docs.opsgenie.com/docs/
|
|||
as a list of key-value pairs.
|
||||
|
||||
This Provider type does support the configuration of a [proxy URL](#https-proxy)
|
||||
and [TLS certificates](#tls-certificates).
|
||||
and [certificate secret reference](#certificate-secret-reference).
|
||||
|
||||
###### Opsgenie example
|
||||
|
||||
|
@ -830,7 +831,7 @@ The provider will also send [Change Events](https://developer.pagerduty.com/api-
|
|||
for `info` level `Severity`, which will be displayed in the PagerDuty service's timeline to track changes.
|
||||
|
||||
This Provider type supports the configuration of a [proxy URL](#https-proxy)
|
||||
and [TLS certificates](#tls-certificates).
|
||||
and [certificate secret reference](#certificate-secret-reference).
|
||||
|
||||
The [Channel](#channel) is used to set the routing key to send the event to the appropriate integration.
|
||||
|
||||
|
@ -915,7 +916,7 @@ global:
|
|||
```
|
||||
|
||||
This Provider type does support the configuration of a [proxy URL](#https-proxy)
|
||||
and [TLS certificates](#tls-certificates).
|
||||
and [certificate secret reference](#certificate-secret-reference).
|
||||
|
||||
###### Prometheus Alertmanager example
|
||||
|
||||
|
@ -987,7 +988,7 @@ The [Channel](#channel) is used to set the ID of the room to send the message
|
|||
to.
|
||||
|
||||
This Provider type does support the configuration of a [proxy URL](#https-proxy)
|
||||
and [TLS certificates](#tls-certificates).
|
||||
and [certificate secret reference](#certificate-secret-reference).
|
||||
|
||||
###### Webex example
|
||||
|
||||
|
@ -1096,7 +1097,7 @@ credentials for the provider API.
|
|||
The Kubernetes secret can have any of the following keys:
|
||||
|
||||
- `address` - overrides `.spec.address`
|
||||
- `proxy` - overrides `.spec.proxy`
|
||||
- `proxy` - overrides `.spec.proxy` (deprecated, use `.spec.proxySecretRef` instead. **Support for this key will be removed in v1**)
|
||||
- `token` - used for authentication
|
||||
- `username` - overrides `.spec.username`
|
||||
- `headers` - HTTP headers values included in the POST request
|
||||
|
@ -1154,7 +1155,7 @@ stringData:
|
|||
#### Proxy auth example
|
||||
|
||||
Some networks need to use an authenticated proxy to access external services.
|
||||
Therefore, the proxy address can be stored as a secret to hide parameters like the username and password:
|
||||
The recommended approach is to use `.spec.proxySecretRef` with a dedicated Secret:
|
||||
|
||||
```yaml
|
||||
---
|
||||
|
@ -1163,15 +1164,56 @@ kind: Secret
|
|||
metadata:
|
||||
name: my-provider-proxy
|
||||
namespace: default
|
||||
stringData:
|
||||
address: "http://proxy_url:proxy_port"
|
||||
username: "proxy_username"
|
||||
password: "proxy_password"
|
||||
```
|
||||
|
||||
**Legacy approach (deprecated):**
|
||||
The proxy address can also be stored in the main secret to hide parameters like the username and password:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: my-provider-proxy-legacy
|
||||
namespace: default
|
||||
stringData:
|
||||
proxy: "http://username:password@proxy_url:proxy_port"
|
||||
```
|
||||
|
||||
### TLS certificates
|
||||
### Certificate secret reference
|
||||
|
||||
`.spec.certSecretRef` is an optional field to specify a name reference to a
|
||||
Secret in the same namespace as the Provider, containing the TLS CA certificate.
|
||||
The secret must be of type `kubernetes.io/tls` or `Opaque`.
|
||||
Secret in the same namespace as the Provider, containing TLS certificates for
|
||||
secure communication. The secret must be of type `kubernetes.io/tls` or `Opaque`.
|
||||
|
||||
#### Supported configurations
|
||||
|
||||
- **CA-only**: Server authentication (provide `ca.crt` only)
|
||||
- **mTLS**: Client certificate authentication (provide `tls.crt` + `tls.key`, optionally with `ca.crt`)
|
||||
|
||||
#### Providers supporting client certificate authentication
|
||||
|
||||
The following webhook-based providers support client certificate authentication:
|
||||
|
||||
| Provider Type | Description |
|
||||
|---------------------|--------------------------------|
|
||||
| `alertmanager` | Prometheus Alertmanager |
|
||||
| `discord` | Discord webhooks |
|
||||
| `forwarder` | Generic forwarder |
|
||||
| `grafana` | Grafana annotations API |
|
||||
| `matrix` | Matrix rooms |
|
||||
| `msteams` | Microsoft Teams |
|
||||
| `opsgenie` | Opsgenie alerts |
|
||||
| `pagerduty` | PagerDuty events |
|
||||
| `rocket` | Rocket.Chat |
|
||||
| `slack` | Slack API |
|
||||
| `webex` | Webex messages |
|
||||
|
||||
Support for client certificate authentication is being expanded to additional providers over time.
|
||||
|
||||
#### Example
|
||||
|
||||
|
@ -1209,10 +1251,17 @@ the controller will log a deprecation warning.
|
|||
### HTTP/S proxy
|
||||
|
||||
`.spec.proxy` is an optional field to specify an HTTP/S proxy address.
|
||||
**Warning:** This field is deprecated, use `.spec.proxySecretRef` instead. **Support for this field will be removed in v1.**
|
||||
|
||||
`.spec.proxySecretRef` is an optional field to specify a name reference to a
|
||||
Secret in the same namespace as the Provider, containing the proxy configuration.
|
||||
The Secret should contain an `address` key with the HTTP/S address of the proxy server.
|
||||
Optional `username` and `password` keys can be provided for proxy authentication.
|
||||
|
||||
If the proxy address contains sensitive information such as basic auth credentials, it is
|
||||
recommended to store the proxy in the Kubernetes secret referenced by `.spec.secretRef.name`.
|
||||
When the referenced Secret contains a `proxy` key, the `.spec.proxy` value is ignored.
|
||||
recommended to use `.spec.proxySecretRef` instead of `.spec.proxy`.
|
||||
When `.spec.proxySecretRef` is specified, both `.spec.proxy` and the `proxy` key from
|
||||
`.spec.secretRef` are ignored.
|
||||
|
||||
### Timeout
|
||||
|
||||
|
@ -1299,14 +1348,16 @@ spec:
|
|||
type: githubdispatch
|
||||
address: https://github.com/stefanprodan/podinfo
|
||||
secretRef:
|
||||
name: api-token
|
||||
name: auth-secret
|
||||
```
|
||||
|
||||
The `address` is the address of your repository where you want to send webhooks to trigger GitHub workflows.
|
||||
|
||||
GitHub uses personal access tokens for authentication with its API:
|
||||
GitHub uses [personal access tokens](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token)
|
||||
or [GitHub app](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation)
|
||||
for authentication with its API:
|
||||
|
||||
* [GitHub personal access token](https://docs.github.com/en/free-pro-team@latest/github/authenticating-to-github/creating-a-personal-access-token)
|
||||
#### GitHub personal access token
|
||||
|
||||
The provider requires a secret in the same format, with the personal access token as the value for the token key:
|
||||
|
||||
|
@ -1315,12 +1366,19 @@ The provider requires a secret in the same format, with the personal access toke
|
|||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: api-token
|
||||
name: auth-secret
|
||||
namespace: default
|
||||
stringData:
|
||||
token: <personal-access-tokens>
|
||||
```
|
||||
|
||||
#### GitHub App
|
||||
|
||||
To use Github App authentication, make sure the GitHub App is registered and
|
||||
installed with the necessary permissions and the github app secret is created as
|
||||
described
|
||||
[here](https://fluxcd.io/flux/components/source/gitrepositories/#github).
|
||||
|
||||
#### Setting up a GitHub workflow
|
||||
|
||||
To trigger a GitHub Actions workflow when a Flux Kustomization finishes reconciling,
|
||||
|
@ -1402,13 +1460,62 @@ jobs:
|
|||
|
||||
### Azure Event Hub
|
||||
|
||||
The Azure Event Hub supports two authentication methods, [JWT](https://docs.microsoft.com/en-us/azure/event-hubs/authenticate-application)
|
||||
and [SAS](https://docs.microsoft.com/en-us/azure/event-hubs/authorize-access-shared-access-signature) based.
|
||||
The Azure Event Hub provider supports the following authentication methods,
|
||||
- [Managed
|
||||
Identity](https://learn.microsoft.com/en-us/azure/event-hubs/authenticate-managed-identity)
|
||||
- [JWT](https://docs.microsoft.com/en-us/azure/event-hubs/authenticate-application)
|
||||
- [SAS](https://docs.microsoft.com/en-us/azure/event-hubs/authorize-access-shared-access-signature)
|
||||
based.
|
||||
|
||||
#### Managed Identity
|
||||
|
||||
Managed identity authentication can be setup using Azure Workload identity.
|
||||
|
||||
##### Pre-requisites
|
||||
|
||||
- Ensure Workload Identity is properly [set up on your
|
||||
cluster](https://learn.microsoft.com/en-us/azure/aks/workload-identity-deploy-cluster#create-an-aks-cluster).
|
||||
|
||||
##### Configure workload identity
|
||||
|
||||
- Create a managed identity to access Azure Event Hub.
|
||||
- Grant the managed identity the necessary permissions to send events to Azure
|
||||
Event hub as described
|
||||
[here](https://learn.microsoft.com/en-us/azure/event-hubs/authenticate-managed-identity#to-assign-azure-roles-using-the-azure-portal).
|
||||
|
||||
- Establish a federated identity credential between the managed identity and the
|
||||
service account to be used for authentication. Ensure the federated credential
|
||||
uses the correct namespace and name of the service account. For more details,
|
||||
please refer to this
|
||||
[guide](https://azure.github.io/azure-workload-identity/docs/quick-start.html#6-establish-federated-identity-credential-between-the-identity-and-the-service-account-issuer--subject).
|
||||
|
||||
##### Single tenant approach
|
||||
|
||||
This approach uses the notification-controller service account for setting up
|
||||
authentication.
|
||||
|
||||
- In the default installation, the notification-controller service account is
|
||||
located in the `flux-system` namespace with name `notification-controller`.
|
||||
|
||||
- Configure workload identity with notification-controller as described in the
|
||||
docs [here](/flux/installation/configuration/workload-identity/).
|
||||
|
||||
##### Multi-tenant approach
|
||||
|
||||
For multi-tenant clusters, set `.spec.serviceAccountName` of the provider to
|
||||
the service account to be used for authentication. Ensure that the service
|
||||
account has the
|
||||
[annotations](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview?tabs=dotnet#service-account-annotations)
|
||||
for the client-id and tenant-id of the managed identity.
|
||||
|
||||
For a complete guide on how to set up authentication for an Azure Event Hub,
|
||||
see the integration [docs](/flux/integrations/azure/).
|
||||
|
||||
#### JWT based auth
|
||||
|
||||
In JWT we use 3 input values. Channel, token and address.
|
||||
We perform the following translation to match we the data we need to communicate with Azure Event Hub.
|
||||
In JWT we use 3 input values. Channel, token and address. We perform the
|
||||
following translation to match we the data we need to communicate with Azure
|
||||
Event Hub.
|
||||
|
||||
- channel = Azure Event Hub namespace
|
||||
- address = Azure Event Hub name
|
||||
|
@ -1438,11 +1545,13 @@ stringData:
|
|||
```
|
||||
|
||||
The controller doesn't take any responsibility for the JWT token to be updated.
|
||||
You need to use a secondary tool to make sure that the token in the secret is renewed.
|
||||
You need to use a secondary tool to make sure that the token in the secret is
|
||||
renewed.
|
||||
|
||||
If you want to make a easy test assuming that you have setup a Azure Enterprise application and you called it
|
||||
event-hub you can follow most of the bellow commands. You will need to provide the `client_secret` that you got
|
||||
when generating the Azure Enterprise Application.
|
||||
If you want to make a easy test assuming that you have setup a Azure Enterprise
|
||||
application and you called it event-hub you can follow most of the bellow
|
||||
commands. You will need to provide the `client_secret` that you got when
|
||||
generating the Azure Enterprise Application.
|
||||
|
||||
```shell
|
||||
export AZURE_CLIENT=$(az ad app list --filter "startswith(displayName,'event-hub')" --query '[].appId' |jq -r '.[0]')
|
||||
|
@ -1485,8 +1594,8 @@ stringData:
|
|||
```
|
||||
|
||||
Assuming that you have created the Azure event hub and namespace you should be
|
||||
able to use a similar command to get your connection string. This will give
|
||||
you the default Root SAS, which is NOT supposed to be used in production.
|
||||
able to use a similar command to get your connection string. This will give you
|
||||
the default Root SAS, which is NOT supposed to be used in production.
|
||||
|
||||
```shell
|
||||
az eventhubs namespace authorization-rule keys list --resource-group <rg-name> --namespace-name <namespace-name> --name RootManageSharedAccessKey -o tsv --query primaryConnectionString
|
||||
|
@ -1538,10 +1647,16 @@ spec:
|
|||
|
||||
#### GitHub
|
||||
|
||||
When `.spec.type` is set to `github`, the referenced secret must contain a key called `token` with the value set to a
|
||||
[GitHub personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token).
|
||||
When `.spec.type` is set to `github`, the referenced secret can contain a
|
||||
personal access token OR github app details.
|
||||
|
||||
The token must have permissions to update the commit status for the GitHub repository specified in `.spec.address`.
|
||||
##### Personal Access Token
|
||||
|
||||
To use personal access tokens, the secret must contain a key called `token` with
|
||||
the value set to a [GitHub personal access
|
||||
token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token).
|
||||
The token must have permissions to update the commit status for the GitHub
|
||||
repository specified in `.spec.address`.
|
||||
|
||||
You can create the secret with `kubectl` like this:
|
||||
|
||||
|
@ -1549,6 +1664,15 @@ You can create the secret with `kubectl` like this:
|
|||
kubectl create secret generic github-token --from-literal=token=<GITHUB-TOKEN>
|
||||
```
|
||||
|
||||
##### GitHub App
|
||||
|
||||
To use [Github App
|
||||
authentication](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/authenticating-as-a-github-app-installation),
|
||||
make sure the GitHub App is registered and installed with the necessary
|
||||
permissions to update the commit status and the github app secret is created as
|
||||
described
|
||||
[here](https://fluxcd.io/flux/components/source/gitrepositories/#github).
|
||||
|
||||
#### GitLab
|
||||
|
||||
When `.spec.type` is set to `gitlab`, the referenced secret must contain a key called `token` with the value set to a
|
||||
|
@ -1639,3 +1763,30 @@ You can create the secret with `kubectl` like this:
|
|||
```shell
|
||||
kubectl create secret generic azuredevops-token --from-literal=token=<AZURE-TOKEN>
|
||||
```
|
||||
|
||||
#### Custom Commit Status Messages
|
||||
|
||||
Git providers supporting commit status updates can use a CEL expression to build a custom commit status message by setting the optional field `spec.commitStatusExpr`:
|
||||
|
||||
```yaml
|
||||
apiVersion: notification.toolkit.fluxcd.io/v1beta3
|
||||
kind: Provider
|
||||
metadata:
|
||||
name: github-status
|
||||
namespace: flux-system
|
||||
spec:
|
||||
type: github
|
||||
address: https://github.com/my-gh-org/my-gh-repo
|
||||
secretRef:
|
||||
name: github-token
|
||||
commitStatusExpr: "(event.involvedObject.kind + '/' + event.involvedObject.name + '/' + event.metadata.foo + '/' + provider.metadata.uid.split('-').first().value()).lowerAscii()"
|
||||
```
|
||||
|
||||
The CEL expression can access the following variables:
|
||||
- `event`: The Flux event object containing metadata about the reconciliation
|
||||
- `provider`: The Provider object
|
||||
- `alert`: The Alert object
|
||||
|
||||
If the `spec.commitStatusExpr` field is not specified, the notification-controller will use a default commit status message based on the involved object kind, name, and a truncated provider UID to generate a commit status (e.g. `kustomization/gitops-system/0c9c2e41`).
|
||||
|
||||
A useful tool for building and testing CEL expressions is the [CEL Playground](https://playcel.undistro.io/).
|
||||
|
|
220
go.mod
220
go.mod
|
@ -1,146 +1,151 @@
|
|||
module github.com/fluxcd/notification-controller
|
||||
|
||||
go 1.23.0
|
||||
go 1.24.0
|
||||
|
||||
replace github.com/fluxcd/notification-controller/api => ./api
|
||||
|
||||
require (
|
||||
cloud.google.com/go/pubsub v1.47.0
|
||||
code.gitea.io/sdk/gitea v0.20.0
|
||||
cloud.google.com/go/pubsub v1.49.0
|
||||
code.gitea.io/sdk/gitea v0.21.0
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6
|
||||
github.com/Azure/azure-amqp-common-go/v4 v4.2.0
|
||||
github.com/Azure/azure-event-hubs-go/v3 v3.6.2
|
||||
github.com/DataDog/datadog-api-client-go/v2 v2.35.0
|
||||
github.com/DataDog/datadog-api-client-go/v2 v2.42.0
|
||||
github.com/PagerDuty/go-pagerduty v1.8.0
|
||||
github.com/cdevents/sdk-go v0.4.1
|
||||
github.com/chainguard-dev/git-urls v1.0.2
|
||||
github.com/containrrr/shoutrrr v0.8.0
|
||||
github.com/fluxcd/cli-utils v0.36.0-flux.12
|
||||
github.com/fluxcd/notification-controller/api v1.5.0
|
||||
github.com/fluxcd/pkg/apis/event v0.16.0
|
||||
github.com/fluxcd/pkg/apis/meta v1.10.0
|
||||
github.com/fluxcd/pkg/git v0.24.0
|
||||
github.com/fluxcd/pkg/masktoken v0.6.0
|
||||
github.com/fluxcd/pkg/runtime v0.53.1
|
||||
github.com/fluxcd/pkg/ssa v0.45.1
|
||||
github.com/getsentry/sentry-go v0.31.1
|
||||
github.com/go-logr/logr v1.4.2
|
||||
github.com/google/cel-go v0.23.2
|
||||
github.com/elazarl/goproxy v1.7.2
|
||||
github.com/fluxcd/cli-utils v0.36.0-flux.14
|
||||
github.com/fluxcd/notification-controller/api v1.6.0
|
||||
github.com/fluxcd/pkg/apis/event v0.18.0
|
||||
github.com/fluxcd/pkg/apis/meta v1.17.0
|
||||
github.com/fluxcd/pkg/auth v0.21.0
|
||||
github.com/fluxcd/pkg/cache v0.10.0
|
||||
github.com/fluxcd/pkg/git v0.34.0
|
||||
github.com/fluxcd/pkg/masktoken v0.7.0
|
||||
github.com/fluxcd/pkg/runtime v0.69.0
|
||||
github.com/fluxcd/pkg/ssa v0.51.0
|
||||
github.com/fluxcd/pkg/ssh v0.20.0
|
||||
github.com/getsentry/sentry-go v0.34.1
|
||||
github.com/go-logr/logr v1.4.3
|
||||
github.com/google/cel-go v0.25.0
|
||||
github.com/google/go-github/v64 v64.0.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7
|
||||
github.com/ktrysmt/go-bitbucket v0.9.81
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8
|
||||
github.com/ktrysmt/go-bitbucket v0.9.86
|
||||
github.com/microsoft/azure-devops-go-api/azuredevops/v6 v6.0.1
|
||||
github.com/nats-io/nats.go v1.39.0
|
||||
github.com/onsi/gomega v1.36.2
|
||||
github.com/nats-io/nats.go v1.43.0
|
||||
github.com/onsi/gomega v1.37.0
|
||||
github.com/sethvargo/go-limiter v1.0.0
|
||||
github.com/slok/go-http-metrics v0.13.0
|
||||
github.com/spf13/pflag v1.0.6
|
||||
github.com/stretchr/testify v1.10.0
|
||||
gitlab.com/gitlab-org/api/client-go v0.122.0
|
||||
golang.org/x/oauth2 v0.26.0
|
||||
golang.org/x/text v0.22.0
|
||||
google.golang.org/api v0.221.0
|
||||
k8s.io/api v0.32.1
|
||||
k8s.io/apimachinery v0.32.1
|
||||
k8s.io/client-go v0.32.1
|
||||
k8s.io/utils v0.0.0-20241210054802-24370beab758
|
||||
sigs.k8s.io/controller-runtime v0.20.1
|
||||
sigs.k8s.io/yaml v1.4.0
|
||||
gitlab.com/gitlab-org/api/client-go v0.134.0
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/text v0.27.0
|
||||
google.golang.org/api v0.241.0
|
||||
k8s.io/api v0.33.2
|
||||
k8s.io/apimachinery v0.33.2
|
||||
k8s.io/client-go v0.33.2
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
|
||||
sigs.k8s.io/controller-runtime v0.21.0
|
||||
sigs.k8s.io/yaml v1.5.0
|
||||
)
|
||||
|
||||
// Fix CVE-2022-28948
|
||||
replace gopkg.in/yaml.v3 => gopkg.in/yaml.v3 v3.0.1
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.19.1 // indirect
|
||||
cloud.google.com/go v0.118.1 // indirect
|
||||
cloud.google.com/go/auth v0.14.1 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
cloud.google.com/go/iam v1.3.1 // indirect
|
||||
github.com/42wim/httpsig v1.2.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
|
||||
github.com/Azure/go-amqp v1.0.0 // indirect
|
||||
cel.dev/expr v0.23.1 // indirect
|
||||
cloud.google.com/go v0.120.0 // indirect
|
||||
cloud.google.com/go/auth v0.16.2 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.7.0 // indirect
|
||||
cloud.google.com/go/iam v1.5.2 // indirect
|
||||
github.com/42wim/httpsig v1.2.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go v65.0.0+incompatible // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.3 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/containerservice/armcontainerservice v1.0.0 // indirect
|
||||
github.com/Azure/go-amqp v1.3.0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.29 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.28 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.21 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
|
||||
github.com/DataDog/zstd v1.5.2 // indirect
|
||||
github.com/MakeNowJust/heredoc v1.0.0 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.5 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.3.0 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.13.0 // indirect
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.16.0 // indirect
|
||||
github.com/carapace-sh/carapace-shlex v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/chai2010/gettext-go v1.0.2 // indirect
|
||||
github.com/cloudevents/sdk-go/v2 v2.15.2 // indirect
|
||||
github.com/cloudflare/circl v1.5.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/devigned/tab v0.1.1 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
|
||||
github.com/docker/cli v28.2.2+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.3 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
|
||||
github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fluxcd/pkg/apis/acl v0.6.0 // indirect
|
||||
github.com/fluxcd/pkg/apis/kustomize v1.9.0 // indirect
|
||||
github.com/fluxcd/pkg/auth v0.3.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/fluxcd/pkg/apis/acl v0.7.0 // indirect
|
||||
github.com/fluxcd/pkg/apis/kustomize v1.11.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.5 // indirect
|
||||
github.com/go-errors/errors v1.5.1 // indirect
|
||||
github.com/go-fed/httpsig v1.1.0 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-logr/zapr v1.3.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.1 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.22.0 // indirect
|
||||
github.com/goccy/go-json v0.10.3 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/gnostic-models v0.6.9 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/go-github/v68 v68.0.0 // indirect
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/go-containerregistry v0.20.6 // indirect
|
||||
github.com/google/go-github/v72 v72.0.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.2 // indirect
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/jpillora/backoff v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/spdystream v0.5.0 // indirect
|
||||
|
@ -150,57 +155,64 @@ require (
|
|||
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||
github.com/nats-io/nkeys v0.4.9 // indirect
|
||||
github.com/nats-io/nkeys v0.4.11 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/package-url/packageurl-go v0.1.1 // indirect
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.20.5 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.65.0 // indirect
|
||||
github.com/prometheus/procfs v0.17.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
|
||||
github.com/spf13/cobra v1.8.1 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/spf13/cobra v1.9.1 // indirect
|
||||
github.com/stoewer/go-strcase v1.3.0 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xlab/treeprint v1.2.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
|
||||
go.opentelemetry.io/otel v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.34.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/crypto v0.33.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/net v0.35.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/term v0.29.0 // indirect
|
||||
golang.org/x/time v0.10.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect
|
||||
google.golang.org/grpc v1.70.0 // indirect
|
||||
google.golang.org/protobuf v1.36.5 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/term v0.33.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.32.1 // indirect
|
||||
k8s.io/cli-runtime v0.32.1 // indirect
|
||||
k8s.io/component-base v0.32.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.33.2 // indirect
|
||||
k8s.io/cli-runtime v0.33.2 // indirect
|
||||
k8s.io/component-base v0.33.2 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
|
||||
k8s.io/kubectl v0.32.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250701173324-9bd5c66d9911 // indirect
|
||||
k8s.io/kubectl v0.33.2 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.19.0 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
|
||||
sigs.k8s.io/kustomize/api v0.20.0 // indirect
|
||||
sigs.k8s.io/kustomize/kyaml v0.20.0 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
|
||||
)
|
||||
|
|
540
go.sum
540
go.sum
|
@ -1,54 +1,57 @@
|
|||
cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
|
||||
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
|
||||
cel.dev/expr v0.23.1 h1:K4KOtPCJQjVggkARsjG9RWXP6O4R73aHeJMa/dmCQQg=
|
||||
cel.dev/expr v0.23.1/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.118.1 h1:b8RATMcrK9A4BH0rj8yQupPXp+aP+cJ0l6H7V9osV1E=
|
||||
cloud.google.com/go v0.118.1/go.mod h1:CFO4UPEPi8oV21xoezZCrd3d81K4fFkDTEJu4R8K+9M=
|
||||
cloud.google.com/go/auth v0.14.1 h1:AwoJbzUdxA/whv1qj3TLKwh3XX5sikny2fc40wUl+h0=
|
||||
cloud.google.com/go/auth v0.14.1/go.mod h1:4JHUxlGXisL0AW8kXPtUF6ztuOksyfUQNFjfsOCXkPM=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
|
||||
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
|
||||
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||
cloud.google.com/go/iam v1.3.1 h1:KFf8SaT71yYq+sQtRISn90Gyhyf4X8RGgeAVC8XGf3E=
|
||||
cloud.google.com/go/iam v1.3.1/go.mod h1:3wMtuyT4NcbnYNPLMBzYRFiEfjKfJlLVLrisE7bwm34=
|
||||
cloud.google.com/go/kms v1.20.5 h1:aQQ8esAIVZ1atdJRxihhdxGQ64/zEbJoJnCz/ydSmKg=
|
||||
cloud.google.com/go/kms v1.20.5/go.mod h1:C5A8M1sv2YWYy1AE6iSrnddSG9lRGdJq5XEdBy28Lmw=
|
||||
cloud.google.com/go/longrunning v0.6.4 h1:3tyw9rO3E2XVXzSApn1gyEEnH2K9SynNQjMlBi3uHLg=
|
||||
cloud.google.com/go/longrunning v0.6.4/go.mod h1:ttZpLCe6e7EXvn9OxpBRx7kZEB0efv8yBO6YnVMfhJs=
|
||||
cloud.google.com/go/pubsub v1.47.0 h1:Ou2Qu4INnf7ykrFjGv2ntFOjVo8Nloh/+OffF4mUu9w=
|
||||
cloud.google.com/go/pubsub v1.47.0/go.mod h1:LaENesmga+2u0nDtLkIOILskxsfvn/BXX9Ak1NFxOs8=
|
||||
code.gitea.io/sdk/gitea v0.20.0 h1:Zm/QDwwZK1awoM4AxdjeAQbxolzx2rIP8dDfmKu+KoU=
|
||||
code.gitea.io/sdk/gitea v0.20.0/go.mod h1:faouBHC/zyx5wLgjmRKR62ydyvMzwWf3QnU0bH7Cw6U=
|
||||
github.com/42wim/httpsig v1.2.1 h1:oLBxptMe9U4ZmSGtkosT8Dlfg31P3VQnAGq6psXv82Y=
|
||||
github.com/42wim/httpsig v1.2.1/go.mod h1:P/UYo7ytNBFwc+dg35IubuAUIs8zj5zzFIgUCEl55WY=
|
||||
cloud.google.com/go v0.120.0 h1:wc6bgG9DHyKqF5/vQvX1CiZrtHnxJjBlKUyF9nP6meA=
|
||||
cloud.google.com/go v0.120.0/go.mod h1:/beW32s8/pGRuj4IILWQNd4uuebeT4dkOhKmkfit64Q=
|
||||
cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4=
|
||||
cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA=
|
||||
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.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU=
|
||||
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo=
|
||||
cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8=
|
||||
cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE=
|
||||
cloud.google.com/go/kms v1.21.2 h1:c/PRUSMNQ8zXrc1sdAUnsenWWaNXN+PzTXfXOcSFdoE=
|
||||
cloud.google.com/go/kms v1.21.2/go.mod h1:8wkMtHV/9Z8mLXEXr1GK7xPSBdi6knuLXIhqjuWcI6w=
|
||||
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
|
||||
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
|
||||
cloud.google.com/go/pubsub v1.49.0 h1:5054IkbslnrMCgA2MAEPcsN3Ky+AyMpEZcii/DoySPo=
|
||||
cloud.google.com/go/pubsub v1.49.0/go.mod h1:K1FswTWP+C1tI/nfi3HQecoVeFvL4HUOB1tdaNXKhUY=
|
||||
code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4=
|
||||
code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA=
|
||||
github.com/42wim/httpsig v1.2.2 h1:ofAYoHUNs/MJOLqQ8hIxeyz2QxOz8qdSVvp3PX/oPgA=
|
||||
github.com/42wim/httpsig v1.2.2/go.mod h1:P/UYo7ytNBFwc+dg35IubuAUIs8zj5zzFIgUCEl55WY=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
|
||||
github.com/Azure/azure-amqp-common-go/v4 v4.2.0 h1:q/jLx1KJ8xeI8XGfkOWMN9XrXzAfVTkyvCxPvHCjd2I=
|
||||
github.com/Azure/azure-amqp-common-go/v4 v4.2.0/go.mod h1:GD3m/WPPma+621UaU6KNjKEo5Hl09z86viKwQjTpV0Q=
|
||||
github.com/Azure/azure-event-hubs-go/v3 v3.6.2 h1:7rNj1/iqS/i3mUKokA2n2eMYO72TB7lO7OmpbKoakKY=
|
||||
github.com/Azure/azure-event-hubs-go/v3 v3.6.2/go.mod h1:n+ocYr9j2JCLYqUqz9eI+lx/TEAtL/g6rZzyTFSuIpc=
|
||||
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.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 h1:1mvYtZfWQAnwNah/C+Z+Jb9rQH95LPE2vlmMuWAHJk8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1/go.mod h1:75I/mXtme1JyWFtz8GocPHVFyH421IBoZErnO16dd0k=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1 h1:Bk5uOhSAenHyR5P61D/NzeQCv+4fEVV8mOkJ82NqpWw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1/go.mod h1:QZ4pw3or1WPmRBxf0cHd1tknzrT54WPBOQoGutCPvSU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
|
||||
github.com/Azure/go-amqp v1.0.0 h1:QfCugi1M+4F2JDTRgVnRw7PYXLXZ9hmqk3+9+oJh3OA=
|
||||
github.com/Azure/go-amqp v1.0.0/go.mod h1:+bg0x3ce5+Q3ahCEXnCsGG3ETpDQe3MEVnOuT2ywPwc=
|
||||
github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw=
|
||||
github.com/Azure/azure-sdk-for-go v65.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 h1:OVoM452qUFBrX+URdH3VpR299ma4kfom0yB0URYky9g=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0/go.mod h1:kUjrAo8bgEwLeZ/CmHqNl3Z/kPm7y6FKfxxK0izYUg4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/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.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/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/go-amqp v1.3.0 h1://1rikYhoIQNXJFXyoO/Rlb4+4EkHYfJceNtLlys2/4=
|
||||
github.com/Azure/go-amqp v1.3.0/go.mod h1:vZAogwdrkbyK3Mla8m/CxSc/aKdnTZ4IbPxl51Y5WZE=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
|
||||
github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw=
|
||||
github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c=
|
||||
github.com/Azure/go-autorest/autorest v0.11.28 h1:ndAExarwr5Y+GaHE6VCaY1kyS/HwwGGyuimVhWsHOEM=
|
||||
github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.21 h1:jjQnVFXPfekaqb8vIsv2G1lxshoW+oGv4MDlhRtnYZk=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.21/go.mod h1:zua7mBUaCc5YnSLKYgGJR/w5ePdMDA6H56upLsHzA9U=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.4.2 h1:iM6UAvjR97ZIeR93qTcwpKNMpV+/FTWjwEbuPD495Tk=
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.4.2/go.mod h1:90gmfKdlmKgfjUpnCEpOJzsUEjrWDSLwHIG73tSXddM=
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.3.1 h1:LXl088ZQlP0SBppGFsRZonW6hSvwgL5gRByMbvUbx8U=
|
||||
|
@ -68,19 +71,19 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM
|
|||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
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.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/DataDog/datadog-api-client-go/v2 v2.35.0 h1:Fj0C0HH5nAolFVdagLOBYMqaYPQ7iy7hLEmS/6gJ9QE=
|
||||
github.com/DataDog/datadog-api-client-go/v2 v2.35.0/go.mod h1:d3tOEgUd2kfsr9uuHQdY+nXrWp4uikgTgVCPdKNK30U=
|
||||
github.com/DataDog/datadog-api-client-go/v2 v2.42.0 h1:0L03LqChbOf7IaaiBUZpXi1Ss6PseGGhQEQrNqD9nU8=
|
||||
github.com/DataDog/datadog-api-client-go/v2 v2.42.0/go.mod h1:d3tOEgUd2kfsr9uuHQdY+nXrWp4uikgTgVCPdKNK30U=
|
||||
github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8=
|
||||
github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
|
||||
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
|
||||
github.com/PagerDuty/go-pagerduty v1.8.0 h1:MTFqTffIcAervB83U7Bx6HERzLbyaSPL/+oxH3zyluI=
|
||||
github.com/PagerDuty/go-pagerduty v1.8.0/go.mod h1:nzIeAqyFSJAFkjWKvMzug0JtwDg+V+UoCWjFrfFH5mI=
|
||||
github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
|
||||
github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
|
||||
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
|
||||
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
|
@ -89,8 +92,10 @@ 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/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.13.0 h1:5FhjW93/YLQJDmPdeyMPw7IjAPzqsr+0jHPfrPz0sZI=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.13.0/go.mod h1:EJ6fgedVEHa2kUyBTTvslJCXJafS/mhJNNKEOCspZXQ=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.16.0 h1:B91r9bHtXp/+XRgS5aZm6ZzTdz3ahgJYmkt4xZkgDz8=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.16.0/go.mod h1:OeVe5ggFzoBnmgitZe/A+BqGOnv1DvU/0uiLQi1wutM=
|
||||
github.com/carapace-sh/carapace-shlex v1.0.1 h1:ww0JCgWpOVuqWG7k3724pJ18Lq8gh5pHQs9j3ojUs1c=
|
||||
github.com/carapace-sh/carapace-shlex v1.0.1/go.mod h1:lJ4ZsdxytE0wHJ8Ta9S7Qq0XpjgjU0mdfCqiI2FHx7M=
|
||||
github.com/cdevents/sdk-go v0.4.1 h1:Cr/iH/I51Z+slxKRx9AV7stn6hr2pjRHQ5wpPJhRLTU=
|
||||
github.com/cdevents/sdk-go v0.4.1/go.mod h1:3IhWLoY4vsyUEzv7XJbyr0BRQ0KPgvNx+wiD2hQGFNU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
|
@ -103,13 +108,12 @@ github.com/chainguard-dev/git-urls v1.0.2/go.mod h1:rbGgj10OS7UgZlbzdUQIQpT0k/D4
|
|||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudevents/sdk-go/v2 v2.15.2 h1:54+I5xQEnI73RBhWHxbI1XJcqOFOVJN85vb41+8mHUc=
|
||||
github.com/cloudevents/sdk-go/v2 v2.15.2/go.mod h1:lL7kSWAE/V8VI4Wh0jbL2v/jvqsm6tjmaQBSvxcv4uE=
|
||||
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
|
||||
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/containrrr/shoutrrr v0.8.0 h1:mfG2ATzIS7NR2Ec6XL+xyoHzN97H8WPjir8aYzJUSec=
|
||||
github.com/containrrr/shoutrrr v0.8.0/go.mod h1:ioyQAyu1LJY6sILuNyKaQaw+9Ttik5QePU8atnAdO2o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk=
|
||||
github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||
|
@ -128,8 +132,14 @@ github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TR
|
|||
github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=
|
||||
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/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/docker/cli v28.2.2+incompatible h1:qzx5BNUDFqlvyq4AHzdNB7gSyVTmU4cgsyN9SdInc1A=
|
||||
github.com/docker/cli v28.2.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
|
||||
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
|
@ -144,55 +154,59 @@ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
|||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fluxcd/cli-utils v0.36.0-flux.12 h1:8cD6SmaKa/lGo0KCu0XWiGrXJMLMBQwSsnoP0cG+Gjw=
|
||||
github.com/fluxcd/cli-utils v0.36.0-flux.12/go.mod h1:Nb/zMqsJAzjz4/HIsEc2LTqxC6eC0rV26t4hkJT/F9o=
|
||||
github.com/fluxcd/pkg/apis/acl v0.6.0 h1:rllf5uQLzTow81ZCslkQ6LPpDNqVQr6/fWaNksdUEtc=
|
||||
github.com/fluxcd/pkg/apis/acl v0.6.0/go.mod h1:IVDZx3MAoDWjlLrJHMF9Z27huFuXAEQlnbWw0M6EcTs=
|
||||
github.com/fluxcd/pkg/apis/event v0.16.0 h1:ffKc/3erowPnh72lFszz7sPQhLZ7bhqNrq+pu1Pb+JE=
|
||||
github.com/fluxcd/pkg/apis/event v0.16.0/go.mod h1:D/QQi5lHT9/Ur3OMFLJO71D4KDQHbJ5s8dQV3h1ZAT0=
|
||||
github.com/fluxcd/pkg/apis/kustomize v1.9.0 h1:SJpT1CK58AnTvCpDKeGfMNA0Xud/4VReZNvPe8XkTxo=
|
||||
github.com/fluxcd/pkg/apis/kustomize v1.9.0/go.mod h1:AZl2GU03oPVue6SUivdiIYd/3mvF94j7t1G2JO26d4s=
|
||||
github.com/fluxcd/pkg/apis/meta v1.10.0 h1:rqbAuyl5ug7A5jjRf/rNwBXmNl6tJ9wG2iIsriwnQUk=
|
||||
github.com/fluxcd/pkg/apis/meta v1.10.0/go.mod h1:n7NstXHDaleAUMajcXTVkhz0MYkvEXy1C/eLI/t1xoI=
|
||||
github.com/fluxcd/pkg/auth v0.3.0 h1:I1A3e81O+bpAgEcJ3e+rXqObKPjzBu6FLYXQTSxXLOs=
|
||||
github.com/fluxcd/pkg/auth v0.3.0/go.mod h1:g9KJ4iNcCd6Sb7al4yN1+olgOfgwmU4lgCWbwvMsFRE=
|
||||
github.com/fluxcd/pkg/git v0.24.0 h1:aMAL8MUNPZXyRia+LVVudTpmLHIpzmz9F5tedvhhLzs=
|
||||
github.com/fluxcd/pkg/git v0.24.0/go.mod h1:vxUhjBwnkvbAByN7UC5Go33/mgrLSIIg1rH+dyOZVRo=
|
||||
github.com/fluxcd/pkg/masktoken v0.6.0 h1:ijSqMl2L9jBR3QFcHA0FX7kxV0xgSB4PY5p//8FdVR4=
|
||||
github.com/fluxcd/pkg/masktoken v0.6.0/go.mod h1:bMj45KySJ2gLeFiFaXD5nQLNFlvDqGbZolsiurZKVUU=
|
||||
github.com/fluxcd/pkg/runtime v0.53.1 h1:S+QRSoiU+LH1sTvJLNvT1x3E5hBq/sjOsRHazA7OqTo=
|
||||
github.com/fluxcd/pkg/runtime v0.53.1/go.mod h1:8vkIhS1AhkmjC98LRm5xM+CRG5KySFTXpJWk+ZdtT4I=
|
||||
github.com/fluxcd/pkg/ssa v0.45.1 h1:ISl84TJwRP/GuZXrKiR9Tf8JOnG5XFgtjcYoR4XQYf4=
|
||||
github.com/fluxcd/pkg/ssa v0.45.1/go.mod h1:8Anf7XVZ0zxOve7HXbDaW1s0gfmP95ksJBlKfDYinhQ=
|
||||
github.com/fluxcd/pkg/ssh v0.16.0 h1:dhSWNp30p05EJ86bhICezad9pG3fJi4CAVKnZ3EmUV8=
|
||||
github.com/fluxcd/pkg/ssh v0.16.0/go.mod h1:MyDegNZHnKNDAwM5/A2t/1FjpvpS8BsRZQ4WqEwCHc0=
|
||||
github.com/fluxcd/cli-utils v0.36.0-flux.14 h1:I//AMVUXTc+M04UtIXArMXQZCazGMwfemodV1j/yG8c=
|
||||
github.com/fluxcd/cli-utils v0.36.0-flux.14/go.mod h1:uDo7BYOfbdmk/asnHuI0IQPl6u0FCgcN54AHDu3Y5As=
|
||||
github.com/fluxcd/pkg/apis/acl v0.7.0 h1:dMhZJH+g6ZRPjs4zVOAN9vHBd1DcavFgcIFkg5ooOE0=
|
||||
github.com/fluxcd/pkg/apis/acl v0.7.0/go.mod h1:uv7pXXR/gydiX4MUwlQa7vS8JONEDztynnjTvY3JxKQ=
|
||||
github.com/fluxcd/pkg/apis/event v0.18.0 h1:PNbWk9gvX8gMIi6VsJapnuDO+giLEeY+6olLVXvXFkk=
|
||||
github.com/fluxcd/pkg/apis/event v0.18.0/go.mod h1:7S/DGboLolfbZ6stO6dcDhG1SfkPWQ9foCULvbiYpiA=
|
||||
github.com/fluxcd/pkg/apis/kustomize v1.11.0 h1:0IzDgxZkc4v+5SDNCvgZhfwfkdkQLPXCner7TNaJFWE=
|
||||
github.com/fluxcd/pkg/apis/kustomize v1.11.0/go.mod h1:j302mJGDww8cn9qvMsRQ0LJ1HPAPs/IlX7CSsoJV7BI=
|
||||
github.com/fluxcd/pkg/apis/meta v1.17.0 h1:KVMDyJQj1NYCsppsFUkbJGMnKxsqJVpnKBFolHf/q8E=
|
||||
github.com/fluxcd/pkg/apis/meta v1.17.0/go.mod h1:97l3hTwBpJbXBY+wetNbqrUsvES8B1jGioKcBUxmqd8=
|
||||
github.com/fluxcd/pkg/auth v0.21.0 h1:ckAQqP12wuptXEkMY18SQKWEY09m9e6yI0mEMsDV15M=
|
||||
github.com/fluxcd/pkg/auth v0.21.0/go.mod h1:MXmpsXT97c874HCw5hnfqFUP7TsG8/Ss1vFrk8JccfM=
|
||||
github.com/fluxcd/pkg/cache v0.10.0 h1:M+OGDM4da1cnz7q+sZSBtkBJHpiJsLnKVmR9OdMWxEY=
|
||||
github.com/fluxcd/pkg/cache v0.10.0/go.mod h1:pPXRzQUDQagsCniuOolqVhnAkbNgYOg8d2cTliPs7ME=
|
||||
github.com/fluxcd/pkg/git v0.34.0 h1:qTViWkfpEDnjzySyKRKliqUeGj/DznqlkmPhaDNIsFY=
|
||||
github.com/fluxcd/pkg/git v0.34.0/go.mod h1:F9Asm3MlLW4uZx3FF92+bqho+oktdMdnTn/QmXe56NE=
|
||||
github.com/fluxcd/pkg/masktoken v0.7.0 h1:pitmyOg2pUVdW+nn2Lk/xqm2TaA08uxvOC0ns3sz6bM=
|
||||
github.com/fluxcd/pkg/masktoken v0.7.0/go.mod h1:Lc1uoDjO1GY6+YdkK+ZqqBIBWquyV58nlSJ5S1N1IYU=
|
||||
github.com/fluxcd/pkg/runtime v0.69.0 h1:5gPY95NSFI34GlQTj0+NHjOFpirSwviCUb9bM09b5nA=
|
||||
github.com/fluxcd/pkg/runtime v0.69.0/go.mod h1:ug+pat+I4wfOBuCy2E/pLmBNd3kOOo4cP2jxnxefPwY=
|
||||
github.com/fluxcd/pkg/ssa v0.51.0 h1:sFarxKZcS0J8sjq9qvs/r+1XiJqNgRodEiPjV75F8R4=
|
||||
github.com/fluxcd/pkg/ssa v0.51.0/go.mod h1:v+h9RC0JxWIqMTK2Eo+8Nh700AXyZChZ2TiLVj4tf3M=
|
||||
github.com/fluxcd/pkg/ssh v0.20.0 h1:Ak0laIYIc/L8lEfqls/LDWRW8wYPESGaravQsCRGLb8=
|
||||
github.com/fluxcd/pkg/ssh v0.20.0/go.mod h1:sRfAAkxx1GwCGjYirKPnTKdNkNrJRo9kqzWLVFXKv7E=
|
||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=
|
||||
github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4=
|
||||
github.com/getsentry/sentry-go v0.31.1 h1:ELVc0h7gwyhnXHDouXkhqTFSO5oslsRDk0++eyE0KJ4=
|
||||
github.com/getsentry/sentry-go v0.31.1/go.mod h1:CYNcMMz73YigoHljQRG+qPF+eMq8gG72XcGN/p71BAY=
|
||||
github.com/getsentry/sentry-go v0.34.1 h1:HSjc1C/OsnZttohEPrrqKH42Iud0HuLCXpv8cU1pWcw=
|
||||
github.com/getsentry/sentry-go v0.34.1/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE=
|
||||
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-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI=
|
||||
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/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/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
|
||||
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
|
||||
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
|
@ -201,7 +215,6 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
|||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
||||
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||
|
@ -209,15 +222,15 @@ github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PU
|
|||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
@ -232,10 +245,10 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
|
|||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4=
|
||||
github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo=
|
||||
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
|
||||
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
|
||||
github.com/google/cel-go v0.25.0 h1:jsFw9Fhn+3y2kBbltZR4VEz5xKkcIFRPDnuEzAGv5GY=
|
||||
github.com/google/cel-go v0.25.0/go.mod h1:hjEb6r5SuOSlhCHmFoLzu8HGCERvIsDAbxDAyNU/MmI=
|
||||
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
|
||||
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
|
@ -244,74 +257,68 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
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.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU=
|
||||
github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y=
|
||||
github.com/google/go-github/v64 v64.0.0 h1:4G61sozmY3eiPAjjoOHponXDBONm+utovTKbyUb2Qdg=
|
||||
github.com/google/go-github/v64 v64.0.0/go.mod h1:xB3vqMQNdHzilXBiO2I+M7iEFtHf+DP/omBOv6tQzVo=
|
||||
github.com/google/go-github/v68 v68.0.0 h1:ZW57zeNZiXTdQ16qrDiZ0k6XucrxZ2CGmoTvcCyQG6s=
|
||||
github.com/google/go-github/v68 v68.0.0/go.mod h1:K9HAUBovM2sLwM408A18h+wd9vqdLOEqTUCbnRIcx68=
|
||||
github.com/google/go-github/v72 v72.0.0 h1:FcIO37BLoVPBO9igQQ6tStsv2asG4IPcYFi655PPvBM=
|
||||
github.com/google/go-github/v72 v72.0.0/go.mod h1:WWtw8GMRiL62mvIquf1kO3onRHeWWKmK01qdCY8c5fg=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20250128161936-077ca0a936bf h1:BvBLUD2hkvLI3dJTJMiopAq8/wp43AAZKTP7qdpptbU=
|
||||
github.com/google/pprof v0.0.0-20250128161936-077ca0a936bf/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 h1:xhMrHhTJ6zxu3gA4enFM9MLn9AY7613teCdFnlUVbSQ=
|
||||
github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5/go.mod h1:5hDyRhoBCxViHszMt12TnOpEI4VVi+U8Gm9iphldiMA=
|
||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/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.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||
github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0=
|
||||
github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
|
||||
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||
github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek=
|
||||
github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jarcoal/httpmock v1.3.0 h1:2RJ8GP0IIaWwcC9Fp2BmVi8Kog3v2Hn7VXM3fTd+nuc=
|
||||
github.com/jarcoal/httpmock v1.3.0/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
|
||||
github.com/k0kubun/pp v3.0.1+incompatible/go.mod h1:GWse8YhT0p8pT4ir3ZgBbfZild3tgzSScAn6HmfYukg=
|
||||
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
|
||||
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
|
||||
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/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/ktrysmt/go-bitbucket v0.9.81 h1:PQxJsFcGdblDOv5PhFA03uNgXMiJfpLo03oYIUdQ2h0=
|
||||
github.com/ktrysmt/go-bitbucket v0.9.81/go.mod h1:eWIy5+e1l2eDf9xxwCEmK7oPvNKR91vwYocJWIUQISQ=
|
||||
github.com/ktrysmt/go-bitbucket v0.9.86 h1:co80t9wS4kKgRLsvDgi+3wQPiLY30u10ehcTxgXFvzw=
|
||||
github.com/ktrysmt/go-bitbucket v0.9.86/go.mod h1:/lsYmiQrBHNPTnPKF0Q+safIS7peInQOGbJXu0xPRho=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
|
@ -322,7 +329,6 @@ github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4
|
|||
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
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/microsoft/azure-devops-go-api/azuredevops/v6 v6.0.1 h1:ACnM5CwgTH6OSQHErzZDrotEG0rffPdJxtF/WOWglAw=
|
||||
|
@ -348,16 +354,18 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
|||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/nats-io/nats.go v1.39.0 h1:2/yg2JQjiYYKLwDuBzV0FbB2sIV+eFNkEevlRi4n9lI=
|
||||
github.com/nats-io/nats.go v1.39.0/go.mod h1:MgRb8oOdigA6cYpEPhXJuRVH6UE/V4jblJ2jQ27IXYM=
|
||||
github.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0=
|
||||
github.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE=
|
||||
github.com/nats-io/nats.go v1.43.0 h1:uRFZ2FEoRvP64+UUhaTokyS18XBCR/xM2vQZKO4i8ug=
|
||||
github.com/nats-io/nats.go v1.43.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
|
||||
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
|
||||
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4 h1:ktYTpKJAVZnDT4VjxSbiBenUjmlL/5QkBEocaWXiQus=
|
||||
github.com/onsi/ginkgo/v2 v2.23.4/go.mod h1:Bt66ApGPBFzHyR+JO10Zbt0Gsp4uWxu5mIOTusL46e8=
|
||||
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
|
||||
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/package-url/packageurl-go v0.1.1 h1:KTRE0bK3sKbFKAk3yy63DpeskU7Cvs/x/Da5l+RtzyU=
|
||||
github.com/package-url/packageurl-go v0.1.1/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
||||
|
@ -366,42 +374,38 @@ github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4
|
|||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||
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/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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=
|
||||
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
|
||||
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
||||
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
|
||||
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
|
||||
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
|
||||
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
||||
github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
|
||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/sethvargo/go-limiter v1.0.0 h1:JqW13eWEMn0VFv86OKn8wiYJY/m250WoXdrjRV0kLe4=
|
||||
github.com/sethvargo/go-limiter v1.0.0/go.mod h1:01b6tW25Ap+MeLYBuD4aHunMrJoNO5PVUFdS9rac3II=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/slok/go-http-metrics v0.13.0 h1:lQDyJJx9wKhmbliyUsZ2l6peGnXRHjsjoqPt5VYzcP8=
|
||||
github.com/slok/go-http-metrics v0.13.0/go.mod h1:HIr7t/HbN2sJaunvnt9wKP9xoBBVZFo1/KiHU3b0w+4=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
|
||||
|
@ -412,14 +416,12 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
|
|||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
|
@ -430,46 +432,48 @@ github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
|
|||
github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
gitlab.com/gitlab-org/api/client-go v0.122.0 h1:Nog85APtgquS+HHkMkP4DiZ6lXlUZYhQKqguS4OJYNM=
|
||||
gitlab.com/gitlab-org/api/client-go v0.122.0/go.mod h1:Jh0qjLILEdbO6z/OY94RD+3NDQRUKiuFSFYozN6cpKM=
|
||||
gitlab.com/gitlab-org/api/client-go v0.134.0 h1:J4i6qPN5hRLsqatPxVbe9w2C0A3JEItyCQrzsP52S2k=
|
||||
gitlab.com/gitlab-org/api/client-go v0.134.0/go.mod h1:crkp9sCwMQ8gDwuMLgk11sDT336t6U3kESBT0BGsOBo=
|
||||
go.einride.tech/aip v0.68.1 h1:16/AfSxcQISGN5z9C5lM+0mLYXihrHbQ1onvYTr93aQ=
|
||||
go.einride.tech/aip v0.68.1/go.mod h1:XaFtaj4HuA3Zwk9xoBtTWgNubZ0ZZXv9BZJCkuKuWbg=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
|
||||
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
|
||||
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
||||
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
||||
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
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.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.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
|
@ -478,10 +482,8 @@ golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvx
|
|||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
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-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
@ -493,26 +495,18 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
|||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
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.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
|
||||
golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
@ -521,37 +515,20 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.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.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.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
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.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
|
||||
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.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.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
|
||||
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
|
@ -560,36 +537,34 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
|
|||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
|
||||
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw=
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
google.golang.org/api v0.221.0 h1:qzaJfLhDsbMeFee8zBRdt/Nc+xmOuafD/dbdgGfutOU=
|
||||
google.golang.org/api v0.221.0/go.mod h1:7sOU2+TL4TxUTdbi0gWgAIg7tH5qBXxoyhtL+9x3biQ=
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
google.golang.org/api v0.241.0 h1:QKwqWQlkc6O895LchPEDUSYr22Xp3NCxpQRiWTB6avE=
|
||||
google.golang.org/api v0.241.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50=
|
||||
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-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s=
|
||||
google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
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.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
|
||||
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
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=
|
||||
|
@ -599,50 +574,57 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
|||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
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.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
|
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||
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.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc=
|
||||
k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
|
||||
k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw=
|
||||
k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto=
|
||||
k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs=
|
||||
k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/cli-runtime v0.32.1 h1:19nwZPlYGJPUDbhAxDIS2/oydCikvKMHsxroKNGA2mM=
|
||||
k8s.io/cli-runtime v0.32.1/go.mod h1:NJPbeadVFnV2E7B7vF+FvU09mpwYlZCu8PqjzfuOnkY=
|
||||
k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU=
|
||||
k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg=
|
||||
k8s.io/component-base v0.32.1 h1:/5IfJ0dHIKBWysGV0yKTFfacZ5yNV1sulPh3ilJjRZk=
|
||||
k8s.io/component-base v0.32.1/go.mod h1:j1iMMHi/sqAHeG5z+O9BFNCF698a1u0186zkjMZQ28w=
|
||||
k8s.io/api v0.33.2 h1:YgwIS5jKfA+BZg//OQhkJNIfie/kmRsO0BmNaVSimvY=
|
||||
k8s.io/api v0.33.2/go.mod h1:fhrbphQJSM2cXzCWgqU29xLDuks4mu7ti9vveEnpSXs=
|
||||
k8s.io/apiextensions-apiserver v0.33.2 h1:6gnkIbngnaUflR3XwE1mCefN3YS8yTD631JXQhsU6M8=
|
||||
k8s.io/apiextensions-apiserver v0.33.2/go.mod h1:IvVanieYsEHJImTKXGP6XCOjTwv2LUMos0YWc9O+QP8=
|
||||
k8s.io/apimachinery v0.33.2 h1:IHFVhqg59mb8PJWTLi8m1mAoepkUNYmptHsV+Z1m5jY=
|
||||
k8s.io/apimachinery v0.33.2/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/cli-runtime v0.33.2 h1:koNYQKSDdq5AExa/RDudXMhhtFasEg48KLS2KSAU74Y=
|
||||
k8s.io/cli-runtime v0.33.2/go.mod h1:gnhsAWpovqf1Zj5YRRBBU7PFsRc6NkEkwYNQE+mXL88=
|
||||
k8s.io/client-go v0.33.2 h1:z8CIcc0P581x/J1ZYf4CNzRKxRvQAwoAolYPbtQes+E=
|
||||
k8s.io/client-go v0.33.2/go.mod h1:9mCgT4wROvL948w6f6ArJNb7yQd7QsvqavDeZHvNmHo=
|
||||
k8s.io/component-base v0.33.2 h1:sCCsn9s/dG3ZrQTX/Us0/Sx2R0G5kwa0wbZFYoVp/+0=
|
||||
k8s.io/component-base v0.33.2/go.mod h1:/41uw9wKzuelhN+u+/C59ixxf4tYQKW7p32ddkYNe2k=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg=
|
||||
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas=
|
||||
k8s.io/kubectl v0.32.1 h1:/btLtXLQUU1rWx8AEvX9jrb9LaI6yeezt3sFALhB8M8=
|
||||
k8s.io/kubectl v0.32.1/go.mod h1:sezNuyWi1STk4ZNPVRIFfgjqMI6XMf+oCVLjZen/pFQ=
|
||||
k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
|
||||
k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/controller-runtime v0.20.1 h1:JbGMAG/X94NeM3xvjenVUaBjy6Ui4Ogd/J5ZtjZnHaE=
|
||||
sigs.k8s.io/controller-runtime v0.20.1/go.mod h1:BrP3w158MwvB3ZbNpaAcIKkHQ7YGpYnzpoSTZ8E14WU=
|
||||
k8s.io/kube-openapi v0.0.0-20250701173324-9bd5c66d9911 h1:gAXU86Fmbr/ktY17lkHwSjw5aoThQvhnstGGIYKlKYc=
|
||||
k8s.io/kube-openapi v0.0.0-20250701173324-9bd5c66d9911/go.mod h1:GLOk5B+hDbRROvt0X2+hqX64v/zO3vXN7J78OUmBSKw=
|
||||
k8s.io/kubectl v0.33.2 h1:7XKZ6DYCklu5MZQzJe+CkCjoGZwD1wWl7t/FxzhMz7Y=
|
||||
k8s.io/kubectl v0.33.2/go.mod h1:8rC67FB8tVTYraovAGNi/idWIK90z2CHFNMmGJZJ3KI=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8=
|
||||
sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/kustomize/api v0.19.0 h1:F+2HB2mU1MSiR9Hp1NEgoU2q9ItNOaBJl0I4Dlus5SQ=
|
||||
sigs.k8s.io/kustomize/api v0.19.0/go.mod h1:/BbwnivGVcBh1r+8m3tH1VNxJmHSk1PzP5fkP6lbL1o=
|
||||
sigs.k8s.io/kustomize/kyaml v0.19.0 h1:RFge5qsO1uHhwJsu3ipV7RNolC7Uozc0jUBC/61XSlA=
|
||||
sigs.k8s.io/kustomize/kyaml v0.19.0/go.mod h1:FeKD5jEOH+FbZPpqUghBP8mrLjJ3+zD3/rf9NNu1cwY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/kustomize/api v0.20.0 h1:xPLqcobHI0bThyRUteO+nCV8G4d1Rlo5HafO57VRcas=
|
||||
sigs.k8s.io/kustomize/api v0.20.0/go.mod h1:F6CfaV27oevRCMJgehLqyX81dlUnRX/Fc13Uo7+OSo4=
|
||||
sigs.k8s.io/kustomize/kyaml v0.20.0 h1:tT8KMKi4R3hCJ1+9HDdek2VoXpkerP92ZfF6fDgGw14=
|
||||
sigs.k8s.io/kustomize/kyaml v0.20.0/go.mod h1:0EmkQHRUsJxY8Ug9Niig1pUMSCGHxQ5RklbpV/Ri6po=
|
||||
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
|
||||
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
|
||||
|
|
|
@ -19,7 +19,6 @@ package controller
|
|||
import (
|
||||
"context"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
kuberecorder "k8s.io/client-go/tools/record"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
|
@ -28,12 +27,16 @@ import (
|
|||
|
||||
apiv1 "github.com/fluxcd/notification-controller/api/v1"
|
||||
apiv1beta3 "github.com/fluxcd/notification-controller/api/v1beta3"
|
||||
"github.com/fluxcd/pkg/cache"
|
||||
"github.com/fluxcd/pkg/runtime/patch"
|
||||
|
||||
"github.com/fluxcd/notification-controller/internal/notifier"
|
||||
)
|
||||
|
||||
// +kubebuilder:rbac:groups=notification.toolkit.fluxcd.io,resources=providers,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch
|
||||
// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch
|
||||
// +kubebuilder:rbac:groups="",resources=serviceaccounts/token,verbs=create
|
||||
|
||||
// ProviderReconciler reconciles a Provider object to migrate it to static
|
||||
// Provider.
|
||||
|
@ -41,40 +44,21 @@ type ProviderReconciler struct {
|
|||
client.Client
|
||||
kuberecorder.EventRecorder
|
||||
|
||||
ControllerName string
|
||||
TokenCache *cache.TokenCache
|
||||
}
|
||||
|
||||
func (r *ProviderReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&apiv1beta3.Provider{}, builder.WithPredicates(finalizerPredicate{})).
|
||||
For(&apiv1beta3.Provider{}, builder.WithPredicates(providerPredicate{})).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
func (r *ProviderReconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, retErr error) {
|
||||
log := ctrl.LoggerFrom(ctx)
|
||||
|
||||
obj := &apiv1beta3.Provider{}
|
||||
if err := r.Get(ctx, req.NamespacedName, obj); err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
|
||||
// Early return if no migration is needed.
|
||||
if !controllerutil.ContainsFinalizer(obj, apiv1.NotificationFinalizer) {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// Examine if the object is under deletion.
|
||||
var delete bool
|
||||
if !obj.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
delete = true
|
||||
}
|
||||
|
||||
// Skip if it's suspend and not being deleted.
|
||||
if obj.Spec.Suspend && !delete {
|
||||
log.Info("reconciliation is suspended for this object")
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
patcher, err := patch.NewHelper(obj, r.Client)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
|
@ -86,11 +70,29 @@ func (r *ProviderReconciler) Reconcile(ctx context.Context, req ctrl.Request) (r
|
|||
}
|
||||
}()
|
||||
|
||||
// Remove the notification-controller finalizer.
|
||||
controllerutil.RemoveFinalizer(obj, apiv1.NotificationFinalizer)
|
||||
// Examine if the object is under deletion.
|
||||
if !obj.ObjectMeta.DeletionTimestamp.IsZero() {
|
||||
return r.reconcileDelete(obj)
|
||||
}
|
||||
|
||||
log.Info("removed finalizer from Provider to migrate to static Provider")
|
||||
r.Event(obj, corev1.EventTypeNormal, "Migration", "removed finalizer from Provider to migrate to static Provider")
|
||||
// Add finalizer if it doesn't exist.
|
||||
if !controllerutil.ContainsFinalizer(obj, apiv1.NotificationFinalizer) {
|
||||
controllerutil.AddFinalizer(obj, apiv1.NotificationFinalizer)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// reconcileDelete handles the deletion of the object.
|
||||
// It cleans up the caches and removes the finalizer.
|
||||
func (r *ProviderReconciler) reconcileDelete(obj *apiv1beta3.Provider) (ctrl.Result, error) {
|
||||
// Remove our finalizer from the list
|
||||
controllerutil.RemoveFinalizer(obj, apiv1.NotificationFinalizer)
|
||||
|
||||
// Cleanup caches.
|
||||
r.TokenCache.DeleteEventsForObject(apiv1beta3.ProviderKind,
|
||||
obj.GetName(), obj.GetNamespace(), notifier.OperationPost)
|
||||
|
||||
// Stop reconciliation as the object is being deleted
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
|
|
@ -52,61 +52,29 @@ func TestProviderReconciler(t *testing.T) {
|
|||
}
|
||||
providerKey := client.ObjectKeyFromObject(provider)
|
||||
|
||||
// Remove finalizer at create.
|
||||
|
||||
provider.ObjectMeta.Finalizers = append(provider.ObjectMeta.Finalizers, "foo.bar", apiv1.NotificationFinalizer)
|
||||
// Create without finalizer.
|
||||
provider.Spec = apiv1beta3.ProviderSpec{
|
||||
Type: "slack",
|
||||
Type: "generic",
|
||||
}
|
||||
g.Expect(testEnv.Create(ctx, provider)).ToNot(HaveOccurred())
|
||||
|
||||
// Should eventually have finalizer.
|
||||
g.Eventually(func() bool {
|
||||
_ = testEnv.Get(ctx, providerKey, provider)
|
||||
return !controllerutil.ContainsFinalizer(provider, apiv1.NotificationFinalizer)
|
||||
return controllerutil.ContainsFinalizer(provider, apiv1.NotificationFinalizer)
|
||||
}, timeout, time.Second).Should(BeTrue())
|
||||
|
||||
// Remove finalizer at update.
|
||||
|
||||
// Remove finalizer.
|
||||
patchHelper, err := patch.NewHelper(provider, testEnv.Client)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
provider.ObjectMeta.Finalizers = append(provider.ObjectMeta.Finalizers, apiv1.NotificationFinalizer)
|
||||
controllerutil.RemoveFinalizer(provider, apiv1.NotificationFinalizer)
|
||||
g.Expect(patchHelper.Patch(ctx, provider)).ToNot(HaveOccurred())
|
||||
|
||||
// Should eventually have finalizer again.
|
||||
g.Eventually(func() bool {
|
||||
_ = testEnv.Get(ctx, providerKey, provider)
|
||||
return !controllerutil.ContainsFinalizer(provider, apiv1.NotificationFinalizer)
|
||||
}, timeout, time.Second).Should(BeTrue())
|
||||
|
||||
// Remove finalizer at delete.
|
||||
|
||||
patchHelper, err = patch.NewHelper(provider, testEnv.Client)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// Suspend the provider to prevent finalizer from getting removed.
|
||||
// Ensure only flux finalizer is set to allow the object to be garbage
|
||||
// collected at the end.
|
||||
// NOTE: Suspending and updating finalizers are done separately here as
|
||||
// doing them in a single patch results in flaky test where the finalizer
|
||||
// update doesn't gets registered with the kube-apiserver, resulting in
|
||||
// timeout waiting for finalizer to appear on the object below.
|
||||
provider.Spec.Suspend = true
|
||||
g.Expect(patchHelper.Patch(ctx, provider)).ToNot(HaveOccurred())
|
||||
g.Eventually(func() bool {
|
||||
_ = k8sClient.Get(ctx, providerKey, provider)
|
||||
return provider.Spec.Suspend == true
|
||||
}, timeout).Should(BeTrue())
|
||||
|
||||
patchHelper, err = patch.NewHelper(provider, testEnv.Client)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// Add finalizer and verify that finalizer exists on the object using a live
|
||||
// client.
|
||||
provider.ObjectMeta.Finalizers = []string{apiv1.NotificationFinalizer}
|
||||
g.Expect(patchHelper.Patch(ctx, provider)).ToNot(HaveOccurred())
|
||||
g.Eventually(func() bool {
|
||||
_ = k8sClient.Get(ctx, providerKey, provider)
|
||||
return controllerutil.ContainsFinalizer(provider, apiv1.NotificationFinalizer)
|
||||
}, timeout).Should(BeTrue())
|
||||
}, timeout, time.Second).Should(BeTrue())
|
||||
|
||||
// Delete the object and verify.
|
||||
g.Expect(testEnv.Delete(ctx, provider)).ToNot(HaveOccurred())
|
||||
|
@ -117,3 +85,85 @@ func TestProviderReconciler(t *testing.T) {
|
|||
return false
|
||||
}, timeout).Should(BeTrue())
|
||||
}
|
||||
|
||||
func TestProviderReconciler_APIServerValidation(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
providerType string
|
||||
commitStatusExpr string
|
||||
err string
|
||||
}{
|
||||
{
|
||||
name: "github provider types can create providers with commitStatusExpr",
|
||||
providerType: "github",
|
||||
commitStatusExpr: "event.metadata.namespace + '/' + event.metadata.name + '/' + provider.metadata.uid",
|
||||
},
|
||||
{
|
||||
name: "gitlab provider types can create providers with commitStatusExpr",
|
||||
providerType: "gitlab",
|
||||
commitStatusExpr: "event.metadata.namespace + '/' + event.metadata.name + '/' + provider.metadata.uid",
|
||||
},
|
||||
{
|
||||
name: "gitea provider types can create providers with commitStatusExpr",
|
||||
providerType: "gitea",
|
||||
commitStatusExpr: "event.metadata.namespace + '/' + event.metadata.name + '/' + provider.metadata.uid",
|
||||
},
|
||||
{
|
||||
name: "bitbucketserver provider types can create providers with commitStatusExpr",
|
||||
providerType: "bitbucketserver",
|
||||
commitStatusExpr: "event.metadata.namespace + '/' + event.metadata.name + '/' + provider.metadata.uid",
|
||||
},
|
||||
{
|
||||
name: "bitbucket provider types can create providers with commitStatusExpr",
|
||||
providerType: "bitbucket",
|
||||
commitStatusExpr: "event.metadata.namespace + '/' + event.metadata.name + '/' + provider.metadata.uid",
|
||||
},
|
||||
{
|
||||
name: "azuredevops provider types can create providers with commitStatusExpr",
|
||||
providerType: "azuredevops",
|
||||
commitStatusExpr: "event.metadata.namespace + '/' + event.metadata.name + '/' + provider.metadata.uid",
|
||||
},
|
||||
{
|
||||
name: "unsupported provider types cannot create providers with commitStatusExpr",
|
||||
providerType: "slack",
|
||||
commitStatusExpr: "event.metadata.namespace + '/' + event.metadata.name + '/' + provider.metadata.uid",
|
||||
err: "spec.commitStatusExpr is only supported for the 'github', 'gitlab', 'gitea', 'bitbucketserver', 'bitbucket', 'azuredevops' provider types",
|
||||
},
|
||||
{
|
||||
name: "github provider types can create providers without commitStatusExpr",
|
||||
providerType: "github",
|
||||
commitStatusExpr: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
obj := &apiv1beta3.Provider{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
GenerateName: "provider-reconcile-",
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: apiv1beta3.ProviderSpec{
|
||||
Type: tt.providerType,
|
||||
CommitStatusExpr: tt.commitStatusExpr,
|
||||
},
|
||||
}
|
||||
|
||||
err := testEnv.Create(ctx, obj)
|
||||
if err == nil {
|
||||
defer func() {
|
||||
err := testEnv.Delete(ctx, obj)
|
||||
g.Expect(err).ToNot(HaveOccurred())
|
||||
}()
|
||||
}
|
||||
|
||||
if tt.err != "" {
|
||||
g.Expect(err.Error()).To(ContainSubstring(tt.err))
|
||||
} else {
|
||||
g.Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
Copyright 2025 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/event"
|
||||
|
||||
apiv1 "github.com/fluxcd/notification-controller/api/v1"
|
||||
apiv1beta3 "github.com/fluxcd/notification-controller/api/v1beta3"
|
||||
)
|
||||
|
||||
// providerPredicate implements predicate functions for the Provider API.
|
||||
type providerPredicate struct{}
|
||||
|
||||
func (providerPredicate) Create(e event.CreateEvent) bool {
|
||||
return !controllerutil.ContainsFinalizer(e.Object, apiv1.NotificationFinalizer)
|
||||
}
|
||||
|
||||
func (providerPredicate) Update(e event.UpdateEvent) bool {
|
||||
if e.ObjectNew == nil {
|
||||
return false
|
||||
}
|
||||
return !controllerutil.ContainsFinalizer(e.ObjectNew, apiv1.NotificationFinalizer) ||
|
||||
!e.ObjectNew.(*apiv1beta3.Provider).ObjectMeta.DeletionTimestamp.IsZero()
|
||||
}
|
||||
|
||||
func (providerPredicate) Delete(e event.DeleteEvent) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (providerPredicate) Generic(e event.GenericEvent) bool {
|
||||
return !controllerutil.ContainsFinalizer(e.Object, apiv1.NotificationFinalizer)
|
||||
}
|
|
@ -82,9 +82,8 @@ func TestMain(m *testing.M) {
|
|||
}
|
||||
|
||||
if err := (&ProviderReconciler{
|
||||
Client: testEnv,
|
||||
ControllerName: controllerName,
|
||||
EventRecorder: testEnv.GetEventRecorderFor(controllerName),
|
||||
Client: testEnv,
|
||||
EventRecorder: testEnv.GetEventRecorderFor(controllerName),
|
||||
}).SetupWithManager(testEnv); err != nil {
|
||||
panic(fmt.Sprintf("Failed to start ProviderReconciler: %v", err))
|
||||
}
|
||||
|
|
|
@ -18,7 +18,10 @@ limitations under the License.
|
|||
// and their default states.
|
||||
package features
|
||||
|
||||
import feathelper "github.com/fluxcd/pkg/runtime/features"
|
||||
import (
|
||||
"github.com/fluxcd/pkg/auth"
|
||||
feathelper "github.com/fluxcd/pkg/runtime/features"
|
||||
)
|
||||
|
||||
const (
|
||||
// CacheSecretsAndConfigMaps controls whether Secrets and ConfigMaps should
|
||||
|
@ -35,6 +38,10 @@ var features = map[string]bool{
|
|||
CacheSecretsAndConfigMaps: false,
|
||||
}
|
||||
|
||||
func init() {
|
||||
auth.SetFeatureGates(features)
|
||||
}
|
||||
|
||||
// FeatureGates contains a list of all supported feature gates and
|
||||
// their default values.
|
||||
func FeatureGates() map[string]bool {
|
||||
|
|
|
@ -18,7 +18,7 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
@ -32,10 +32,10 @@ import (
|
|||
)
|
||||
|
||||
type Alertmanager struct {
|
||||
URL string
|
||||
ProxyURL string
|
||||
CertPool *x509.CertPool
|
||||
Token string
|
||||
URL string
|
||||
ProxyURL string
|
||||
TLSConfig *tls.Config
|
||||
Token string
|
||||
}
|
||||
|
||||
type AlertManagerAlert struct {
|
||||
|
@ -74,17 +74,17 @@ func (a *AlertManagerTime) UnmarshalJSON(jsonRepr []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func NewAlertmanager(hookURL string, proxyURL string, certPool *x509.CertPool, token string) (*Alertmanager, error) {
|
||||
func NewAlertmanager(hookURL string, proxyURL string, tlsConfig *tls.Config, token string) (*Alertmanager, error) {
|
||||
_, err := url.ParseRequestURI(hookURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid Alertmanager URL %s: '%w'", hookURL, err)
|
||||
}
|
||||
|
||||
return &Alertmanager{
|
||||
URL: hookURL,
|
||||
ProxyURL: proxyURL,
|
||||
CertPool: certPool,
|
||||
Token: token,
|
||||
URL: hookURL,
|
||||
ProxyURL: proxyURL,
|
||||
Token: token,
|
||||
TLSConfig: tlsConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -137,15 +137,22 @@ func (s *Alertmanager) Post(ctx context.Context, event eventv1.Event) error {
|
|||
},
|
||||
}
|
||||
|
||||
var opts []requestOptFunc
|
||||
if s.Token != "" {
|
||||
opts = append(opts, func(request *retryablehttp.Request) {
|
||||
request.Header.Add("Authorization", "Bearer "+s.Token)
|
||||
})
|
||||
var opts []postOption
|
||||
if s.ProxyURL != "" {
|
||||
opts = append(opts, withProxy(s.ProxyURL))
|
||||
}
|
||||
err := postMessage(ctx, s.URL, s.ProxyURL, s.CertPool, payload, opts...)
|
||||
if err != nil {
|
||||
if s.TLSConfig != nil {
|
||||
opts = append(opts, withTLSConfig(s.TLSConfig))
|
||||
}
|
||||
if s.Token != "" {
|
||||
opts = append(opts, withRequestModifier(func(request *retryablehttp.Request) {
|
||||
request.Header.Add("Authorization", "Bearer "+s.Token)
|
||||
}))
|
||||
}
|
||||
|
||||
if err := postMessage(ctx, s.URL, payload, opts...); err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -43,10 +43,10 @@ func Fuzz_AlertManager(f *testing.F) {
|
|||
}))
|
||||
defer ts.Close()
|
||||
|
||||
var cert x509.CertPool
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&cert)
|
||||
var tlsConfig tls.Config
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&tlsConfig)
|
||||
|
||||
alertmanager, err := NewAlertmanager(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", &cert, "")
|
||||
alertmanager, err := NewAlertmanager(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", &tlsConfig, "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -40,14 +40,14 @@ type azureDevOpsClient interface {
|
|||
|
||||
// AzureDevOps is an Azure DevOps notifier.
|
||||
type AzureDevOps struct {
|
||||
Project string
|
||||
Repo string
|
||||
ProviderUID string
|
||||
Client azureDevOpsClient
|
||||
Project string
|
||||
Repo string
|
||||
CommitStatus string
|
||||
Client azureDevOpsClient
|
||||
}
|
||||
|
||||
// NewAzureDevOps creates and returns a new AzureDevOps notifier.
|
||||
func NewAzureDevOps(providerUID string, addr string, token string, certPool *x509.CertPool) (*AzureDevOps, error) {
|
||||
func NewAzureDevOps(commitStatus string, addr string, token string, certPool *x509.CertPool) (*AzureDevOps, error) {
|
||||
if len(token) == 0 {
|
||||
return nil, errors.New("azure devops token cannot be empty")
|
||||
}
|
||||
|
@ -57,6 +57,11 @@ func NewAzureDevOps(providerUID string, addr string, token string, certPool *x50
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// this should never happen
|
||||
if commitStatus == "" {
|
||||
return nil, errors.New("commit status cannot be empty")
|
||||
}
|
||||
|
||||
comp := strings.Split(id, "/")
|
||||
if len(comp) != 4 {
|
||||
return nil, fmt.Errorf("invalid repository id %q", id)
|
||||
|
@ -77,10 +82,10 @@ func NewAzureDevOps(providerUID string, addr string, token string, certPool *x50
|
|||
Client: *client,
|
||||
}
|
||||
return &AzureDevOps{
|
||||
Project: proj,
|
||||
Repo: repo,
|
||||
ProviderUID: providerUID,
|
||||
Client: gitClient,
|
||||
Project: proj,
|
||||
Repo: repo,
|
||||
CommitStatus: commitStatus,
|
||||
Client: gitClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -108,7 +113,7 @@ func (a AzureDevOps) Post(ctx context.Context, event eventv1.Event) error {
|
|||
g := commitStatusGenre(event)
|
||||
|
||||
_, desc := formatNameAndDescription(event)
|
||||
id := generateCommitStatusID(a.ProviderUID, event)
|
||||
id := a.CommitStatus
|
||||
createArgs := git.CreateCommitStatusArgs{
|
||||
Project: &a.Project,
|
||||
RepositoryId: &a.Repo,
|
||||
|
|
|
@ -33,12 +33,12 @@ import (
|
|||
const apiLocations = `{"count":0,"value":[{"area":"","id":"428dd4fb-fda5-4722-af02-9313b80305da","routeTemplate":"","resourceName":"","maxVersion":"6.0","minVersion":"5.0","releasedVersion":"6.0"}]}`
|
||||
|
||||
func Fuzz_AzureDevOps(f *testing.F) {
|
||||
f.Add("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "alakazam", "org/proj/_git/repo", "revision/dsa123a", "error", "", []byte{}, []byte(`{"count":1,"value":[{"state":"error","description":"","context":{"genre":"fluxcd","name":"/"}}]}`))
|
||||
f.Add("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "alakazam", "org/proj/_git/repo", "revision/dsa123a", "info", "", []byte{}, []byte(`{"count":1,"value":[{"state":"info","description":"","context":{"genre":"fluxcd","name":"/"}}]}`))
|
||||
f.Add("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "alakazam", "org/proj/_git/repo", "revision/dsa123a", "info", "", []byte{}, []byte(`{"count":0,"value":[]}`))
|
||||
f.Add("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "alakazam", "org/proj/_git/repo", "", "", "Progressing", []byte{}, []byte{})
|
||||
f.Add("kustomization/gitops-system/0c9c2e41", "alakazam", "org/proj/_git/repo", "revision/dsa123a", "error", "", []byte{}, []byte(`{"count":1,"value":[{"state":"error","description":"","context":{"genre":"fluxcd","name":"/"}}]}`))
|
||||
f.Add("kustomization/gitops-system/0c9c2e41", "alakazam", "org/proj/_git/repo", "revision/dsa123a", "info", "", []byte{}, []byte(`{"count":1,"value":[{"state":"info","description":"","context":{"genre":"fluxcd","name":"/"}}]}`))
|
||||
f.Add("kustomization/gitops-system/0c9c2e41", "alakazam", "org/proj/_git/repo", "revision/dsa123a", "info", "", []byte{}, []byte(`{"count":0,"value":[]}`))
|
||||
f.Add("kustomization/gitops-system/0c9c2e41", "alakazam", "org/proj/_git/repo", "", "", "Progressing", []byte{}, []byte{})
|
||||
|
||||
f.Fuzz(func(t *testing.T, uuid, token, urlSuffix, revision, severity, reason string, seed, response []byte) {
|
||||
f.Fuzz(func(t *testing.T, commitStatus, token, urlSuffix, revision, severity, reason string, seed, response []byte) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if strings.HasSuffix(r.URL.Path, "_apis") {
|
||||
w.Write([]byte(apiLocations))
|
||||
|
@ -54,7 +54,7 @@ func Fuzz_AzureDevOps(f *testing.F) {
|
|||
var cert x509.CertPool
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&cert)
|
||||
|
||||
azureDevOps, err := NewAzureDevOps(uuid, fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, &cert)
|
||||
azureDevOps, err := NewAzureDevOps(commitStatus, fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, &cert)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -27,19 +27,24 @@ import (
|
|||
)
|
||||
|
||||
func TestNewAzureDevOpsBasic(t *testing.T) {
|
||||
a, err := NewAzureDevOps("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://dev.azure.com/foo/bar/_git/baz", "foo", nil)
|
||||
a, err := NewAzureDevOps("kustomization/gitops-system/0c9c2e41", "https://dev.azure.com/foo/bar/_git/baz", "foo", nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, a.Project, "bar")
|
||||
assert.Equal(t, a.Repo, "baz")
|
||||
}
|
||||
|
||||
func TestNewAzureDevOpsInvalidUrl(t *testing.T) {
|
||||
_, err := NewAzureDevOps("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://dev.azure.com/foo/bar/baz", "foo", nil)
|
||||
_, err := NewAzureDevOps("kustomization/gitops-system/0c9c2e41", "https://dev.azure.com/foo/bar/baz", "foo", nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestNewAzureDevOpsMissingToken(t *testing.T) {
|
||||
_, err := NewAzureDevOps("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://dev.azure.com/foo/bar/baz", "", nil)
|
||||
_, err := NewAzureDevOps("kustomization/gitops-system/0c9c2e41", "https://dev.azure.com/foo/bar/baz", "", nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestNewAzureDevOpsEmptyCommitStatus(t *testing.T) {
|
||||
_, err := NewAzureDevOps("", "https://dev.azure.com/foo/bar/_git/baz", "foo", nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
|
@ -160,7 +165,7 @@ func TestAzureDevOps_Post(t *testing.T) {
|
|||
|
||||
for _, tt := range postTests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a, err := NewAzureDevOps("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com/foo/bar/_git/baz", "foo", nil)
|
||||
a, err := NewAzureDevOps("kustomization/gitops-system/0c9c2e41", "https://example.com/foo/bar/_git/baz", "foo", nil)
|
||||
fakeClient := &fakeDevOpsClient{}
|
||||
a.Client = fakeClient
|
||||
assert.Nil(t, err)
|
||||
|
|
|
@ -17,10 +17,20 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/Azure/azure-amqp-common-go/v4/auth"
|
||||
azauth "github.com/Azure/azure-amqp-common-go/v4/auth"
|
||||
eventhub "github.com/Azure/azure-event-hubs-go/v3"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
|
||||
"github.com/fluxcd/pkg/auth"
|
||||
"github.com/fluxcd/pkg/auth/azure"
|
||||
"github.com/fluxcd/pkg/cache"
|
||||
|
||||
"github.com/fluxcd/notification-controller/api/v1beta3"
|
||||
)
|
||||
|
||||
// AzureEventHub holds the eventhub client
|
||||
|
@ -29,21 +39,33 @@ type AzureEventHub struct {
|
|||
}
|
||||
|
||||
// NewAzureEventHub creates a eventhub client
|
||||
func NewAzureEventHub(endpointURL, token, eventHubNamespace string) (*AzureEventHub, error) {
|
||||
func NewAzureEventHub(ctx context.Context, endpointURL, token, eventHubNamespace, proxy,
|
||||
serviceAccountName, providerName, providerNamespace string, tokenClient client.Client,
|
||||
tokenCache *cache.TokenCache) (*AzureEventHub, error) {
|
||||
var hub *eventhub.Hub
|
||||
var err error
|
||||
|
||||
// token should only be defined if JWT is used
|
||||
if token != "" {
|
||||
hub, err = newJWTHub(endpointURL, token, eventHubNamespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create a eventhub using JWT %v", err)
|
||||
}
|
||||
} else {
|
||||
if err := validateAuthOptions(endpointURL, token, serviceAccountName); err != nil {
|
||||
return nil, fmt.Errorf("invalid authentication options: %v", err)
|
||||
}
|
||||
|
||||
if isSASAuth(endpointURL) {
|
||||
hub, err = newSASHub(endpointURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create a eventhub using SAS %v", err)
|
||||
}
|
||||
} else {
|
||||
// if token doesn't exist, try to create a new token using managed identity
|
||||
if token == "" {
|
||||
token, err = newManagedIdentityToken(ctx, proxy, serviceAccountName, providerName, providerNamespace, tokenClient, tokenCache)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create a eventhub using managed identity %v", err)
|
||||
}
|
||||
}
|
||||
hub, err = newJWTHub(endpointURL, token, eventHubNamespace)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create a eventhub using authentication token %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &AzureEventHub{
|
||||
|
@ -88,9 +110,9 @@ func NewJWTProvider(jwt string) *PureJWT {
|
|||
}
|
||||
|
||||
// GetToken uses a JWT token, we assume that we will get new tokens when needed, thus no Expiry defined
|
||||
func (j *PureJWT) GetToken(uri string) (*auth.Token, error) {
|
||||
return &auth.Token{
|
||||
TokenType: auth.CBSTokenTypeJWT,
|
||||
func (j *PureJWT) GetToken(uri string) (*azauth.Token, error) {
|
||||
return &azauth.Token{
|
||||
TokenType: azauth.CBSTokenTypeJWT,
|
||||
Token: j.jwt,
|
||||
Expiry: "",
|
||||
}, nil
|
||||
|
@ -116,3 +138,75 @@ func newSASHub(address string) (*eventhub.Hub, error) {
|
|||
|
||||
return hub, nil
|
||||
}
|
||||
|
||||
// newManagedIdentityToken is used to attempt credential-free authentication.
|
||||
func newManagedIdentityToken(ctx context.Context, proxy, serviceAccountName, providerName,
|
||||
providerNamespace string, tokenClient client.Client, tokenCache *cache.TokenCache) (string, error) {
|
||||
opts := []auth.Option{auth.WithScopes(azure.ScopeEventHubs)}
|
||||
if proxy != "" {
|
||||
proxyURL, err := url.Parse(proxy)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error parsing proxy URL : %w", err)
|
||||
}
|
||||
opts = append(opts, auth.WithProxyURL(*proxyURL))
|
||||
}
|
||||
|
||||
if serviceAccountName != "" {
|
||||
serviceAccount := types.NamespacedName{
|
||||
Name: serviceAccountName,
|
||||
Namespace: providerNamespace,
|
||||
}
|
||||
opts = append(opts, auth.WithServiceAccount(serviceAccount, tokenClient))
|
||||
}
|
||||
|
||||
if tokenCache != nil {
|
||||
involvedObject := cache.InvolvedObject{
|
||||
Kind: v1beta3.ProviderKind,
|
||||
Name: providerName,
|
||||
Namespace: providerNamespace,
|
||||
Operation: OperationPost,
|
||||
}
|
||||
opts = append(opts, auth.WithCache(*tokenCache, involvedObject))
|
||||
}
|
||||
|
||||
token, err := auth.GetAccessToken(ctx, azure.Provider{}, opts...)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get token for azure event hub: %w", err)
|
||||
}
|
||||
|
||||
return token.(*azure.Token).AccessToken.Token, nil
|
||||
}
|
||||
|
||||
// validateAuthOptions checks if the authentication options are valid
|
||||
func validateAuthOptions(endpointURL, token, serviceAccountName string) error {
|
||||
if endpointURL == "" {
|
||||
return fmt.Errorf("endpoint URL cannot be empty")
|
||||
}
|
||||
|
||||
if isSASAuth(endpointURL) {
|
||||
if err := validateSASAuth(token, serviceAccountName); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if serviceAccountName != "" && token != "" {
|
||||
return fmt.Errorf("serviceAccountName and jwt token authentication cannot be set at the same time")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// isSASAuth checks if the endpoint URL contains SAS authentication parameters
|
||||
func isSASAuth(endpointURL string) bool {
|
||||
return strings.Contains(endpointURL, "SharedAccessKey")
|
||||
}
|
||||
|
||||
// validateSASAuth checks if SAS authentication is used correctly
|
||||
func validateSASAuth(token, serviceAccountName string) error {
|
||||
if serviceAccountName != "" {
|
||||
return fmt.Errorf("serviceAccountName and SAS authentication cannot be set at the same time")
|
||||
}
|
||||
if token != "" {
|
||||
return fmt.Errorf("jwt token and SAS authentication cannot be set at the same time")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
Copyright 2021 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 notifier
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewAzureEventHub(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
endpointURL string
|
||||
token string
|
||||
eventHubNamespace string
|
||||
serviceAccountName string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "JWT Authentication",
|
||||
endpointURL: "azure-nc-eventhub",
|
||||
token: "jwt-token",
|
||||
eventHubNamespace: "namespace",
|
||||
},
|
||||
{
|
||||
name: "SAS Authentication",
|
||||
endpointURL: "Endpoint=sb://example.com/;SharedAccessKeyName=keyName;SharedAccessKey=key;EntityPath=eventhub",
|
||||
token: "",
|
||||
eventHubNamespace: "namespace",
|
||||
},
|
||||
{
|
||||
name: "Default Azure Credential",
|
||||
endpointURL: "azure-nc-eventhub",
|
||||
token: "",
|
||||
eventHubNamespace: "namespace",
|
||||
err: errors.New("failed to create a eventhub using managed identity failed to get token for azure event hub: failed to create provider access token for the controller: ManagedIdentityCredential: failed to authenticate a system assigned identity. The endpoint responded with {\"error\":\"invalid_request\",\"error_description\":\"Identity not found\"}"),
|
||||
},
|
||||
{
|
||||
name: "SAS auth with serviceAccountName set",
|
||||
endpointURL: "Endpoint=sb://example.com/;SharedAccessKeyName=keyName;SharedAccessKey=key;EntityPath=eventhub",
|
||||
token: "",
|
||||
serviceAccountName: "test-service-account",
|
||||
eventHubNamespace: "namespace",
|
||||
err: errors.New("invalid authentication options: serviceAccountName and SAS authentication cannot be set at the same time"),
|
||||
},
|
||||
{
|
||||
name: "SAS auth with token set",
|
||||
endpointURL: "Endpoint=sb://example.com/;SharedAccessKeyName=keyName;SharedAccessKey=key;EntityPath=eventhub",
|
||||
token: "test-token",
|
||||
eventHubNamespace: "namespace",
|
||||
err: errors.New("invalid authentication options: jwt token and SAS authentication cannot be set at the same time"),
|
||||
},
|
||||
{
|
||||
name: "token auth with serviceAccountName set",
|
||||
endpointURL: "azure-nc-eventhub",
|
||||
token: "test-token",
|
||||
serviceAccountName: "test-service-account",
|
||||
eventHubNamespace: "namespace",
|
||||
err: errors.New("invalid authentication options: serviceAccountName and jwt token authentication cannot be set at the same time"),
|
||||
},
|
||||
{
|
||||
name: "empty endpoint URL",
|
||||
endpointURL: "",
|
||||
token: "test-token",
|
||||
eventHubNamespace: "namespace",
|
||||
err: errors.New("invalid authentication options: endpoint URL cannot be empty"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
client, err := NewAzureEventHub(context.TODO(), tt.endpointURL, tt.token, tt.eventHubNamespace, "", tt.serviceAccountName, "", "", nil, nil)
|
||||
if tt.err != nil {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.err, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, client)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -34,18 +34,23 @@ import (
|
|||
|
||||
// Bitbucket is a Bitbucket Server notifier.
|
||||
type Bitbucket struct {
|
||||
Owner string
|
||||
Repo string
|
||||
ProviderUID string
|
||||
Client *bitbucket.Client
|
||||
Owner string
|
||||
Repo string
|
||||
CommitStatus string
|
||||
Client *bitbucket.Client
|
||||
}
|
||||
|
||||
// NewBitbucket creates and returns a new Bitbucket notifier.
|
||||
func NewBitbucket(providerUID string, addr string, token string, certPool *x509.CertPool) (*Bitbucket, error) {
|
||||
func NewBitbucket(commitStatus string, addr string, token string, certPool *x509.CertPool) (*Bitbucket, error) {
|
||||
if len(token) == 0 {
|
||||
return nil, errors.New("bitbucket token cannot be empty")
|
||||
}
|
||||
|
||||
// this should never happen
|
||||
if commitStatus == "" {
|
||||
return nil, errors.New("commit status cannot be empty")
|
||||
}
|
||||
|
||||
_, id, err := parseGitAddress(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -77,10 +82,10 @@ func NewBitbucket(providerUID string, addr string, token string, certPool *x509.
|
|||
}
|
||||
|
||||
return &Bitbucket{
|
||||
Owner: owner,
|
||||
Repo: repo,
|
||||
ProviderUID: providerUID,
|
||||
Client: client,
|
||||
Owner: owner,
|
||||
Repo: repo,
|
||||
CommitStatus: commitStatus,
|
||||
Client: client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -105,7 +110,7 @@ func (b Bitbucket) Post(ctx context.Context, event eventv1.Event) error {
|
|||
}
|
||||
|
||||
name, desc := formatNameAndDescription(event)
|
||||
id := generateCommitStatusID(b.ProviderUID, event)
|
||||
id := b.CommitStatus
|
||||
// key has a limitation of 40 characters in bitbucket api
|
||||
key := sha1String(id)
|
||||
|
||||
|
|
|
@ -31,10 +31,10 @@ import (
|
|||
)
|
||||
|
||||
func Fuzz_Bitbucket(f *testing.F) {
|
||||
f.Add("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "user:pass", "org/repo", "revision/dsa123a", "info", []byte{}, []byte(`{"state":"SUCCESSFUL","description":"","key":"","name":"","url":""}`))
|
||||
f.Add("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "user:pass", "org/repo", "revision/dsa123a", "error", []byte{}, []byte(`{}`))
|
||||
f.Add("kustomization/gitops-system/0c9c2e41", "user:pass", "org/repo", "revision/dsa123a", "info", []byte{}, []byte(`{"state":"SUCCESSFUL","description":"","key":"","name":"","url":""}`))
|
||||
f.Add("kustomization/gitops-system/0c9c2e41", "user:pass", "org/repo", "revision/dsa123a", "error", []byte{}, []byte(`{}`))
|
||||
|
||||
f.Fuzz(func(t *testing.T, uuid, token, urlSuffix, revision, severity string, seed, response []byte) {
|
||||
f.Fuzz(func(t *testing.T, commitStatus, token, urlSuffix, revision, severity string, seed, response []byte) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
io.Copy(io.Discard, r.Body)
|
||||
w.Write(response)
|
||||
|
@ -45,7 +45,7 @@ func Fuzz_Bitbucket(f *testing.F) {
|
|||
var cert x509.CertPool
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&cert)
|
||||
|
||||
bitbucket, err := NewBitbucket(uuid, fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, &cert)
|
||||
bitbucket, err := NewBitbucket(commitStatus, fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, &cert)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -23,18 +23,24 @@ import (
|
|||
)
|
||||
|
||||
func TestNewBitbucketBasic(t *testing.T) {
|
||||
b, err := NewBitbucket("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://bitbucket.org/foo/bar", "foo:bar", nil)
|
||||
b, err := NewBitbucket("kustomization/gitops-system/0c9c2e41", "https://bitbucket.org/foo/bar", "foo:bar", nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, b.Owner, "foo")
|
||||
assert.Equal(t, b.Repo, "bar")
|
||||
assert.Equal(t, b.CommitStatus, "kustomization/gitops-system/0c9c2e41")
|
||||
}
|
||||
|
||||
func TestNewBitbucketEmptyCommitStatus(t *testing.T) {
|
||||
_, err := NewBitbucket("", "https://bitbucket.org/foo/bar", "foo:bar", nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestNewBitbucketInvalidUrl(t *testing.T) {
|
||||
_, err := NewBitbucket("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://bitbucket.org/foo/bar/baz", "foo:bar", nil)
|
||||
_, err := NewBitbucket("kustomization/gitops-system/0c9c2e41", "https://bitbucket.org/foo/bar/baz", "foo:bar", nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestNewBitbucketInvalidToken(t *testing.T) {
|
||||
_, err := NewBitbucket("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://bitbucket.org/foo/bar", "bar", nil)
|
||||
_, err := NewBitbucket("kustomization/gitops-system/0c9c2e41", "https://bitbucket.org/foo/bar", "bar", nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ import (
|
|||
|
||||
// BitbucketServer is a notifier for BitBucket Server and Data Center.
|
||||
type BitbucketServer struct {
|
||||
ProviderUID string
|
||||
CommitStatus string
|
||||
Url *url.URL
|
||||
ProviderAddress string
|
||||
Username string
|
||||
|
@ -81,12 +81,17 @@ type bbServerBuildStatusSetRequest struct {
|
|||
}
|
||||
|
||||
// NewBitbucketServer creates and returns a new BitbucketServer notifier.
|
||||
func NewBitbucketServer(providerUID string, addr string, token string, certPool *x509.CertPool, username string, password string) (*BitbucketServer, error) {
|
||||
func NewBitbucketServer(commitStatus string, addr string, token string, certPool *x509.CertPool, username string, password string) (*BitbucketServer, error) {
|
||||
url, err := parseBitbucketServerGitAddress(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// this should never happen
|
||||
if commitStatus == "" {
|
||||
return nil, errors.New("commit status cannot be empty")
|
||||
}
|
||||
|
||||
httpClient := retryablehttp.NewClient()
|
||||
if certPool != nil {
|
||||
httpClient.HTTPClient.Transport = &http.Transport{
|
||||
|
@ -107,7 +112,7 @@ func NewBitbucketServer(providerUID string, addr string, token string, certPool
|
|||
}
|
||||
|
||||
return &BitbucketServer{
|
||||
ProviderUID: providerUID,
|
||||
CommitStatus: commitStatus,
|
||||
Url: url,
|
||||
ProviderAddress: addr,
|
||||
Token: token,
|
||||
|
@ -138,7 +143,7 @@ func (b BitbucketServer) Post(ctx context.Context, event eventv1.Event) error {
|
|||
|
||||
name, desc := formatNameAndDescription(event)
|
||||
name = name + " [" + desc + "]" //Bitbucket server displays this data on browser. Thus adding description here.
|
||||
id := generateCommitStatusID(b.ProviderUID, event)
|
||||
id := b.CommitStatus
|
||||
// key has a limitation of 40 characters in bitbucket api
|
||||
key := sha1String(id)
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ import (
|
|||
)
|
||||
|
||||
func TestNewBitbucketServerBasicNoContext(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
b, err := NewBitbucketServer("kustomization/gitops-system/0c9c2e41", "https://example.com:7990/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, b.Username, "dummyuser")
|
||||
assert.Equal(t, b.Password, "testpassword")
|
||||
|
@ -44,7 +44,7 @@ func TestNewBitbucketServerBasicNoContext(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNewBitbucketServerBasicWithContext(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/context/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
b, err := NewBitbucketServer("kustomization/gitops-system/0c9c2e41", "https://example.com:7990/context/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, b.Username, "dummyuser")
|
||||
assert.Equal(t, b.Password, "testpassword")
|
||||
|
@ -53,72 +53,72 @@ func TestNewBitbucketServerBasicWithContext(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestBitbucketServerApiPathNoContext(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
b, err := NewBitbucketServer("kustomization/gitops-system/0c9c2e41", "https://example.com:7990/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
assert.Nil(t, err)
|
||||
u := b.Url.JoinPath(b.createBuildPath("00151b98e303e19610378e6f1c49e31e5e80cd3b")).String()
|
||||
assert.Equal(t, u, "https://example.com:7990/rest/api/latest/projects/projectfoo/repos/repobar/commits/00151b98e303e19610378e6f1c49e31e5e80cd3b/builds")
|
||||
}
|
||||
|
||||
func TestBitbucketServerApiPathOneWordContext(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/context1/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
b, err := NewBitbucketServer("kustomization/gitops-system/0c9c2e41", "https://example.com:7990/context1/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
assert.Nil(t, err)
|
||||
u := b.Url.JoinPath(b.createBuildPath("00151b98e303e19610378e6f1c49e31e5e80cd3b")).String()
|
||||
assert.Equal(t, u, "https://example.com:7990/context1/rest/api/latest/projects/projectfoo/repos/repobar/commits/00151b98e303e19610378e6f1c49e31e5e80cd3b/builds")
|
||||
}
|
||||
|
||||
func TestBitbucketServerApiPathMultipleWordContext(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/context1/context2/context3/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
b, err := NewBitbucketServer("kustomization/gitops-system/0c9c2e41", "https://example.com:7990/context1/context2/context3/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
assert.Nil(t, err)
|
||||
u := b.Url.JoinPath(b.createBuildPath("00151b98e303e19610378e6f1c49e31e5e80cd3b")).String()
|
||||
assert.Equal(t, u, "https://example.com:7990/context1/context2/context3/rest/api/latest/projects/projectfoo/repos/repobar/commits/00151b98e303e19610378e6f1c49e31e5e80cd3b/builds")
|
||||
}
|
||||
|
||||
func TestBitbucketServerApiPathOneWordScmInContext(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/scm/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
b, err := NewBitbucketServer("kustomization/gitops-system/0c9c2e41", "https://example.com:7990/scm/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
assert.Nil(t, err)
|
||||
u := b.Url.JoinPath(b.createBuildPath("00151b98e303e19610378e6f1c49e31e5e80cd3b")).String()
|
||||
assert.Equal(t, u, "https://example.com:7990/scm/rest/api/latest/projects/projectfoo/repos/repobar/commits/00151b98e303e19610378e6f1c49e31e5e80cd3b/builds")
|
||||
}
|
||||
|
||||
func TestBitbucketServerApiPathMultipleWordScmInContext(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/scm/context2/scm/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
b, err := NewBitbucketServer("kustomization/gitops-system/0c9c2e41", "https://example.com:7990/scm/context2/scm/scm/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
assert.Nil(t, err)
|
||||
u := b.Url.JoinPath(b.createBuildPath("00151b98e303e19610378e6f1c49e31e5e80cd3b")).String()
|
||||
assert.Equal(t, u, "https://example.com:7990/scm/context2/scm/rest/api/latest/projects/projectfoo/repos/repobar/commits/00151b98e303e19610378e6f1c49e31e5e80cd3b/builds")
|
||||
}
|
||||
|
||||
func TestBitbucketServerApiPathScmAlreadyRemovedInInput(t *testing.T) {
|
||||
_, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/context1/context2/context3/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
_, err := NewBitbucketServer("kustomization/gitops-system/0c9c2e41", "https://example.com:7990/context1/context2/context3/projectfoo/repobar.git", "", nil, "dummyuser", "testpassword")
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, err.Error(), "could not parse git address: supplied provider address is not http(s) git clone url")
|
||||
}
|
||||
|
||||
func TestBitbucketServerSshAddress(t *testing.T) {
|
||||
_, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "ssh://git@mybitbucket:2222/ap/fluxcd-sandbox.git", "", nil, "", "")
|
||||
_, err := NewBitbucketServer("kustomization/gitops-system/0c9c2e41", "ssh://git@mybitbucket:2222/ap/fluxcd-sandbox.git", "", nil, "", "")
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, err.Error(), "could not parse git address: unsupported scheme type in address: ssh. Must be http or https")
|
||||
}
|
||||
|
||||
func TestNewBitbucketServerToken(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/scm/projectfoo/repobar.git", "BBDC-ODIxODYxMzIyNzUyOttorMjO059P2rYTb6EH7mP", nil, "", "")
|
||||
b, err := NewBitbucketServer("kustomization/gitops-system/0c9c2e41", "https://example.com:7990/scm/projectfoo/repobar.git", "BBDC-ODIxODYxMzIyNzUyOttorMjO059P2rYTb6EH7mP", nil, "", "")
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, b.Token, "BBDC-ODIxODYxMzIyNzUyOttorMjO059P2rYTb6EH7mP")
|
||||
}
|
||||
|
||||
func TestNewBitbucketServerInvalidCreds(t *testing.T) {
|
||||
_, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/scm/projectfoo/repobar.git", "", nil, "", "")
|
||||
_, err := NewBitbucketServer("kustomization/gitops-system/0c9c2e41", "https://example.com:7990/scm/projectfoo/repobar.git", "", nil, "", "")
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, err.Error(), "invalid credentials, expected to be one of username/password or API Token")
|
||||
}
|
||||
|
||||
func TestNewBitbucketServerInvalidRepo(t *testing.T) {
|
||||
_, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/scm/projectfoo/repobar/invalid.git", "BBDC-ODIxODYxMzIyNzUyOttorMjO059P2rYTb6EH7mP", nil, "", "")
|
||||
_, err := NewBitbucketServer("kustomization/gitops-system/0c9c2e41", "https://example.com:7990/scm/projectfoo/repobar/invalid.git", "BBDC-ODIxODYxMzIyNzUyOttorMjO059P2rYTb6EH7mP", nil, "", "")
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, err.Error(), "could not parse git address: invalid repository id \"projectfoo/repobar/invalid\"")
|
||||
}
|
||||
|
||||
func TestPostBitbucketServerMissingRevision(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/scm/projectfoo/repobar.git", "BBDC-ODIxODYxMzIyNzUyOttorMjO059P2rYTb6EH7mP", nil, "", "")
|
||||
b, err := NewBitbucketServer("kustomization/gitops-system/0c9c2e41", "https://example.com:7990/scm/projectfoo/repobar.git", "BBDC-ODIxODYxMzIyNzUyOttorMjO059P2rYTb6EH7mP", nil, "", "")
|
||||
assert.Nil(t, err)
|
||||
|
||||
//Validate missing revision
|
||||
|
@ -129,8 +129,14 @@ func TestPostBitbucketServerMissingRevision(t *testing.T) {
|
|||
assert.Equal(t, err.Error(), "missing revision metadata")
|
||||
}
|
||||
|
||||
func TestNewBitbucketServerEmptyCommitStatus(t *testing.T) {
|
||||
_, err := NewBitbucketServer("", "https://example.com:7990/scm/projectfoo/repobar.git", "BBDC-ODIxODYxMzIyNzUyOttorMjO059P2rYTb6EH7mP", nil, "", "")
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, err.Error(), "commit status cannot be empty")
|
||||
}
|
||||
|
||||
func TestPostBitbucketServerBadCommitHash(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/scm/projectfoo/repobar.git", "BBDC-ODIxODYxMzIyNzUyOttorMjO059P2rYTb6EH7mP", nil, "", "")
|
||||
b, err := NewBitbucketServer("kustomization/gitops-system/0c9c2e41", "https://example.com:7990/scm/projectfoo/repobar.git", "BBDC-ODIxODYxMzIyNzUyOttorMjO059P2rYTb6EH7mP", nil, "", "")
|
||||
assert.Nil(t, err)
|
||||
|
||||
//Validate extract commit hash
|
||||
|
@ -143,7 +149,7 @@ func TestPostBitbucketServerBadCommitHash(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPostBitbucketServerBadBitbucketState(t *testing.T) {
|
||||
b, err := NewBitbucketServer("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com:7990/scm/projectfoo/repobar.git", "BBDC-ODIxODYxMzIyNzUyOttorMjO059P2rYTb6EH7mP", nil, "", "")
|
||||
b, err := NewBitbucketServer("kustomization/gitops-system/0c9c2e41", "https://example.com:7990/scm/projectfoo/repobar.git", "BBDC-ODIxODYxMzIyNzUyOttorMjO059P2rYTb6EH7mP", nil, "", "")
|
||||
assert.Nil(t, err)
|
||||
|
||||
//Validate conversion to bitbucket state
|
||||
|
@ -182,14 +188,14 @@ func TestBitBucketServerPostValidateRequest(t *testing.T) {
|
|||
password string
|
||||
token string
|
||||
event eventv1.Event
|
||||
provideruid string
|
||||
commitStatus string
|
||||
key string
|
||||
uriHash string
|
||||
}{
|
||||
{
|
||||
name: "Validate Token Auth ",
|
||||
token: "goodtoken",
|
||||
provideruid: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
name: "Validate Token Auth ",
|
||||
token: "goodtoken",
|
||||
commitStatus: "kustomization/gitops-system/0c9c2e41",
|
||||
headers: map[string]string{
|
||||
"Authorization": "Bearer goodtoken",
|
||||
"x-atlassian-token": "no-check",
|
||||
|
@ -198,15 +204,13 @@ func TestBitBucketServerPostValidateRequest(t *testing.T) {
|
|||
event: generateTestEventKustomization("info", map[string]string{
|
||||
eventv1.MetaRevisionKey: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
}),
|
||||
key: sha1String(generateCommitStatusID("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", generateTestEventKustomization("info", map[string]string{
|
||||
eventv1.MetaRevisionKey: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
}))),
|
||||
key: sha1String("kustomization/gitops-system/0c9c2e41"),
|
||||
uriHash: "5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
},
|
||||
{
|
||||
name: "Event with origin revision",
|
||||
token: "goodtoken",
|
||||
provideruid: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
name: "Event with origin revision",
|
||||
token: "goodtoken",
|
||||
commitStatus: "kustomization/gitops-system/0c9c2e41",
|
||||
headers: map[string]string{
|
||||
"Authorization": "Bearer goodtoken",
|
||||
"x-atlassian-token": "no-check",
|
||||
|
@ -216,16 +220,14 @@ func TestBitBucketServerPostValidateRequest(t *testing.T) {
|
|||
eventv1.MetaRevisionKey: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
eventv1.MetaOriginRevisionKey: "main@sha1:e7c17dd8b8384bbc84b7e7385394cb7f48332b2d",
|
||||
}),
|
||||
key: sha1String(generateCommitStatusID("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", generateTestEventKustomization("info", map[string]string{
|
||||
eventv1.MetaRevisionKey: "main@sha1:e7c17dd8b8384bbc84b7e7385394cb7f48332b2d",
|
||||
}))),
|
||||
key: sha1String("kustomization/gitops-system/0c9c2e41"),
|
||||
uriHash: "e7c17dd8b8384bbc84b7e7385394cb7f48332b2d",
|
||||
},
|
||||
{
|
||||
name: "Validate Basic Auth and Post State=Successful",
|
||||
username: "hello",
|
||||
password: "password",
|
||||
provideruid: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
name: "Validate Basic Auth and Post State=Successful",
|
||||
username: "hello",
|
||||
password: "password",
|
||||
commitStatus: "kustomization/gitops-system/0c9c2e41",
|
||||
headers: map[string]string{
|
||||
"Authorization": "Basic " + base64.StdEncoding.EncodeToString([]byte("hello"+":"+"password")),
|
||||
"x-atlassian-token": "no-check",
|
||||
|
@ -234,16 +236,14 @@ func TestBitBucketServerPostValidateRequest(t *testing.T) {
|
|||
event: generateTestEventKustomization("info", map[string]string{
|
||||
eventv1.MetaRevisionKey: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
}),
|
||||
key: sha1String(generateCommitStatusID("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", generateTestEventKustomization("info", map[string]string{
|
||||
eventv1.MetaRevisionKey: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
}))),
|
||||
key: sha1String("kustomization/gitops-system/0c9c2e41"),
|
||||
uriHash: "5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
},
|
||||
{
|
||||
name: "Validate Post State=Failed",
|
||||
username: "hello",
|
||||
password: "password",
|
||||
provideruid: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
name: "Validate Post State=Failed",
|
||||
username: "hello",
|
||||
password: "password",
|
||||
commitStatus: "kustomization/gitops-system/0c9c2e41",
|
||||
headers: map[string]string{
|
||||
"Authorization": "Basic " + base64.StdEncoding.EncodeToString([]byte("hello"+":"+"password")),
|
||||
"x-atlassian-token": "no-check",
|
||||
|
@ -252,9 +252,7 @@ func TestBitBucketServerPostValidateRequest(t *testing.T) {
|
|||
event: generateTestEventKustomization("error", map[string]string{
|
||||
eventv1.MetaRevisionKey: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
}),
|
||||
key: sha1String(generateCommitStatusID("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", generateTestEventKustomization("error", map[string]string{
|
||||
eventv1.MetaRevisionKey: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
}))),
|
||||
key: sha1String("kustomization/gitops-system/0c9c2e41"),
|
||||
uriHash: "5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
},
|
||||
{
|
||||
|
@ -263,7 +261,7 @@ func TestBitBucketServerPostValidateRequest(t *testing.T) {
|
|||
errorString: "could not get existing commit status: could not unmarshal json response body for duplicate commit status: unexpected end of JSON input",
|
||||
username: "hello",
|
||||
password: "password",
|
||||
provideruid: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
commitStatus: "kustomization/gitops-system/0c9c2e41",
|
||||
headers: map[string]string{
|
||||
"Authorization": "Basic " + base64.StdEncoding.EncodeToString([]byte("hello"+":"+"password")),
|
||||
"x-atlassian-token": "no-check",
|
||||
|
@ -272,9 +270,7 @@ func TestBitBucketServerPostValidateRequest(t *testing.T) {
|
|||
event: generateTestEventKustomization("error", map[string]string{
|
||||
eventv1.MetaRevisionKey: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
}),
|
||||
key: sha1String(generateCommitStatusID("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", generateTestEventKustomization("error", map[string]string{
|
||||
eventv1.MetaRevisionKey: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
}))),
|
||||
key: sha1String("kustomization/gitops-system/0c9c2e41"),
|
||||
uriHash: "5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
},
|
||||
{
|
||||
|
@ -283,7 +279,7 @@ func TestBitBucketServerPostValidateRequest(t *testing.T) {
|
|||
errorString: "could not get existing commit status: failed api call to check duplicate commit status: 400 - Bad Request",
|
||||
username: "hello",
|
||||
password: "password",
|
||||
provideruid: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
commitStatus: "kustomization/gitops-system/0c9c2e41",
|
||||
headers: map[string]string{
|
||||
"Authorization": "Basic " + base64.StdEncoding.EncodeToString([]byte("hello"+":"+"password")),
|
||||
"x-atlassian-token": "no-check",
|
||||
|
@ -292,9 +288,7 @@ func TestBitBucketServerPostValidateRequest(t *testing.T) {
|
|||
event: generateTestEventKustomization("error", map[string]string{
|
||||
eventv1.MetaRevisionKey: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
}),
|
||||
key: sha1String(generateCommitStatusID("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", generateTestEventKustomization("error", map[string]string{
|
||||
eventv1.MetaRevisionKey: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
}))),
|
||||
key: sha1String("kustomization/gitops-system/0c9c2e41"),
|
||||
uriHash: "5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
},
|
||||
{
|
||||
|
@ -303,7 +297,7 @@ func TestBitBucketServerPostValidateRequest(t *testing.T) {
|
|||
errorString: "could not post build status: could not post build commit status: 401 - Unauthorized",
|
||||
username: "hello",
|
||||
password: "password",
|
||||
provideruid: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
commitStatus: "kustomization/gitops-system/0c9c2e41",
|
||||
headers: map[string]string{
|
||||
"Authorization": "Basic " + base64.StdEncoding.EncodeToString([]byte("hello"+":"+"password")),
|
||||
"x-atlassian-token": "no-check",
|
||||
|
@ -312,16 +306,14 @@ func TestBitBucketServerPostValidateRequest(t *testing.T) {
|
|||
event: generateTestEventKustomization("error", map[string]string{
|
||||
eventv1.MetaRevisionKey: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
}),
|
||||
key: sha1String(generateCommitStatusID("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", generateTestEventKustomization("error", map[string]string{
|
||||
eventv1.MetaRevisionKey: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
}))),
|
||||
key: sha1String("kustomization/gitops-system/0c9c2e41"),
|
||||
uriHash: "5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
},
|
||||
{
|
||||
name: "Validate duplicate commit status successful match",
|
||||
username: "hello",
|
||||
password: "password",
|
||||
provideruid: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
name: "Validate duplicate commit status successful match",
|
||||
username: "hello",
|
||||
password: "password",
|
||||
commitStatus: "kustomization/gitops-system/0c9c2e41",
|
||||
headers: map[string]string{
|
||||
"Authorization": "Basic " + base64.StdEncoding.EncodeToString([]byte("hello"+":"+"password")),
|
||||
"x-atlassian-token": "no-check",
|
||||
|
@ -330,9 +322,7 @@ func TestBitBucketServerPostValidateRequest(t *testing.T) {
|
|||
event: generateTestEventKustomization("info", map[string]string{
|
||||
eventv1.MetaRevisionKey: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
}),
|
||||
key: sha1String(generateCommitStatusID("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", generateTestEventKustomization("info", map[string]string{
|
||||
eventv1.MetaRevisionKey: "main@sha1:5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
}))),
|
||||
key: sha1String("kustomization/gitops-system/0c9c2e41"),
|
||||
uriHash: "5394cb7f48332b2de7c17dd8b8384bbc84b7e738",
|
||||
},
|
||||
}
|
||||
|
@ -367,7 +357,7 @@ func TestBitBucketServerPostValidateRequest(t *testing.T) {
|
|||
jsondata, _ := json.Marshal(&bbServerBuildStatus{
|
||||
Name: name,
|
||||
Description: desc,
|
||||
Key: sha1String(generateCommitStatusID(tt.provideruid, tt.event)),
|
||||
Key: sha1String(tt.commitStatus),
|
||||
State: "SUCCESSFUL",
|
||||
Url: "https://example.com:7990/scm/projectfoo/repobar.git",
|
||||
})
|
||||
|
@ -450,7 +440,7 @@ func TestBitBucketServerPostValidateRequest(t *testing.T) {
|
|||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
c, err := NewBitbucketServer(tt.provideruid, ts.URL+"/scm/projectfoo/repobar.git", tt.token, nil, tt.username, tt.password)
|
||||
c, err := NewBitbucketServer(tt.commitStatus, ts.URL+"/scm/projectfoo/repobar.git", tt.token, nil, tt.username, tt.password)
|
||||
require.NoError(t, err)
|
||||
err = c.Post(context.TODO(), tt.event)
|
||||
if tt.testFailReason == "" {
|
||||
|
|
|
@ -19,55 +19,120 @@ package notifier
|
|||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
)
|
||||
|
||||
type requestOptFunc func(*retryablehttp.Request)
|
||||
type postOptions struct {
|
||||
proxy string
|
||||
tlsConfig *tls.Config
|
||||
requestModifier func(*retryablehttp.Request)
|
||||
responseValidator func(statusCode int, body []byte) error
|
||||
}
|
||||
|
||||
func postMessage(ctx context.Context, address, proxy string, certPool *x509.CertPool, payload interface{}, reqOpts ...requestOptFunc) error {
|
||||
httpClient := retryablehttp.NewClient()
|
||||
if certPool != nil {
|
||||
httpClient.HTTPClient.Transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: certPool,
|
||||
},
|
||||
}
|
||||
type postOption func(*postOptions)
|
||||
|
||||
func postMessage(ctx context.Context, address string, payload interface{}, opts ...postOption) error {
|
||||
options := &postOptions{
|
||||
// Default validateResponse function verifies that the response status code is 200, 202 or 201.
|
||||
responseValidator: func(statusCode int, body []byte) error {
|
||||
if statusCode == http.StatusOK ||
|
||||
statusCode == http.StatusAccepted ||
|
||||
statusCode == http.StatusCreated {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("request failed with status code %d, %s", statusCode, string(body))
|
||||
},
|
||||
}
|
||||
|
||||
if proxy != "" {
|
||||
proxyURL, err := url.Parse(proxy)
|
||||
for _, o := range opts {
|
||||
o(options)
|
||||
}
|
||||
|
||||
httpClient, err := newHTTPClient(options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
data, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshalling notification payload failed: %w", err)
|
||||
}
|
||||
|
||||
req, err := retryablehttp.NewRequestWithContext(ctx, http.MethodPost, address, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create a new request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
if options.requestModifier != nil {
|
||||
options.requestModifier(req)
|
||||
}
|
||||
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read response body: %w", err)
|
||||
}
|
||||
|
||||
if err := options.responseValidator(resp.StatusCode, body); err != nil {
|
||||
return fmt.Errorf("request failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func withProxy(proxy string) postOption {
|
||||
return func(opts *postOptions) {
|
||||
opts.proxy = proxy
|
||||
}
|
||||
}
|
||||
|
||||
func withTLSConfig(tlsConfig *tls.Config) postOption {
|
||||
return func(opts *postOptions) {
|
||||
opts.tlsConfig = tlsConfig
|
||||
}
|
||||
}
|
||||
|
||||
func withRequestModifier(reqModifier func(*retryablehttp.Request)) postOption {
|
||||
return func(opts *postOptions) {
|
||||
opts.requestModifier = reqModifier
|
||||
}
|
||||
}
|
||||
|
||||
func withResponseValidator(respValidator func(statusCode int, body []byte) error) postOption {
|
||||
return func(opts *postOptions) {
|
||||
opts.responseValidator = respValidator
|
||||
}
|
||||
}
|
||||
|
||||
func newHTTPClient(opts *postOptions) (*retryablehttp.Client, error) {
|
||||
httpClient := retryablehttp.NewClient()
|
||||
|
||||
transport := httpClient.HTTPClient.Transport.(*http.Transport)
|
||||
|
||||
if opts.tlsConfig != nil {
|
||||
transport.TLSClientConfig = opts.tlsConfig
|
||||
}
|
||||
|
||||
if opts.proxy != "" {
|
||||
proxyURL, err := url.Parse(opts.proxy)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse proxy URL '%s', error: %w", proxy, err)
|
||||
}
|
||||
var tlsConfig *tls.Config
|
||||
if certPool != nil {
|
||||
tlsConfig = &tls.Config{
|
||||
RootCAs: certPool,
|
||||
}
|
||||
}
|
||||
httpClient.HTTPClient.Transport = &http.Transport{
|
||||
Proxy: http.ProxyURL(proxyURL),
|
||||
TLSClientConfig: tlsConfig,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: 15 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).DialContext,
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
MaxIdleConnsPerHost: runtime.GOMAXPROCS(0) + 1,
|
||||
return nil, fmt.Errorf("unable to parse proxy URL: %w", err)
|
||||
}
|
||||
transport.Proxy = http.ProxyURL(proxyURL)
|
||||
}
|
||||
|
||||
// Disable the timeout for the HTTP client,
|
||||
|
@ -79,34 +144,5 @@ func postMessage(ctx context.Context, address, proxy string, certPool *x509.Cert
|
|||
httpClient.RetryMax = 4
|
||||
httpClient.Logger = nil
|
||||
|
||||
data, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshalling notification payload failed: %w", err)
|
||||
}
|
||||
|
||||
req, err := retryablehttp.NewRequest(http.MethodPost, address, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create a new request: %w", err)
|
||||
}
|
||||
if ctx != nil {
|
||||
req = req.WithContext(ctx)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
for _, o := range reqOpts {
|
||||
o(req)
|
||||
}
|
||||
resp, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to execute request: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusAccepted && resp.StatusCode != http.StatusCreated {
|
||||
b, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read response body, %s", err)
|
||||
}
|
||||
return fmt.Errorf("request failed with status code %d, %s", resp.StatusCode, string(b))
|
||||
}
|
||||
|
||||
return nil
|
||||
return httpClient, nil
|
||||
}
|
||||
|
|
|
@ -18,15 +18,19 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
|
||||
"github.com/hashicorp/go-retryablehttp"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
|
@ -45,7 +49,7 @@ func Test_postMessage(t *testing.T) {
|
|||
require.Equal(t, "success", payload["status"])
|
||||
}))
|
||||
defer ts.Close()
|
||||
err := postMessage(context.Background(), ts.URL, "", nil, map[string]string{"status": "success"})
|
||||
err := postMessage(context.Background(), ts.URL, map[string]string{"status": "success"})
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
|
@ -56,7 +60,7 @@ func Test_postMessage_timeout(t *testing.T) {
|
|||
defer ts.Close()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
|
||||
defer cancel()
|
||||
err := postMessage(ctx, ts.URL, "", nil, map[string]string{"status": "success"})
|
||||
err := postMessage(ctx, ts.URL, map[string]string{"status": "success"})
|
||||
require.Error(t, err, "context deadline exceeded")
|
||||
}
|
||||
|
||||
|
@ -77,10 +81,43 @@ func Test_postSelfSignedCert(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
certpool := x509.NewCertPool()
|
||||
certpool.AddCert(cert)
|
||||
err = postMessage(context.Background(), ts.URL, "", certpool, map[string]string{"status": "success"})
|
||||
tlsConfig := &tls.Config{RootCAs: certpool}
|
||||
err = postMessage(context.Background(), ts.URL, map[string]string{"status": "success"}, withTLSConfig(tlsConfig))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_postMessage_requestModifier(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, "Bearer token", r.Header.Get("Authorization"))
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
err := postMessage(context.Background(), ts.URL, map[string]string{"status": "success"}, withRequestModifier(func(req *retryablehttp.Request) {
|
||||
req.Header.Set("Authorization", "Bearer token")
|
||||
}))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_postMessage_responseValidator(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// Default response validator determines success, but the custom validator below will determine failure .
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte("error: bad request"))
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
err := postMessage(context.Background(), ts.URL, map[string]string{"status": "success"})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = postMessage(context.Background(), ts.URL, map[string]string{"status": "success"}, withResponseValidator(func(_ int, body []byte) error {
|
||||
if strings.HasPrefix(string(body), "error:") {
|
||||
return errors.New(string(body))
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
require.ErrorContains(t, err, "request failed: error: bad request")
|
||||
}
|
||||
|
||||
func testEvent() eventv1.Event {
|
||||
return eventv1.Event{
|
||||
InvolvedObject: corev1.ObjectReference{
|
||||
|
|
|
@ -90,8 +90,12 @@ func (s *Discord) Post(ctx context.Context, event eventv1.Event) error {
|
|||
|
||||
payload.Attachments = []SlackAttachment{a}
|
||||
|
||||
err := postMessage(ctx, s.URL, s.ProxyURL, nil, payload)
|
||||
if err != nil {
|
||||
var opts []postOption
|
||||
if s.ProxyURL != "" {
|
||||
opts = append(opts, withProxy(s.ProxyURL))
|
||||
}
|
||||
|
||||
if err := postMessage(ctx, s.URL, payload, opts...); err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -17,9 +17,15 @@ limitations under the License.
|
|||
package notifier
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/fluxcd/pkg/cache"
|
||||
|
||||
apiv1 "github.com/fluxcd/notification-controller/api/v1beta3"
|
||||
)
|
||||
|
||||
|
@ -64,76 +70,180 @@ type notifierMap map[string]factoryFunc
|
|||
type factoryFunc func(opts notifierOptions) (Interface, error)
|
||||
|
||||
type notifierOptions struct {
|
||||
URL string
|
||||
ProxyURL string
|
||||
Username string
|
||||
Channel string
|
||||
Token string
|
||||
Headers map[string]string
|
||||
CertPool *x509.CertPool
|
||||
Password string
|
||||
ProviderUID string
|
||||
Context context.Context
|
||||
URL string
|
||||
ProxyURL string
|
||||
Username string
|
||||
Channel string
|
||||
Token string
|
||||
Headers map[string]string
|
||||
// CertPool is kept for Git platform providers (GitHub, GitLab, etc.) that use third-party SDKs.
|
||||
// TODO: Remove this field once all notifiers support client certificate authentication via TLSConfig.
|
||||
CertPool *x509.CertPool
|
||||
TLSConfig *tls.Config
|
||||
Password string
|
||||
CommitStatus string
|
||||
ProviderName string
|
||||
ProviderNamespace string
|
||||
SecretData map[string][]byte
|
||||
ServiceAccountName string
|
||||
TokenCache *cache.TokenCache
|
||||
TokenClient client.Client
|
||||
}
|
||||
|
||||
type Factory struct {
|
||||
notifierOptions
|
||||
}
|
||||
|
||||
func NewFactory(url string,
|
||||
proxy string,
|
||||
username string,
|
||||
channel string,
|
||||
token string,
|
||||
headers map[string]string,
|
||||
certPool *x509.CertPool,
|
||||
password string,
|
||||
providerUID string) *Factory {
|
||||
// Option represents a functional option for configuring a notifier.
|
||||
type Option func(*notifierOptions)
|
||||
|
||||
// WithProxyURL sets the proxy URL for the notifier.
|
||||
func WithProxyURL(url string) Option {
|
||||
return func(o *notifierOptions) {
|
||||
o.ProxyURL = url
|
||||
}
|
||||
}
|
||||
|
||||
// WithUsername sets the username for the notifier.
|
||||
func WithUsername(username string) Option {
|
||||
return func(o *notifierOptions) {
|
||||
o.Username = username
|
||||
}
|
||||
}
|
||||
|
||||
// WithChannel sets the channel for the notifier.
|
||||
func WithChannel(channel string) Option {
|
||||
return func(o *notifierOptions) {
|
||||
o.Channel = channel
|
||||
}
|
||||
}
|
||||
|
||||
// WithToken sets the token for the notifier.
|
||||
func WithToken(token string) Option {
|
||||
return func(o *notifierOptions) {
|
||||
o.Token = token
|
||||
}
|
||||
}
|
||||
|
||||
// WithHeaders sets the headers for the notifier.
|
||||
func WithHeaders(headers map[string]string) Option {
|
||||
return func(o *notifierOptions) {
|
||||
o.Headers = headers
|
||||
}
|
||||
}
|
||||
|
||||
// WithCertPool sets the certificate pool for the notifier.
|
||||
func WithCertPool(certPool *x509.CertPool) Option {
|
||||
return func(o *notifierOptions) {
|
||||
o.CertPool = certPool
|
||||
}
|
||||
}
|
||||
|
||||
// WithTLSConfig sets the TLS configuration for the notifier.
|
||||
func WithTLSConfig(tlsConfig *tls.Config) Option {
|
||||
return func(o *notifierOptions) {
|
||||
o.TLSConfig = tlsConfig
|
||||
}
|
||||
}
|
||||
|
||||
// WithPassword sets the password for the notifier.
|
||||
func WithPassword(password string) Option {
|
||||
return func(o *notifierOptions) {
|
||||
o.Password = password
|
||||
}
|
||||
}
|
||||
|
||||
// WithCommitStatus sets the custom commit status for the notifier.
|
||||
func WithCommitStatus(commitStatus string) Option {
|
||||
return func(o *notifierOptions) {
|
||||
o.CommitStatus = commitStatus
|
||||
}
|
||||
}
|
||||
|
||||
// WithProviderName sets the provider name for the notifier.
|
||||
func WithProviderName(name string) Option {
|
||||
return func(o *notifierOptions) {
|
||||
o.ProviderName = name
|
||||
}
|
||||
}
|
||||
|
||||
// WithProviderNamespace sets the provider namespace for the notifier.
|
||||
func WithProviderNamespace(namespace string) Option {
|
||||
return func(o *notifierOptions) {
|
||||
o.ProviderNamespace = namespace
|
||||
}
|
||||
}
|
||||
|
||||
// WithSecretData sets the secret data for the notifier.
|
||||
func WithSecretData(data map[string][]byte) Option {
|
||||
return func(o *notifierOptions) {
|
||||
o.SecretData = data
|
||||
}
|
||||
}
|
||||
|
||||
// WithTokenCache sets the token cache for the notifier.
|
||||
func WithTokenCache(cache *cache.TokenCache) Option {
|
||||
return func(o *notifierOptions) {
|
||||
o.TokenCache = cache
|
||||
}
|
||||
}
|
||||
|
||||
// WithTokenClient sets the token client for the notifier.
|
||||
func WithTokenClient(kubeClient client.Client) Option {
|
||||
return func(o *notifierOptions) {
|
||||
o.TokenClient = kubeClient
|
||||
}
|
||||
}
|
||||
|
||||
// WithServiceAccount sets the service account for the notifier.
|
||||
func WithServiceAccount(serviceAccountName string) Option {
|
||||
return func(o *notifierOptions) {
|
||||
o.ServiceAccountName = serviceAccountName
|
||||
}
|
||||
}
|
||||
|
||||
// WithURL sets the webhook URL for the notifier.
|
||||
func WithURL(url string) Option {
|
||||
return func(o *notifierOptions) {
|
||||
o.URL = url
|
||||
}
|
||||
}
|
||||
|
||||
// NewFactory creates a new notifier factory with optional configurations.
|
||||
func NewFactory(ctx context.Context, opts ...Option) *Factory {
|
||||
options := notifierOptions{
|
||||
Context: ctx,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
return &Factory{
|
||||
notifierOptions: notifierOptions{
|
||||
URL: url,
|
||||
ProxyURL: proxy,
|
||||
Username: username,
|
||||
Channel: channel,
|
||||
Token: token,
|
||||
Headers: headers,
|
||||
CertPool: certPool,
|
||||
Password: password,
|
||||
ProviderUID: providerUID,
|
||||
},
|
||||
notifierOptions: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (f Factory) Notifier(provider string) (Interface, error) {
|
||||
if f.URL == "" {
|
||||
return &NopNotifier{}, nil
|
||||
notifier, ok := notifiers[provider]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("provider %s not supported", provider)
|
||||
}
|
||||
|
||||
var (
|
||||
n Interface
|
||||
err error
|
||||
)
|
||||
if notifier, ok := notifiers[provider]; ok {
|
||||
n, err = notifier(f.notifierOptions)
|
||||
} else {
|
||||
err = fmt.Errorf("provider %s not supported", provider)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
n = &NopNotifier{}
|
||||
}
|
||||
return n, err
|
||||
return notifier(f.notifierOptions)
|
||||
}
|
||||
|
||||
func genericNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
return NewForwarder(opts.URL, opts.ProxyURL, opts.Headers, opts.CertPool, nil)
|
||||
return NewForwarder(opts.URL, opts.ProxyURL, opts.Headers, opts.TLSConfig, nil)
|
||||
}
|
||||
|
||||
func genericHMACNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
return NewForwarder(opts.URL, opts.ProxyURL, opts.Headers, opts.CertPool, []byte(opts.Token))
|
||||
return NewForwarder(opts.URL, opts.ProxyURL, opts.Headers, opts.TLSConfig, []byte(opts.Token))
|
||||
}
|
||||
|
||||
func slackNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
return NewSlack(opts.URL, opts.ProxyURL, opts.Token, opts.CertPool, opts.Username, opts.Channel)
|
||||
return NewSlack(opts.URL, opts.ProxyURL, opts.Token, opts.TLSConfig, opts.Username, opts.Channel)
|
||||
}
|
||||
|
||||
func discordNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
|
@ -141,11 +251,11 @@ func discordNotifierFunc(opts notifierOptions) (Interface, error) {
|
|||
}
|
||||
|
||||
func rocketNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
return NewRocket(opts.URL, opts.ProxyURL, opts.CertPool, opts.Username, opts.Channel)
|
||||
return NewRocket(opts.URL, opts.ProxyURL, opts.TLSConfig, opts.Username, opts.Channel)
|
||||
}
|
||||
|
||||
func msteamsNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
return NewMSTeams(opts.URL, opts.ProxyURL, opts.CertPool)
|
||||
return NewMSTeams(opts.URL, opts.ProxyURL, opts.TLSConfig)
|
||||
}
|
||||
|
||||
func googleChatNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
|
@ -157,7 +267,7 @@ func googlePubSubNotifierFunc(opts notifierOptions) (Interface, error) {
|
|||
}
|
||||
|
||||
func webexNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
return NewWebex(opts.URL, opts.ProxyURL, opts.CertPool, opts.Channel, opts.Token)
|
||||
return NewWebex(opts.URL, opts.ProxyURL, opts.TLSConfig, opts.Channel, opts.Token)
|
||||
}
|
||||
|
||||
func sentryNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
|
@ -165,11 +275,11 @@ func sentryNotifierFunc(opts notifierOptions) (Interface, error) {
|
|||
}
|
||||
|
||||
func azureEventHubNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
return NewAzureEventHub(opts.URL, opts.Token, opts.Channel)
|
||||
return NewAzureEventHub(opts.Context, opts.URL, opts.Token, opts.Channel, opts.ProxyURL, opts.ServiceAccountName, opts.ProviderName, opts.ProviderNamespace, opts.TokenClient, opts.TokenCache)
|
||||
}
|
||||
|
||||
func telegramNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
return NewTelegram(opts.Channel, opts.Token)
|
||||
return NewTelegram(opts.ProxyURL, opts.Channel, opts.Token)
|
||||
}
|
||||
|
||||
func larkNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
|
@ -177,23 +287,23 @@ func larkNotifierFunc(opts notifierOptions) (Interface, error) {
|
|||
}
|
||||
|
||||
func matrixNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
return NewMatrix(opts.URL, opts.Token, opts.Channel, opts.CertPool)
|
||||
return NewMatrix(opts.URL, opts.Token, opts.Channel, opts.TLSConfig)
|
||||
}
|
||||
|
||||
func opsgenieNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
return NewOpsgenie(opts.URL, opts.ProxyURL, opts.CertPool, opts.Token)
|
||||
return NewOpsgenie(opts.URL, opts.ProxyURL, opts.TLSConfig, opts.Token)
|
||||
}
|
||||
|
||||
func alertmanagerNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
return NewAlertmanager(opts.URL, opts.ProxyURL, opts.CertPool, opts.Token)
|
||||
return NewAlertmanager(opts.URL, opts.ProxyURL, opts.TLSConfig, opts.Token)
|
||||
}
|
||||
|
||||
func grafanaNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
return NewGrafana(opts.URL, opts.ProxyURL, opts.Token, opts.CertPool, opts.Username, opts.Password)
|
||||
return NewGrafana(opts.URL, opts.ProxyURL, opts.Token, opts.TLSConfig, opts.Username, opts.Password)
|
||||
}
|
||||
|
||||
func pagerDutyNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
return NewPagerDuty(opts.URL, opts.ProxyURL, opts.CertPool, opts.Channel)
|
||||
return NewPagerDuty(opts.URL, opts.ProxyURL, opts.TLSConfig, opts.Channel)
|
||||
}
|
||||
|
||||
func dataDogNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
|
@ -208,38 +318,38 @@ func gitHubNotifierFunc(opts notifierOptions) (Interface, error) {
|
|||
if opts.Token == "" && opts.Password != "" {
|
||||
opts.Token = opts.Password
|
||||
}
|
||||
return NewGitHub(opts.ProviderUID, opts.URL, opts.Token, opts.CertPool)
|
||||
return NewGitHub(opts.CommitStatus, opts.URL, opts.Token, opts.CertPool, opts.ProxyURL, opts.ProviderName, opts.ProviderNamespace, opts.SecretData, opts.TokenCache)
|
||||
}
|
||||
|
||||
func gitHubDispatchNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
if opts.Token == "" && opts.Password != "" {
|
||||
opts.Token = opts.Password
|
||||
}
|
||||
return NewGitHubDispatch(opts.URL, opts.Token, opts.CertPool)
|
||||
return NewGitHubDispatch(opts.URL, opts.Token, opts.CertPool, opts.ProxyURL, opts.ProviderName, opts.ProviderNamespace, opts.SecretData, opts.TokenCache)
|
||||
}
|
||||
|
||||
func gitLabNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
if opts.Token == "" && opts.Password != "" {
|
||||
opts.Token = opts.Password
|
||||
}
|
||||
return NewGitLab(opts.ProviderUID, opts.URL, opts.Token, opts.CertPool)
|
||||
return NewGitLab(opts.CommitStatus, opts.URL, opts.Token, opts.CertPool)
|
||||
}
|
||||
|
||||
func giteaNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
if opts.Token == "" && opts.Password != "" {
|
||||
opts.Token = opts.Password
|
||||
}
|
||||
return NewGitea(opts.ProviderUID, opts.URL, opts.Token, opts.CertPool)
|
||||
return NewGitea(opts.CommitStatus, opts.URL, opts.ProxyURL, opts.Token, opts.CertPool)
|
||||
}
|
||||
|
||||
func bitbucketServerNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
return NewBitbucketServer(opts.ProviderUID, opts.URL, opts.Token, opts.CertPool, opts.Username, opts.Password)
|
||||
return NewBitbucketServer(opts.CommitStatus, opts.URL, opts.Token, opts.CertPool, opts.Username, opts.Password)
|
||||
}
|
||||
|
||||
func bitbucketNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
return NewBitbucket(opts.ProviderUID, opts.URL, opts.Token, opts.CertPool)
|
||||
return NewBitbucket(opts.CommitStatus, opts.URL, opts.Token, opts.CertPool)
|
||||
}
|
||||
|
||||
func azureDevOpsNotifierFunc(opts notifierOptions) (Interface, error) {
|
||||
return NewAzureDevOps(opts.ProviderUID, opts.URL, opts.Token, opts.CertPool)
|
||||
return NewAzureDevOps(opts.CommitStatus, opts.URL, opts.Token, opts.CertPool)
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import (
|
|||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
@ -37,14 +37,14 @@ const NotificationHeader = "gotk-component"
|
|||
// Forwarder is an implementation of the notification Interface that posts the
|
||||
// body as an HTTP request using an optional proxy.
|
||||
type Forwarder struct {
|
||||
URL string
|
||||
ProxyURL string
|
||||
Headers map[string]string
|
||||
CertPool *x509.CertPool
|
||||
HMACKey []byte
|
||||
URL string
|
||||
ProxyURL string
|
||||
Headers map[string]string
|
||||
TLSConfig *tls.Config
|
||||
HMACKey []byte
|
||||
}
|
||||
|
||||
func NewForwarder(hookURL string, proxyURL string, headers map[string]string, certPool *x509.CertPool, hmacKey []byte) (*Forwarder, error) {
|
||||
func NewForwarder(hookURL string, proxyURL string, headers map[string]string, tlsConfig *tls.Config, hmacKey []byte) (*Forwarder, error) {
|
||||
if _, err := url.ParseRequestURI(hookURL); err != nil {
|
||||
return nil, fmt.Errorf("invalid hook URL %s: %w", hookURL, err)
|
||||
}
|
||||
|
@ -54,11 +54,11 @@ func NewForwarder(hookURL string, proxyURL string, headers map[string]string, ce
|
|||
}
|
||||
|
||||
return &Forwarder{
|
||||
URL: hookURL,
|
||||
ProxyURL: proxyURL,
|
||||
Headers: headers,
|
||||
CertPool: certPool,
|
||||
HMACKey: hmacKey,
|
||||
URL: hookURL,
|
||||
ProxyURL: proxyURL,
|
||||
Headers: headers,
|
||||
HMACKey: hmacKey,
|
||||
TLSConfig: tlsConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -77,18 +77,28 @@ func (f *Forwarder) Post(ctx context.Context, event eventv1.Event) error {
|
|||
}
|
||||
sig = fmt.Sprintf("sha256=%s", sign(eventJSON, f.HMACKey))
|
||||
}
|
||||
err := postMessage(ctx, f.URL, f.ProxyURL, f.CertPool, event, func(req *retryablehttp.Request) {
|
||||
req.Header.Set(NotificationHeader, event.ReportingController)
|
||||
for key, val := range f.Headers {
|
||||
req.Header.Set(key, val)
|
||||
}
|
||||
if sig != "" {
|
||||
req.Header.Set("X-Signature", sig)
|
||||
}
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
opts := []postOption{
|
||||
withRequestModifier(func(req *retryablehttp.Request) {
|
||||
req.Header.Set(NotificationHeader, event.ReportingController)
|
||||
for key, val := range f.Headers {
|
||||
req.Header.Set(key, val)
|
||||
}
|
||||
if sig != "" {
|
||||
req.Header.Set("X-Signature", sig)
|
||||
}
|
||||
}),
|
||||
}
|
||||
if f.ProxyURL != "" {
|
||||
opts = append(opts, withProxy(f.ProxyURL))
|
||||
}
|
||||
if f.TLSConfig != nil {
|
||||
opts = append(opts, withTLSConfig(f.TLSConfig))
|
||||
}
|
||||
|
||||
if err := postMessage(ctx, f.URL, event, opts...); err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -41,13 +41,13 @@ func Fuzz_Forwarder(f *testing.F) {
|
|||
}))
|
||||
defer ts.Close()
|
||||
|
||||
var cert x509.CertPool
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&cert)
|
||||
var tlsConfig tls.Config
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&tlsConfig)
|
||||
|
||||
header := make(map[string]string)
|
||||
_ = fuzz.NewConsumer(seed).FuzzMap(&header)
|
||||
|
||||
forwarder, err := NewForwarder(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", header, &cert, hmacKey)
|
||||
forwarder, err := NewForwarder(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", header, &tlsConfig, hmacKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -34,22 +34,27 @@ import (
|
|||
)
|
||||
|
||||
type Gitea struct {
|
||||
BaseURL string
|
||||
Token string
|
||||
Owner string
|
||||
Repo string
|
||||
ProviderUID string
|
||||
Client *gitea.Client
|
||||
Debug bool
|
||||
BaseURL string
|
||||
Token string
|
||||
Owner string
|
||||
Repo string
|
||||
CommitStatus string
|
||||
Client *gitea.Client
|
||||
Debug bool
|
||||
}
|
||||
|
||||
var _ Interface = &Gitea{}
|
||||
|
||||
func NewGitea(providerUID string, addr string, token string, certPool *x509.CertPool) (*Gitea, error) {
|
||||
func NewGitea(commitStatus string, addr string, proxyURL string, token string, certPool *x509.CertPool) (*Gitea, error) {
|
||||
if len(token) == 0 {
|
||||
return nil, errors.New("gitea token cannot be empty")
|
||||
}
|
||||
|
||||
// this should never happen
|
||||
if commitStatus == "" {
|
||||
return nil, errors.New("commit status cannot be empty")
|
||||
}
|
||||
|
||||
host, id, err := parseGitAddress(addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed parsing Git URL: %w", err)
|
||||
|
@ -64,28 +69,34 @@ func NewGitea(providerUID string, addr string, token string, certPool *x509.Cert
|
|||
return nil, fmt.Errorf("invalid repository id %q", id)
|
||||
}
|
||||
|
||||
client, err := gitea.NewClient(host, gitea.SetToken(token))
|
||||
tr := &http.Transport{}
|
||||
if certPool != nil {
|
||||
tr.TLSClientConfig = &tls.Config{
|
||||
RootCAs: certPool,
|
||||
}
|
||||
}
|
||||
|
||||
if proxyURL != "" {
|
||||
parsedProxyURL, err := url.Parse(proxyURL)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid proxy URL")
|
||||
}
|
||||
tr.Proxy = http.ProxyURL(parsedProxyURL)
|
||||
}
|
||||
|
||||
client, err := gitea.NewClient(host, gitea.SetToken(token), gitea.SetHTTPClient(&http.Client{Transport: tr}))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed creating Gitea client: %w", err)
|
||||
}
|
||||
|
||||
if certPool != nil {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: certPool,
|
||||
},
|
||||
}
|
||||
client.SetHTTPClient(&http.Client{Transport: tr})
|
||||
}
|
||||
|
||||
return &Gitea{
|
||||
BaseURL: host,
|
||||
Token: token,
|
||||
Owner: idComponents[0],
|
||||
Repo: idComponents[1],
|
||||
ProviderUID: providerUID,
|
||||
Client: client,
|
||||
Debug: os.Getenv("NOTIFIER_GITEA_DEBUG") == "true",
|
||||
BaseURL: host,
|
||||
Token: token,
|
||||
Owner: idComponents[0],
|
||||
Repo: idComponents[1],
|
||||
CommitStatus: commitStatus,
|
||||
Client: client,
|
||||
Debug: os.Getenv("NOTIFIER_GITEA_DEBUG") == "true",
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -104,7 +115,7 @@ func (g *Gitea) Post(ctx context.Context, event eventv1.Event) error {
|
|||
}
|
||||
|
||||
_, desc := formatNameAndDescription(event)
|
||||
id := generateCommitStatusID(g.ProviderUID, event)
|
||||
id := g.CommitStatus
|
||||
|
||||
status := gitea.CreateStatusOption{
|
||||
State: state,
|
||||
|
|
|
@ -18,7 +18,9 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
testproxy "github.com/fluxcd/notification-controller/tests/proxy"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
@ -31,10 +33,20 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// newTestServer returns an HTTP server mimicking parts of Gitea's API so that tests don't
|
||||
// newTestHTTPServer returns an HTTP server mimicking parts of Gitea's API so that tests don't
|
||||
// need to rely on 3rd-party components to be available (like the try.gitea.io server).
|
||||
func newTestServer(t *testing.T) *httptest.Server {
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
func newTestHTTPServer(t *testing.T) *httptest.Server {
|
||||
return httptest.NewServer(newGiteaStubHandler(t))
|
||||
}
|
||||
|
||||
// newTestHTTPSServer returns an HTTPS server mimicking parts of Gitea's API so that tests don't
|
||||
// need to rely on 3rd-party components to be available (like the try.gitea.io server).
|
||||
func newTestHTTPSServer(t *testing.T) *httptest.Server {
|
||||
return httptest.NewTLSServer(newGiteaStubHandler(t))
|
||||
}
|
||||
|
||||
func newGiteaStubHandler(t *testing.T) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/api/v1/version":
|
||||
fmt.Fprintf(w, `{"version":"1.18.3"}`)
|
||||
|
@ -49,15 +61,69 @@ func newTestServer(t *testing.T) *httptest.Server {
|
|||
default:
|
||||
t.Logf("unknown %s request at %s", r.Method, r.URL.Path)
|
||||
}
|
||||
}))
|
||||
return srv
|
||||
})
|
||||
}
|
||||
|
||||
func TestNewGiteaBasic(t *testing.T) {
|
||||
srv := newTestServer(t)
|
||||
srv := newTestHTTPServer(t)
|
||||
defer srv.Close()
|
||||
|
||||
g, err := NewGitea("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", srv.URL+"/foo/bar", "foobar", nil)
|
||||
g, err := NewGitea("kustomization/gitops-system/0c9c2e41", srv.URL+"/foo/bar", "", "foobar", nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, g.Owner, "foo")
|
||||
assert.Equal(t, g.Repo, "bar")
|
||||
assert.Equal(t, g.BaseURL, srv.URL)
|
||||
}
|
||||
|
||||
func TestNewGiteaWithCertPool(t *testing.T) {
|
||||
srv := newTestHTTPSServer(t)
|
||||
defer srv.Close()
|
||||
|
||||
certPool := x509.NewCertPool()
|
||||
certPool.AddCert(srv.Certificate())
|
||||
|
||||
g, err := NewGitea("kustomization/gitops-system/0c9c2e41", srv.URL+"/foo/bar", "", "foobar", certPool)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, g.Owner, "foo")
|
||||
assert.Equal(t, g.Repo, "bar")
|
||||
assert.Equal(t, g.BaseURL, srv.URL)
|
||||
}
|
||||
|
||||
func TestNewGiteaNoCertificate(t *testing.T) {
|
||||
srv := newTestHTTPSServer(t)
|
||||
defer srv.Close()
|
||||
|
||||
certPool := x509.NewCertPool()
|
||||
|
||||
_, err := NewGitea("kustomization/gitops-system/0c9c2e41", srv.URL+"/foo/bar", "", "foobar", certPool)
|
||||
assert.Error(t, err)
|
||||
assert.ErrorContains(t, err, "tls: failed to verify certificate: x509: certificate signed by unknown authority")
|
||||
}
|
||||
|
||||
func TestNewGiteaWithProxyURL(t *testing.T) {
|
||||
srv := newTestHTTPServer(t)
|
||||
defer srv.Close()
|
||||
proxyAddr, _ := testproxy.New(t)
|
||||
proxyURL := fmt.Sprintf("http://%s", proxyAddr)
|
||||
|
||||
g, err := NewGitea("kustomization/gitops-system/0c9c2e41", srv.URL+"/foo/bar", proxyURL, "foobar", nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, g.Owner, "foo")
|
||||
assert.Equal(t, g.Repo, "bar")
|
||||
assert.Equal(t, g.BaseURL, srv.URL)
|
||||
}
|
||||
|
||||
func TestNewGiteaWithProxyURLAndCertPool(t *testing.T) {
|
||||
srv := newTestHTTPSServer(t)
|
||||
defer srv.Close()
|
||||
|
||||
certPool := x509.NewCertPool()
|
||||
certPool.AddCert(srv.Certificate())
|
||||
|
||||
proxyAddr, _ := testproxy.New(t)
|
||||
proxyURL := fmt.Sprintf("http://%s", proxyAddr)
|
||||
|
||||
g, err := NewGitea("kustomization/gitops-system/0c9c2e41", srv.URL+"/foo/bar", proxyURL, "foobar", certPool)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, g.Owner, "foo")
|
||||
assert.Equal(t, g.Repo, "bar")
|
||||
|
@ -65,26 +131,39 @@ func TestNewGiteaBasic(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNewGiteaInvalidUrl(t *testing.T) {
|
||||
srv := newTestServer(t)
|
||||
srv := newTestHTTPServer(t)
|
||||
defer srv.Close()
|
||||
|
||||
_, err := NewGitea("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", srv.URL+"/foo/bar/baz", "foobar", nil)
|
||||
_, err := NewGitea("kustomization/gitops-system/0c9c2e41", srv.URL+"/foo/bar/baz", "", "foobar", nil)
|
||||
assert.ErrorContains(t, err, "invalid repository id")
|
||||
}
|
||||
|
||||
func TestNewGiteaInvalidProxyUrl(t *testing.T) {
|
||||
_, err := NewGitea("kustomization/gitops-system/0c9c2e41", "/foo/bar", "wrong\nURL", "foobar", nil)
|
||||
assert.ErrorContains(t, err, "invalid proxy URL")
|
||||
}
|
||||
|
||||
func TestNewGiteaEmptyToken(t *testing.T) {
|
||||
srv := newTestServer(t)
|
||||
srv := newTestHTTPServer(t)
|
||||
defer srv.Close()
|
||||
|
||||
_, err := NewGitea("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", srv.URL+"/foo/bar", "", nil)
|
||||
_, err := NewGitea("kustomization/gitops-system/0c9c2e41", srv.URL+"/foo/bar", "", "", nil)
|
||||
assert.ErrorContains(t, err, "gitea token cannot be empty")
|
||||
}
|
||||
|
||||
func TestGitea_Post(t *testing.T) {
|
||||
srv := newTestServer(t)
|
||||
func TestNewGiteaEmptyCommitStatus(t *testing.T) {
|
||||
srv := newTestHTTPServer(t)
|
||||
defer srv.Close()
|
||||
|
||||
g, err := NewGitea("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", srv.URL+"/foo/bar", "foobar", nil)
|
||||
_, err := NewGitea("", srv.URL+"/foo/bar", "", "foobar", nil)
|
||||
assert.ErrorContains(t, err, "commit status cannot be empty")
|
||||
}
|
||||
|
||||
func TestGitea_Post(t *testing.T) {
|
||||
srv := newTestHTTPServer(t)
|
||||
defer srv.Close()
|
||||
|
||||
g, err := NewGitea("kustomization/gitops-system/0c9c2e41", srv.URL+"/foo/bar", "", "foobar", nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
for _, tt := range []struct {
|
||||
|
|
|
@ -18,74 +18,44 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-github/v64/github"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"github.com/fluxcd/pkg/cache"
|
||||
)
|
||||
|
||||
type GitHub struct {
|
||||
Owner string
|
||||
Repo string
|
||||
ProviderUID string
|
||||
Client *github.Client
|
||||
Owner string
|
||||
Repo string
|
||||
CommitStatus string
|
||||
Client *github.Client
|
||||
}
|
||||
|
||||
func NewGitHub(providerUID string, addr string, token string, certPool *x509.CertPool) (*GitHub, error) {
|
||||
if len(token) == 0 {
|
||||
return nil, errors.New("github token cannot be empty")
|
||||
func NewGitHub(commitStatus string, addr string, token string, certPool *x509.CertPool,
|
||||
proxyURL string, providerName string, providerNamespace string, secretData map[string][]byte,
|
||||
tokenCache *cache.TokenCache) (*GitHub, error) {
|
||||
|
||||
// this should never happen
|
||||
if commitStatus == "" {
|
||||
return nil, errors.New("commit status cannot be empty")
|
||||
}
|
||||
|
||||
host, id, err := parseGitAddress(addr)
|
||||
repoInfo, err := getRepoInfoAndGithubClient(addr, token, certPool,
|
||||
proxyURL, providerName, providerNamespace, secretData, tokenCache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
baseUrl, err := url.Parse(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
comp := strings.Split(id, "/")
|
||||
if len(comp) != 2 {
|
||||
return nil, fmt.Errorf("invalid repository id %q", id)
|
||||
}
|
||||
|
||||
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
|
||||
tc := oauth2.NewClient(context.Background(), ts)
|
||||
client := github.NewClient(tc)
|
||||
if baseUrl.Host != "github.com" {
|
||||
if certPool != nil {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: certPool,
|
||||
},
|
||||
}
|
||||
hc := &http.Client{Transport: tr}
|
||||
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, hc)
|
||||
tc = oauth2.NewClient(ctx, ts)
|
||||
}
|
||||
|
||||
client, err = github.NewEnterpriseClient(host, host, tc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create enterprise GitHub client: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &GitHub{
|
||||
Owner: comp[0],
|
||||
Repo: comp[1],
|
||||
ProviderUID: providerUID,
|
||||
Client: client,
|
||||
Owner: repoInfo.owner,
|
||||
Repo: repoInfo.repo,
|
||||
CommitStatus: commitStatus,
|
||||
Client: repoInfo.client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -110,7 +80,7 @@ func (g *GitHub) Post(ctx context.Context, event eventv1.Event) error {
|
|||
}
|
||||
|
||||
_, desc := formatNameAndDescription(event)
|
||||
id := generateCommitStatusID(g.ProviderUID, event)
|
||||
id := g.CommitStatus
|
||||
status := &github.RepoStatus{
|
||||
State: &state,
|
||||
Context: &id,
|
||||
|
|
|
@ -18,19 +18,14 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
|
||||
"github.com/fluxcd/pkg/cache"
|
||||
|
||||
"github.com/google/go-github/v64/github"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type GitHubDispatch struct {
|
||||
|
@ -39,51 +34,20 @@ type GitHubDispatch struct {
|
|||
Client *github.Client
|
||||
}
|
||||
|
||||
func NewGitHubDispatch(addr string, token string, certPool *x509.CertPool) (*GitHubDispatch, error) {
|
||||
if len(token) == 0 {
|
||||
return nil, errors.New("github token cannot be empty")
|
||||
}
|
||||
func NewGitHubDispatch(addr string, token string, certPool *x509.CertPool, proxyURL string,
|
||||
providerName string, providerNamespace string, secretData map[string][]byte,
|
||||
tokenCache *cache.TokenCache) (*GitHubDispatch, error) {
|
||||
|
||||
host, id, err := parseGitAddress(addr)
|
||||
repoInfo, err := getRepoInfoAndGithubClient(addr, token, certPool,
|
||||
proxyURL, providerName, providerNamespace, secretData, tokenCache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
baseUrl, err := url.Parse(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
comp := strings.Split(id, "/")
|
||||
if len(comp) != 2 {
|
||||
return nil, fmt.Errorf("invalid repository id %q", id)
|
||||
}
|
||||
|
||||
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
|
||||
tc := oauth2.NewClient(context.Background(), ts)
|
||||
client := github.NewClient(tc)
|
||||
if baseUrl.Host != "github.com" {
|
||||
if certPool != nil {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: certPool,
|
||||
},
|
||||
}
|
||||
hc := &http.Client{Transport: tr}
|
||||
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, hc)
|
||||
tc = oauth2.NewClient(ctx, ts)
|
||||
}
|
||||
|
||||
client, err = github.NewEnterpriseClient(host, host, tc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create enterprise GitHub client: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &GitHubDispatch{
|
||||
Owner: comp[0],
|
||||
Repo: comp[1],
|
||||
Client: client,
|
||||
Owner: repoInfo.owner,
|
||||
Repo: repoInfo.repo,
|
||||
Client: repoInfo.client,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ func Fuzz_GitHub_Dispatch(f *testing.F) {
|
|||
var cert x509.CertPool
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&cert)
|
||||
|
||||
dispatch, err := NewGitHubDispatch(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, &cert)
|
||||
dispatch, err := NewGitHubDispatch(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, &cert, "", "", "", nil, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -18,16 +18,23 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
|
||||
authgithub "github.com/fluxcd/pkg/git/github"
|
||||
"github.com/fluxcd/pkg/ssh"
|
||||
)
|
||||
|
||||
func TestNewGitHubDispatchBasic(t *testing.T) {
|
||||
g, err := NewGitHubDispatch("https://github.com/foo/bar", "foobar", nil)
|
||||
g, err := NewGitHubDispatch("https://github.com/foo/bar", "foobar", nil, "", "", "", nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, g.Owner, "foo")
|
||||
assert.Equal(t, g.Repo, "bar")
|
||||
|
@ -35,7 +42,7 @@ func TestNewGitHubDispatchBasic(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNewEnterpriseGitHubDispatchBasic(t *testing.T) {
|
||||
g, err := NewGitHubDispatch("https://foobar.com/foo/bar", "foobar", nil)
|
||||
g, err := NewGitHubDispatch("https://foobar.com/foo/bar", "foobar", nil, "", "", "", nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, g.Owner, "foo")
|
||||
assert.Equal(t, g.Repo, "bar")
|
||||
|
@ -43,17 +50,98 @@ func TestNewEnterpriseGitHubDispatchBasic(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNewGitHubDispatchInvalidUrl(t *testing.T) {
|
||||
_, err := NewGitHubDispatch("https://github.com/foo/bar/baz", "foobar", nil)
|
||||
_, err := NewGitHubDispatch("https://github.com/foo/bar/baz", "foobar", nil, "", "", "", nil, nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestNewGitHubDispatchEmptyToken(t *testing.T) {
|
||||
_, err := NewGitHubDispatch("https://github.com/foo/bar", "", nil)
|
||||
_, err := NewGitHubDispatch("https://github.com/foo/bar", "", nil, "", "", "", nil, nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestNewGithubDispatchProvider(t *testing.T) {
|
||||
appID := "123"
|
||||
installationID := "456"
|
||||
kp, _ := ssh.GenerateKeyPair(ssh.RSA_4096)
|
||||
expiresAt := time.Now().UTC().Add(time.Hour)
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
secretData map[string][]byte
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "nil provider, no token",
|
||||
wantErr: errors.New("github token or github app details must be specified"),
|
||||
},
|
||||
{
|
||||
name: "provider with no github options",
|
||||
secretData: map[string][]byte{},
|
||||
wantErr: errors.New("github token or github app details must be specified"),
|
||||
},
|
||||
{
|
||||
name: "provider with missing app ID in options ",
|
||||
secretData: map[string][]byte{
|
||||
"githubAppInstallationID": []byte(installationID),
|
||||
"githubAppPrivateKey": kp.PrivateKey,
|
||||
},
|
||||
wantErr: errors.New("github token or github app details must be specified"),
|
||||
},
|
||||
{
|
||||
name: "provider with missing app installation ID in options ",
|
||||
secretData: map[string][]byte{
|
||||
"githubAppID": []byte(appID),
|
||||
"githubAppPrivateKey": kp.PrivateKey,
|
||||
},
|
||||
wantErr: errors.New("app installation ID must be provided to use github app authentication"),
|
||||
},
|
||||
{
|
||||
name: "provider with missing app private key in options ",
|
||||
secretData: map[string][]byte{
|
||||
"githubAppID": []byte(appID),
|
||||
"githubAppInstallationID": []byte(installationID),
|
||||
},
|
||||
wantErr: errors.New("private key must be provided to use github app authentication"),
|
||||
},
|
||||
{
|
||||
name: "provider with complete app authentication information",
|
||||
secretData: map[string][]byte{
|
||||
"githubAppID": []byte(appID),
|
||||
"githubAppInstallationID": []byte(installationID),
|
||||
"githubAppPrivateKey": kp.PrivateKey,
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
var response []byte
|
||||
var err error
|
||||
response, err = json.Marshal(&authgithub.AppToken{Token: "access-token", ExpiresAt: expiresAt})
|
||||
assert.Nil(t, err)
|
||||
w.Write(response)
|
||||
}
|
||||
srv := httptest.NewServer(http.HandlerFunc(handler))
|
||||
t.Cleanup(func() {
|
||||
srv.Close()
|
||||
})
|
||||
|
||||
if len(tt.secretData) > 0 {
|
||||
tt.secretData["githubAppBaseURL"] = []byte(srv.URL)
|
||||
}
|
||||
_, err := NewGitHubDispatch("https://github.com/foo/bar", "", nil, "", "foo", "bar", tt.secretData, nil)
|
||||
if tt.wantErr != nil {
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, tt.wantErr, err)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGitHubDispatch_PostUpdate(t *testing.T) {
|
||||
githubDispatch, err := NewGitHubDispatch("https://github.com/foo/bar", "foobar", nil)
|
||||
githubDispatch, err := NewGitHubDispatch("https://github.com/foo/bar", "foobar", nil, "", "", "", nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
event := testEvent()
|
||||
|
|
|
@ -30,14 +30,14 @@ import (
|
|||
)
|
||||
|
||||
func Fuzz_GitHub(f *testing.F) {
|
||||
f.Add("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "token", "org/repo", "revision/abce1", "error", "", []byte{}, []byte(`[{"context":"/","state":"failure","description":""}]`))
|
||||
f.Add("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[{"context":"/","state":"success","description":""}]`))
|
||||
f.Add("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[{"context":"/","state":"failure","description":""}]`))
|
||||
f.Add("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[{"context":"/"}]`))
|
||||
f.Add("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[{}]`))
|
||||
f.Add("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "token", "org/repo", "revision/abce1", "info", "Progressing", []byte{}, []byte{})
|
||||
f.Add("kustomization/gitops-system/0c9c2e41", "token", "org/repo", "revision/abce1", "error", "", []byte{}, []byte(`[{"context":"/","state":"failure","description":""}]`))
|
||||
f.Add("kustomization/gitops-system/0c9c2e41", "token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[{"context":"/","state":"success","description":""}]`))
|
||||
f.Add("kustomization/gitops-system/0c9c2e41", "token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[{"context":"/","state":"failure","description":""}]`))
|
||||
f.Add("kustomization/gitops-system/0c9c2e41", "token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[{"context":"/"}]`))
|
||||
f.Add("kustomization/gitops-system/0c9c2e41", "token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[{}]`))
|
||||
f.Add("kustomization/gitops-system/0c9c2e41", "token", "org/repo", "revision/abce1", "info", "Progressing", []byte{}, []byte{})
|
||||
|
||||
f.Fuzz(func(t *testing.T, uuid, token, urlSuffix, revision, severity, reason string, seed, response []byte) {
|
||||
f.Fuzz(func(t *testing.T, commitStatus, token, urlSuffix, revision, severity, reason string, seed, response []byte) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write(response)
|
||||
io.Copy(io.Discard, r.Body)
|
||||
|
@ -48,7 +48,7 @@ func Fuzz_GitHub(f *testing.F) {
|
|||
var cert x509.CertPool
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&cert)
|
||||
|
||||
github, err := NewGitHub(uuid, fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, &cert)
|
||||
github, err := NewGitHub(commitStatus, fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, &cert, "", "foo", "bar", nil, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
Copyright 2025 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package notifier
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
gogithub "github.com/google/go-github/v64/github"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/fluxcd/pkg/cache"
|
||||
"github.com/fluxcd/pkg/git/github"
|
||||
|
||||
"github.com/fluxcd/notification-controller/api/v1beta3"
|
||||
)
|
||||
|
||||
// repoInfo is an internal type encapsulating owner, repo and client
|
||||
type repoInfo struct {
|
||||
owner string
|
||||
repo string
|
||||
client *gogithub.Client
|
||||
}
|
||||
|
||||
// getGitHubAppOptions constructs the github app authentication options.
|
||||
func getGitHubAppOptions(providerName, providerNamespace, proxy string,
|
||||
secretData map[string][]byte, tokenCache *cache.TokenCache) ([]github.OptFunc, error) {
|
||||
|
||||
githubOpts := []github.OptFunc{
|
||||
github.WithAppData(secretData),
|
||||
}
|
||||
|
||||
if len(githubOpts) > 0 && proxy != "" {
|
||||
proxyURL, err := url.Parse(proxy)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing proxy URL '%s': %w", proxy, err)
|
||||
}
|
||||
githubOpts = append(githubOpts, github.WithProxyURL(proxyURL))
|
||||
}
|
||||
|
||||
if len(githubOpts) > 0 && tokenCache != nil {
|
||||
githubOpts = append(githubOpts, github.WithCache(tokenCache,
|
||||
v1beta3.ProviderKind, providerName, providerNamespace, OperationPost))
|
||||
}
|
||||
|
||||
return githubOpts, nil
|
||||
}
|
||||
|
||||
// getRepoInfoAndGithubClient gets the github client and repository info used by Github and GithubDispatch providers
|
||||
func getRepoInfoAndGithubClient(addr string, token string, certPool *x509.CertPool,
|
||||
proxyURL string, providerName string, providerNamespace string,
|
||||
secretData map[string][]byte, tokenCache *cache.TokenCache) (*repoInfo, error) {
|
||||
|
||||
if len(token) == 0 {
|
||||
if _, ok := secretData[github.KeyAppID]; !ok {
|
||||
return nil, errors.New("github token or github app details must be specified")
|
||||
}
|
||||
|
||||
githubOpts, err := getGitHubAppOptions(providerName, providerNamespace, proxyURL, secretData, tokenCache)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := github.New(githubOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
appToken, err := client.GetToken(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
token = appToken.Token
|
||||
}
|
||||
|
||||
host, id, err := parseGitAddress(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
comp := strings.Split(id, "/")
|
||||
if len(comp) != 2 {
|
||||
return nil, fmt.Errorf("invalid repository id %q", id)
|
||||
}
|
||||
|
||||
baseUrl, err := url.Parse(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
|
||||
tc := oauth2.NewClient(context.Background(), ts)
|
||||
client := gogithub.NewClient(tc)
|
||||
if baseUrl.Host != "github.com" {
|
||||
if certPool != nil {
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
RootCAs: certPool,
|
||||
},
|
||||
}
|
||||
hc := &http.Client{Transport: tr}
|
||||
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, hc)
|
||||
tc = oauth2.NewClient(ctx, ts)
|
||||
}
|
||||
client, err = gogithub.NewClient(tc).WithEnterpriseURLs(host, host)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create enterprise GitHub client: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &repoInfo{comp[0], comp[1], client}, nil
|
||||
}
|
|
@ -17,38 +17,134 @@ limitations under the License.
|
|||
package notifier
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
authgithub "github.com/fluxcd/pkg/git/github"
|
||||
"github.com/fluxcd/pkg/ssh"
|
||||
|
||||
"github.com/google/go-github/v64/github"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewGitHubBasic(t *testing.T) {
|
||||
g, err := NewGitHub("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://github.com/foo/bar", "foobar", nil)
|
||||
g, err := NewGitHub("kustomization/gitops-system/0c9c2e41", "https://github.com/foo/bar", "foobar", nil, "", "", "", nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, g.Owner, "foo")
|
||||
assert.Equal(t, g.Repo, "bar")
|
||||
assert.Equal(t, g.Client.BaseURL.Host, "api.github.com")
|
||||
assert.Equal(t, g.CommitStatus, "kustomization/gitops-system/0c9c2e41")
|
||||
}
|
||||
|
||||
func TestNewEmterpriseGitHubBasic(t *testing.T) {
|
||||
g, err := NewGitHub("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://foobar.com/foo/bar", "foobar", nil)
|
||||
g, err := NewGitHub("kustomization/gitops-system/0c9c2e41", "https://foobar.com/foo/bar", "foobar", nil, "", "", "", nil, nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, g.Owner, "foo")
|
||||
assert.Equal(t, g.Repo, "bar")
|
||||
assert.Equal(t, g.Client.BaseURL.Host, "foobar.com")
|
||||
assert.Equal(t, g.CommitStatus, "kustomization/gitops-system/0c9c2e41")
|
||||
}
|
||||
|
||||
func TestNewGitHubInvalidUrl(t *testing.T) {
|
||||
_, err := NewGitHub("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://github.com/foo/bar/baz", "foobar", nil)
|
||||
_, err := NewGitHub("kustomization/gitops-system/0c9c2e41", "https://github.com/foo/bar/baz", "foobar", nil, "", "", "", nil, nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestNewGitHubEmptyToken(t *testing.T) {
|
||||
_, err := NewGitHub("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://github.com/foo/bar", "", nil)
|
||||
_, err := NewGitHub("kustomization/gitops-system/0c9c2e41", "https://github.com/foo/bar", "", nil, "", "", "", nil, nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestNewGitHubEmptyCommitStatus(t *testing.T) {
|
||||
_, err := NewGitHub("", "https://github.com/foo/bar", "foobar", nil, "", "", "", nil, nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestNewGithubProvider(t *testing.T) {
|
||||
appID := "123"
|
||||
installationID := "456"
|
||||
kp, _ := ssh.GenerateKeyPair(ssh.RSA_4096)
|
||||
expiresAt := time.Now().UTC().Add(time.Hour)
|
||||
|
||||
for _, tt := range []struct {
|
||||
name string
|
||||
secretData map[string][]byte
|
||||
wantErr error
|
||||
}{
|
||||
{
|
||||
name: "nil provider, no token",
|
||||
wantErr: errors.New("github token or github app details must be specified"),
|
||||
},
|
||||
{
|
||||
name: "provider with no github options",
|
||||
secretData: map[string][]byte{},
|
||||
wantErr: errors.New("github token or github app details must be specified"),
|
||||
},
|
||||
{
|
||||
name: "provider with missing app ID in options ",
|
||||
secretData: map[string][]byte{
|
||||
"githubAppInstallationID": []byte(installationID),
|
||||
"githubAppPrivateKey": kp.PrivateKey,
|
||||
},
|
||||
wantErr: errors.New("github token or github app details must be specified"),
|
||||
},
|
||||
{
|
||||
name: "provider with missing app installation ID in options ",
|
||||
secretData: map[string][]byte{
|
||||
"githubAppID": []byte(appID),
|
||||
"githubAppPrivateKey": kp.PrivateKey,
|
||||
},
|
||||
wantErr: errors.New("app installation ID must be provided to use github app authentication"),
|
||||
},
|
||||
{
|
||||
name: "provider with missing app private key in options ",
|
||||
secretData: map[string][]byte{
|
||||
"githubAppID": []byte(appID),
|
||||
"githubAppInstallationID": []byte(installationID),
|
||||
},
|
||||
wantErr: errors.New("private key must be provided to use github app authentication"),
|
||||
},
|
||||
{
|
||||
name: "provider with complete app authentication information",
|
||||
secretData: map[string][]byte{
|
||||
"githubAppID": []byte(appID),
|
||||
"githubAppInstallationID": []byte(installationID),
|
||||
"githubAppPrivateKey": kp.PrivateKey,
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
var response []byte
|
||||
var err error
|
||||
response, err = json.Marshal(&authgithub.AppToken{Token: "access-token", ExpiresAt: expiresAt})
|
||||
assert.Nil(t, err)
|
||||
w.Write(response)
|
||||
}
|
||||
srv := httptest.NewServer(http.HandlerFunc(handler))
|
||||
t.Cleanup(func() {
|
||||
srv.Close()
|
||||
})
|
||||
|
||||
if len(tt.secretData) > 0 {
|
||||
tt.secretData["githubAppBaseURL"] = []byte(srv.URL)
|
||||
}
|
||||
_, err := NewGitHub("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://github.com/foo/bar", "", nil, "", "foo", "bar", tt.secretData, nil)
|
||||
if tt.wantErr != nil {
|
||||
assert.NotNil(t, err)
|
||||
assert.Equal(t, tt.wantErr, err)
|
||||
} else {
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDuplicateGithubStatus(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
|
|
|
@ -31,12 +31,12 @@ import (
|
|||
)
|
||||
|
||||
type GitLab struct {
|
||||
Id string
|
||||
ProviderUID string
|
||||
Client *gitlab.Client
|
||||
Id string
|
||||
CommitStatus string
|
||||
Client *gitlab.Client
|
||||
}
|
||||
|
||||
func NewGitLab(providerUID string, addr string, token string, certPool *x509.CertPool) (*GitLab, error) {
|
||||
func NewGitLab(commitStatus string, addr string, token string, certPool *x509.CertPool) (*GitLab, error) {
|
||||
if len(token) == 0 {
|
||||
return nil, errors.New("gitlab token cannot be empty")
|
||||
}
|
||||
|
@ -46,6 +46,11 @@ func NewGitLab(providerUID string, addr string, token string, certPool *x509.Cer
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// this should never happen
|
||||
if commitStatus == "" {
|
||||
return nil, errors.New("commit status cannot be empty")
|
||||
}
|
||||
|
||||
opts := []gitlab.ClientOptionFunc{gitlab.WithBaseURL(host)}
|
||||
if certPool != nil {
|
||||
tr := &http.Transport{
|
||||
|
@ -62,9 +67,9 @@ func NewGitLab(providerUID string, addr string, token string, certPool *x509.Cer
|
|||
}
|
||||
|
||||
gitlab := &GitLab{
|
||||
Id: id,
|
||||
ProviderUID: providerUID,
|
||||
Client: client,
|
||||
Id: id,
|
||||
CommitStatus: commitStatus,
|
||||
Client: client,
|
||||
}
|
||||
|
||||
return gitlab, nil
|
||||
|
@ -91,7 +96,7 @@ func (g *GitLab) Post(ctx context.Context, event eventv1.Event) error {
|
|||
}
|
||||
|
||||
_, desc := formatNameAndDescription(event)
|
||||
id := generateCommitStatusID(g.ProviderUID, event)
|
||||
id := g.CommitStatus
|
||||
status := &gitlab.CommitStatus{
|
||||
Name: id,
|
||||
SHA: rev,
|
||||
|
|
|
@ -30,12 +30,12 @@ import (
|
|||
)
|
||||
|
||||
func Fuzz_GitLab(f *testing.F) {
|
||||
f.Add("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "token", "org/repo", "revision/abce1", "error", "", []byte{}, []byte(`[{"sha":"abce1","status":"failed","name":"/","description":""}]`))
|
||||
f.Add("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[{"sha":"abce1","status":"failed","name":"/","description":""}]`))
|
||||
f.Add("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "token", "org/repo", "revision/abce1", "info", "Progressing", []byte{}, []byte{})
|
||||
f.Add("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[]`))
|
||||
f.Add("kustomization/gitops-system/0c9c2e41", "token", "org/repo", "revision/abce1", "error", "", []byte{}, []byte(`[{"sha":"abce1","status":"failed","name":"/","description":""}]`))
|
||||
f.Add("kustomization/gitops-system/0c9c2e41", "token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[{"sha":"abce1","status":"failed","name":"/","description":""}]`))
|
||||
f.Add("kustomization/gitops-system/0c9c2e41", "token", "org/repo", "revision/abce1", "info", "Progressing", []byte{}, []byte{})
|
||||
f.Add("kustomization/gitops-system/0c9c2e41", "token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[]`))
|
||||
|
||||
f.Fuzz(func(t *testing.T, uuid, token, urlSuffix, revision, severity, reason string, seed, response []byte) {
|
||||
f.Fuzz(func(t *testing.T, commitStatus, token, urlSuffix, revision, severity, reason string, seed, response []byte) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write(response)
|
||||
io.Copy(io.Discard, r.Body)
|
||||
|
@ -46,7 +46,7 @@ func Fuzz_GitLab(f *testing.F) {
|
|||
var cert x509.CertPool
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&cert)
|
||||
|
||||
gitLab, err := NewGitLab(uuid, fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, &cert)
|
||||
gitLab, err := NewGitLab(commitStatus, fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, &cert)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -23,25 +23,31 @@ import (
|
|||
)
|
||||
|
||||
func TestNewGitLabBasic(t *testing.T) {
|
||||
g, err := NewGitLab("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://gitlab.com/foo/bar", "foobar", nil)
|
||||
g, err := NewGitLab("kustomization/gitops-system/0c9c2e41", "https://gitlab.com/foo/bar", "foobar", nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, g.Id, "foo/bar")
|
||||
assert.Equal(t, g.CommitStatus, "kustomization/gitops-system/0c9c2e41")
|
||||
}
|
||||
|
||||
func TestNewGitLabSubgroups(t *testing.T) {
|
||||
g, err := NewGitLab("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://gitlab.com/foo/bar/baz", "foobar", nil)
|
||||
g, err := NewGitLab("kustomization/gitops-system/0c9c2e41", "https://gitlab.com/foo/bar/baz", "foobar", nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, g.Id, "foo/bar/baz")
|
||||
}
|
||||
|
||||
func TestNewGitLabSelfHosted(t *testing.T) {
|
||||
g, err := NewGitLab("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://example.com/foo/bar", "foo:bar", nil)
|
||||
g, err := NewGitLab("kustomization/gitops-system/0c9c2e41", "https://example.com/foo/bar", "foo:bar", nil)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, g.Id, "foo/bar")
|
||||
assert.Equal(t, g.Client.BaseURL().Host, "example.com")
|
||||
}
|
||||
|
||||
func TestNewGitLabEmptyToken(t *testing.T) {
|
||||
_, err := NewGitLab("0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a", "https://gitlab.com/foo/bar", "", nil)
|
||||
_, err := NewGitLab("kustomization/gitops-system/0c9c2e41", "https://gitlab.com/foo/bar", "", nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestNewGitLabEmptyCommitStatus(t *testing.T) {
|
||||
_, err := NewGitLab("", "https://gitlab.com/foo/bar", "foobar", nil)
|
||||
assert.NotNil(t, err)
|
||||
}
|
||||
|
|
|
@ -142,8 +142,12 @@ func (s *GoogleChat) Post(ctx context.Context, event eventv1.Event) error {
|
|||
Cards: []GoogleChatCard{card},
|
||||
}
|
||||
|
||||
err := postMessage(ctx, s.URL, s.ProxyURL, nil, payload)
|
||||
if err != nil {
|
||||
var opts []postOption
|
||||
if s.ProxyURL != "" {
|
||||
opts = append(opts, withProxy(s.ProxyURL))
|
||||
}
|
||||
|
||||
if err := postMessage(ctx, s.URL, payload, opts...); err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
@ -28,12 +28,12 @@ import (
|
|||
)
|
||||
|
||||
type Grafana struct {
|
||||
URL string
|
||||
Token string
|
||||
ProxyURL string
|
||||
CertPool *x509.CertPool
|
||||
Username string
|
||||
Password string
|
||||
URL string
|
||||
Token string
|
||||
ProxyURL string
|
||||
TLSConfig *tls.Config
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// GraphitePayload represents a Grafana API annotation in Graphite format
|
||||
|
@ -44,19 +44,19 @@ type GraphitePayload struct {
|
|||
}
|
||||
|
||||
// NewGrafana validates the Grafana URL and returns a Grafana object
|
||||
func NewGrafana(URL string, proxyURL string, token string, certPool *x509.CertPool, username string, password string) (*Grafana, error) {
|
||||
func NewGrafana(URL string, proxyURL string, token string, tlsConfig *tls.Config, username string, password string) (*Grafana, error) {
|
||||
_, err := url.ParseRequestURI(URL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid Grafana URL %s", URL)
|
||||
}
|
||||
|
||||
return &Grafana{
|
||||
URL: URL,
|
||||
ProxyURL: proxyURL,
|
||||
Token: token,
|
||||
CertPool: certPool,
|
||||
Username: username,
|
||||
Password: password,
|
||||
URL: URL,
|
||||
ProxyURL: proxyURL,
|
||||
Token: token,
|
||||
Username: username,
|
||||
Password: password,
|
||||
TLSConfig: tlsConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -84,15 +84,24 @@ func (g *Grafana) Post(ctx context.Context, event eventv1.Event) error {
|
|||
Tags: sfields,
|
||||
}
|
||||
|
||||
err := postMessage(ctx, g.URL, g.ProxyURL, g.CertPool, payload, func(request *retryablehttp.Request) {
|
||||
if (g.Username != "" && g.Password != "") && g.Token == "" {
|
||||
request.Header.Add("Authorization", "Basic "+basicAuth(g.Username, g.Password))
|
||||
}
|
||||
if g.Token != "" {
|
||||
request.Header.Add("Authorization", "Bearer "+g.Token)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
opts := []postOption{
|
||||
withRequestModifier(func(req *retryablehttp.Request) {
|
||||
if (g.Username != "" && g.Password != "") && g.Token == "" {
|
||||
req.Header.Add("Authorization", "Basic "+basicAuth(g.Username, g.Password))
|
||||
}
|
||||
if g.Token != "" {
|
||||
req.Header.Add("Authorization", "Bearer "+g.Token)
|
||||
}
|
||||
}),
|
||||
}
|
||||
if g.ProxyURL != "" {
|
||||
opts = append(opts, withProxy(g.ProxyURL))
|
||||
}
|
||||
if g.TLSConfig != nil {
|
||||
opts = append(opts, withTLSConfig(g.TLSConfig))
|
||||
}
|
||||
|
||||
if err := postMessage(ctx, g.URL, payload, opts...); err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -18,7 +18,7 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -43,10 +43,10 @@ func Fuzz_Grafana(f *testing.F) {
|
|||
}))
|
||||
defer ts.Close()
|
||||
|
||||
var cert x509.CertPool
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&cert)
|
||||
var tlsConfig tls.Config
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&tlsConfig)
|
||||
|
||||
grafana, err := NewGrafana(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", token, &cert, username, password)
|
||||
grafana, err := NewGrafana(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", token, &tlsConfig, username, password)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -109,5 +109,5 @@ func (l *Lark) Post(ctx context.Context, event eventv1.Event) error {
|
|||
Card: card,
|
||||
}
|
||||
|
||||
return postMessage(ctx, l.URL, "", nil, payload)
|
||||
return postMessage(ctx, l.URL, payload)
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package notifier
|
|||
import (
|
||||
"context"
|
||||
"crypto/sha1"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
@ -15,10 +15,10 @@ import (
|
|||
)
|
||||
|
||||
type Matrix struct {
|
||||
Token string
|
||||
URL string
|
||||
RoomId string
|
||||
CertPool *x509.CertPool
|
||||
Token string
|
||||
URL string
|
||||
RoomId string
|
||||
TLSConfig *tls.Config
|
||||
}
|
||||
|
||||
type MatrixPayload struct {
|
||||
|
@ -26,17 +26,17 @@ type MatrixPayload struct {
|
|||
MsgType string `json:"msgtype"`
|
||||
}
|
||||
|
||||
func NewMatrix(serverURL, token, roomId string, certPool *x509.CertPool) (*Matrix, error) {
|
||||
func NewMatrix(serverURL, token, roomId string, tlsConfig *tls.Config) (*Matrix, error) {
|
||||
_, err := url.ParseRequestURI(serverURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid Matrix homeserver URL %s: '%w'", serverURL, err)
|
||||
}
|
||||
|
||||
return &Matrix{
|
||||
URL: serverURL,
|
||||
RoomId: roomId,
|
||||
Token: token,
|
||||
CertPool: certPool,
|
||||
URL: serverURL,
|
||||
RoomId: roomId,
|
||||
Token: token,
|
||||
TLSConfig: tlsConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -65,11 +65,17 @@ func (m *Matrix) Post(ctx context.Context, event eventv1.Event) error {
|
|||
MsgType: "m.text",
|
||||
}
|
||||
|
||||
err = postMessage(ctx, fullURL, "", m.CertPool, payload, func(request *retryablehttp.Request) {
|
||||
request.Method = http.MethodPut
|
||||
request.Header.Add("Authorization", "Bearer "+m.Token)
|
||||
})
|
||||
if err != nil {
|
||||
opts := []postOption{
|
||||
withRequestModifier(func(req *retryablehttp.Request) {
|
||||
req.Method = http.MethodPut
|
||||
req.Header.Add("Authorization", "Bearer "+m.Token)
|
||||
}),
|
||||
}
|
||||
if m.TLSConfig != nil {
|
||||
opts = append(opts, withTLSConfig(m.TLSConfig))
|
||||
}
|
||||
|
||||
if err := postMessage(ctx, fullURL, payload, opts...); err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -27,10 +27,10 @@ func Fuzz_Matrix(f *testing.F) {
|
|||
}))
|
||||
defer ts.Close()
|
||||
|
||||
var cert x509.CertPool
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&cert)
|
||||
var tlsConfig tls.Config
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&tlsConfig)
|
||||
|
||||
matrix, err := NewMatrix(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, roomId, &cert)
|
||||
matrix, err := NewMatrix(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, roomId, &tlsConfig)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 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 notifier
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
|
||||
)
|
||||
|
||||
type NopNotifier struct{}
|
||||
|
||||
func (n *NopNotifier) Post(ctx context.Context, event eventv1.Event) error {
|
||||
return nil
|
||||
}
|
|
@ -22,6 +22,9 @@ import (
|
|||
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
|
||||
)
|
||||
|
||||
// OperationPost is the operation name used in cache event metrics
|
||||
const OperationPost = "post"
|
||||
|
||||
type Interface interface {
|
||||
Post(ctx context.Context, event eventv1.Event) error
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
|
@ -28,10 +28,10 @@ import (
|
|||
)
|
||||
|
||||
type Opsgenie struct {
|
||||
URL string
|
||||
ProxyURL string
|
||||
CertPool *x509.CertPool
|
||||
ApiKey string
|
||||
URL string
|
||||
ProxyURL string
|
||||
TLSConfig *tls.Config
|
||||
ApiKey string
|
||||
}
|
||||
|
||||
type OpsgenieAlert struct {
|
||||
|
@ -40,7 +40,7 @@ type OpsgenieAlert struct {
|
|||
Details map[string]string `json:"details"`
|
||||
}
|
||||
|
||||
func NewOpsgenie(hookURL string, proxyURL string, certPool *x509.CertPool, token string) (*Opsgenie, error) {
|
||||
func NewOpsgenie(hookURL string, proxyURL string, tlsConfig *tls.Config, token string) (*Opsgenie, error) {
|
||||
_, err := url.ParseRequestURI(hookURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid Opsgenie hook URL %s: '%w'", hookURL, err)
|
||||
|
@ -51,10 +51,10 @@ func NewOpsgenie(hookURL string, proxyURL string, certPool *x509.CertPool, token
|
|||
}
|
||||
|
||||
return &Opsgenie{
|
||||
URL: hookURL,
|
||||
ProxyURL: proxyURL,
|
||||
CertPool: certPool,
|
||||
ApiKey: token,
|
||||
URL: hookURL,
|
||||
ProxyURL: proxyURL,
|
||||
ApiKey: token,
|
||||
TLSConfig: tlsConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -78,12 +78,21 @@ func (s *Opsgenie) Post(ctx context.Context, event eventv1.Event) error {
|
|||
Details: details,
|
||||
}
|
||||
|
||||
err := postMessage(ctx, s.URL, s.ProxyURL, s.CertPool, payload, func(req *retryablehttp.Request) {
|
||||
req.Header.Set("Authorization", "GenieKey "+s.ApiKey)
|
||||
})
|
||||
opts := []postOption{
|
||||
withRequestModifier(func(req *retryablehttp.Request) {
|
||||
req.Header.Set("Authorization", "GenieKey "+s.ApiKey)
|
||||
}),
|
||||
}
|
||||
if s.ProxyURL != "" {
|
||||
opts = append(opts, withProxy(s.ProxyURL))
|
||||
}
|
||||
if s.TLSConfig != nil {
|
||||
opts = append(opts, withTLSConfig(s.TLSConfig))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if err := postMessage(ctx, s.URL, payload, opts...); err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -42,10 +42,10 @@ func Fuzz_OpsGenie(f *testing.F) {
|
|||
}))
|
||||
defer ts.Close()
|
||||
|
||||
var cert x509.CertPool
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&cert)
|
||||
var tlsConfig tls.Config
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&tlsConfig)
|
||||
|
||||
opsgenie, err := NewOpsgenie(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", &cert, token)
|
||||
opsgenie, err := NewOpsgenie(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", &tlsConfig, token)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"time"
|
||||
|
@ -33,10 +33,10 @@ type PagerDuty struct {
|
|||
Endpoint string
|
||||
RoutingKey string
|
||||
ProxyURL string
|
||||
CertPool *x509.CertPool
|
||||
TLSConfig *tls.Config
|
||||
}
|
||||
|
||||
func NewPagerDuty(endpoint string, proxyURL string, certPool *x509.CertPool, routingKey string) (*PagerDuty, error) {
|
||||
func NewPagerDuty(endpoint string, proxyURL string, tlsConfig *tls.Config, routingKey string) (*PagerDuty, error) {
|
||||
URL, err := url.ParseRequestURI(endpoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid PagerDuty endpoint URL %q: '%w'", endpoint, err)
|
||||
|
@ -45,7 +45,7 @@ func NewPagerDuty(endpoint string, proxyURL string, certPool *x509.CertPool, rou
|
|||
Endpoint: URL.Scheme + "://" + URL.Host,
|
||||
RoutingKey: routingKey,
|
||||
ProxyURL: proxyURL,
|
||||
CertPool: certPool,
|
||||
TLSConfig: tlsConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -54,19 +54,36 @@ func (p *PagerDuty) Post(ctx context.Context, event eventv1.Event) error {
|
|||
if event.HasMetadata(eventv1.MetaCommitStatusKey, eventv1.MetaCommitStatusUpdateValue) || event.HasReason(meta.ProgressingReason) {
|
||||
return nil
|
||||
}
|
||||
e := toPagerDutyV2Event(event, p.RoutingKey)
|
||||
err := postMessage(ctx, p.Endpoint+"/v2/enqueue", p.ProxyURL, p.CertPool, e)
|
||||
if err != nil {
|
||||
|
||||
var opts []postOption
|
||||
if p.ProxyURL != "" {
|
||||
opts = append(opts, withProxy(p.ProxyURL))
|
||||
}
|
||||
if p.TLSConfig != nil {
|
||||
opts = append(opts, withTLSConfig(p.TLSConfig))
|
||||
}
|
||||
|
||||
if err := postMessage(
|
||||
ctx,
|
||||
p.Endpoint+"/v2/enqueue",
|
||||
toPagerDutyV2Event(event, p.RoutingKey),
|
||||
opts...,
|
||||
); err != nil {
|
||||
return fmt.Errorf("failed sending event: %w", err)
|
||||
}
|
||||
|
||||
// Send a change event for info events
|
||||
if event.Severity == eventv1.EventSeverityInfo {
|
||||
ce := toPagerDutyChangeEvent(event, p.RoutingKey)
|
||||
err = postMessage(ctx, p.Endpoint+"/v2/change/enqueue", p.ProxyURL, p.CertPool, ce)
|
||||
if err != nil {
|
||||
if err := postMessage(
|
||||
ctx,
|
||||
p.Endpoint+"/v2/change/enqueue",
|
||||
toPagerDutyChangeEvent(event, p.RoutingKey),
|
||||
opts...,
|
||||
); err != nil {
|
||||
return fmt.Errorf("failed sending change event: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
@ -32,10 +32,10 @@ func Fuzz_PagerDuty(f *testing.F) {
|
|||
ts := httptest.NewServer(mux)
|
||||
defer ts.Close()
|
||||
|
||||
var cert x509.CertPool
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&cert)
|
||||
var tlsConfig tls.Config
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&tlsConfig)
|
||||
|
||||
pd, err := NewPagerDuty(ts.URL, "", &cert, routingKey)
|
||||
pd, err := NewPagerDuty(ts.URL, "", &tlsConfig, routingKey)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
@ -28,26 +28,26 @@ import (
|
|||
|
||||
// Rocket holds the hook URL
|
||||
type Rocket struct {
|
||||
URL string
|
||||
ProxyURL string
|
||||
Username string
|
||||
Channel string
|
||||
CertPool *x509.CertPool
|
||||
URL string
|
||||
ProxyURL string
|
||||
Username string
|
||||
Channel string
|
||||
TLSConfig *tls.Config
|
||||
}
|
||||
|
||||
// NewRocket validates the Rocket URL and returns a Rocket object
|
||||
func NewRocket(hookURL string, proxyURL string, certPool *x509.CertPool, username string, channel string) (*Rocket, error) {
|
||||
func NewRocket(hookURL string, proxyURL string, tlsConfig *tls.Config, username string, channel string) (*Rocket, error) {
|
||||
_, err := url.ParseRequestURI(hookURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid Rocket hook URL %s: '%w'", hookURL, err)
|
||||
}
|
||||
|
||||
return &Rocket{
|
||||
Channel: channel,
|
||||
URL: hookURL,
|
||||
ProxyURL: proxyURL,
|
||||
Username: username,
|
||||
CertPool: certPool,
|
||||
Channel: channel,
|
||||
URL: hookURL,
|
||||
ProxyURL: proxyURL,
|
||||
Username: username,
|
||||
TLSConfig: tlsConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -83,8 +83,15 @@ func (s *Rocket) Post(ctx context.Context, event eventv1.Event) error {
|
|||
|
||||
payload.Attachments = []SlackAttachment{a}
|
||||
|
||||
err := postMessage(ctx, s.URL, s.ProxyURL, s.CertPool, payload)
|
||||
if err != nil {
|
||||
var opts []postOption
|
||||
if s.ProxyURL != "" {
|
||||
opts = append(opts, withProxy(s.ProxyURL))
|
||||
}
|
||||
if s.TLSConfig != nil {
|
||||
opts = append(opts, withTLSConfig(s.TLSConfig))
|
||||
}
|
||||
|
||||
if err := postMessage(ctx, s.URL, payload, opts...); err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -18,7 +18,6 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -42,10 +41,7 @@ func Fuzz_Rocket(f *testing.F) {
|
|||
}))
|
||||
defer ts.Close()
|
||||
|
||||
var cert x509.CertPool
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&cert)
|
||||
|
||||
rocket, err := NewRocket(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", &cert, username, channel)
|
||||
rocket, err := NewRocket(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", nil, username, channel)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -34,6 +34,10 @@ type Sentry struct {
|
|||
|
||||
// NewSentry creates a Sentry client from the provided Data Source Name (DSN)
|
||||
func NewSentry(certPool *x509.CertPool, dsn string, environment string) (*Sentry, error) {
|
||||
if dsn == "" {
|
||||
return nil, fmt.Errorf("DSN cannot be empty")
|
||||
}
|
||||
|
||||
tr := &http.Transport{}
|
||||
if certPool != nil {
|
||||
tr = &http.Transport{
|
||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package notifier
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -25,15 +26,42 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewSentry(t *testing.T) {
|
||||
s, err := NewSentry(nil, "https://test@localhost/1", "foo")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, s.Client.Options().Dsn, "https://test@localhost/1")
|
||||
assert.Equal(t, s.Client.Options().Environment, "foo")
|
||||
tests := []struct {
|
||||
name string
|
||||
dsn string
|
||||
environment string
|
||||
err error
|
||||
}{
|
||||
{
|
||||
name: "valid DSN",
|
||||
dsn: "https://test@localhost/1",
|
||||
environment: "foo",
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
name: "empty DSN",
|
||||
dsn: "",
|
||||
environment: "foo",
|
||||
err: errors.New("DSN cannot be empty"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s, err := NewSentry(nil, tt.dsn, tt.environment)
|
||||
if tt.err != nil {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tt.err, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, s.Client.Options().Dsn, tt.dsn)
|
||||
assert.Equal(t, s.Client.Options().Environment, tt.environment)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToSentryEvent(t *testing.T) {
|
||||
|
|
|
@ -18,7 +18,8 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
@ -29,12 +30,12 @@ import (
|
|||
|
||||
// Slack holds the hook URL
|
||||
type Slack struct {
|
||||
URL string
|
||||
ProxyURL string
|
||||
Token string
|
||||
Username string
|
||||
Channel string
|
||||
CertPool *x509.CertPool
|
||||
URL string
|
||||
ProxyURL string
|
||||
Token string
|
||||
Username string
|
||||
Channel string
|
||||
TLSConfig *tls.Config
|
||||
}
|
||||
|
||||
// SlackPayload holds the channel and attachments
|
||||
|
@ -63,19 +64,19 @@ type SlackField struct {
|
|||
}
|
||||
|
||||
// NewSlack validates the Slack URL and returns a Slack object
|
||||
func NewSlack(hookURL string, proxyURL string, token string, certPool *x509.CertPool, username string, channel string) (*Slack, error) {
|
||||
func NewSlack(hookURL string, proxyURL string, token string, tlsConfig *tls.Config, username string, channel string) (*Slack, error) {
|
||||
_, err := url.ParseRequestURI(hookURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid Slack hook URL %s: '%w'", hookURL, err)
|
||||
}
|
||||
|
||||
return &Slack{
|
||||
Channel: channel,
|
||||
Username: username,
|
||||
URL: hookURL,
|
||||
ProxyURL: proxyURL,
|
||||
Token: token,
|
||||
CertPool: certPool,
|
||||
Channel: channel,
|
||||
Username: username,
|
||||
URL: hookURL,
|
||||
ProxyURL: proxyURL,
|
||||
Token: token,
|
||||
TLSConfig: tlsConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -118,13 +119,49 @@ func (s *Slack) Post(ctx context.Context, event eventv1.Event) error {
|
|||
|
||||
payload.Attachments = []SlackAttachment{a}
|
||||
|
||||
err := postMessage(ctx, s.URL, s.ProxyURL, s.CertPool, payload, func(request *retryablehttp.Request) {
|
||||
if s.Token != "" {
|
||||
request.Header.Add("Authorization", "Bearer "+s.Token)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
opts := []postOption{
|
||||
withRequestModifier(func(request *retryablehttp.Request) {
|
||||
if s.Token != "" {
|
||||
request.Header.Add("Authorization", "Bearer "+s.Token)
|
||||
}
|
||||
}),
|
||||
}
|
||||
if s.ProxyURL != "" {
|
||||
opts = append(opts, withProxy(s.ProxyURL))
|
||||
}
|
||||
if s.TLSConfig != nil {
|
||||
opts = append(opts, withTLSConfig(s.TLSConfig))
|
||||
}
|
||||
if s.URL == "https://slack.com/api/chat.postMessage" {
|
||||
opts = append(opts, withResponseValidator(validateSlackResponse))
|
||||
}
|
||||
|
||||
if err := postMessage(ctx, s.URL, payload, opts...); err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateSlackResponse validates that a chat.postMessage API response is successful.
|
||||
// chat.postMessage API always returns 200 OK.
|
||||
// See https://api.slack.com/methods/chat.postMessage.
|
||||
//
|
||||
// On the other hand, incoming webhooks return more expressive HTTP status codes.
|
||||
// See https://api.slack.com/messaging/webhooks#handling_errors.
|
||||
func validateSlackResponse(_ int, body []byte) error {
|
||||
type slackResponse struct {
|
||||
Ok bool `json:"ok"`
|
||||
Error string `json:"error"`
|
||||
}
|
||||
|
||||
slackResp := slackResponse{}
|
||||
if err := json.Unmarshal(body, &slackResp); err != nil {
|
||||
return fmt.Errorf("unable to unmarshal response body: %w", err)
|
||||
}
|
||||
|
||||
if slackResp.Ok {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("Slack responded with error: %s", slackResp.Error)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -43,10 +43,10 @@ func Fuzz_Slack(f *testing.F) {
|
|||
}))
|
||||
defer ts.Close()
|
||||
|
||||
var cert x509.CertPool
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&cert)
|
||||
var tlsConfig tls.Config
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&tlsConfig)
|
||||
|
||||
slack, err := NewSlack(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", token, &cert, username, channel)
|
||||
slack, err := NewSlack(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", token, &tlsConfig, username, channel)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -58,3 +58,18 @@ func TestSlack_PostUpdate(t *testing.T) {
|
|||
err = slack.Post(context.TODO(), event)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestSlack_ValidateResponse(t *testing.T) {
|
||||
body := []byte(`{
|
||||
"ok": true
|
||||
}`)
|
||||
err := validateSlackResponse(http.StatusOK, body)
|
||||
require.NoError(t, err)
|
||||
|
||||
body = []byte(`{
|
||||
"ok": false,
|
||||
"error": "too_many_attachments"
|
||||
}`)
|
||||
err = validateSlackResponse(http.StatusOK, body)
|
||||
require.ErrorContains(t, err, "Slack responded with error: too_many_attachments")
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"slices"
|
||||
|
@ -39,10 +39,10 @@ const (
|
|||
|
||||
// MS Teams holds the incoming webhook URL
|
||||
type MSTeams struct {
|
||||
URL string
|
||||
ProxyURL string
|
||||
CertPool *x509.CertPool
|
||||
Schema int
|
||||
URL string
|
||||
ProxyURL string
|
||||
Schema int
|
||||
TLSConfig *tls.Config
|
||||
}
|
||||
|
||||
// MSTeamsPayload holds the message card data
|
||||
|
@ -121,17 +121,17 @@ type msAdaptiveCardFact struct {
|
|||
}
|
||||
|
||||
// NewMSTeams validates the MS Teams URL and returns a MSTeams object
|
||||
func NewMSTeams(hookURL string, proxyURL string, certPool *x509.CertPool) (*MSTeams, error) {
|
||||
func NewMSTeams(hookURL string, proxyURL string, tlsConfig *tls.Config) (*MSTeams, error) {
|
||||
u, err := url.ParseRequestURI(hookURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid MS Teams webhook URL %s: '%w'", hookURL, err)
|
||||
}
|
||||
|
||||
provider := &MSTeams{
|
||||
URL: hookURL,
|
||||
ProxyURL: proxyURL,
|
||||
CertPool: certPool,
|
||||
Schema: msTeamsSchemaAdaptiveCard,
|
||||
URL: hookURL,
|
||||
ProxyURL: proxyURL,
|
||||
Schema: msTeamsSchemaAdaptiveCard,
|
||||
TLSConfig: tlsConfig,
|
||||
}
|
||||
|
||||
// Check if the webhook URL is the deprecated connector and update the schema accordingly.
|
||||
|
@ -161,8 +161,15 @@ func (s *MSTeams) Post(ctx context.Context, event eventv1.Event) error {
|
|||
payload = buildMSTeamsAdaptiveCardPayload(&event, objName)
|
||||
}
|
||||
|
||||
err := postMessage(ctx, s.URL, s.ProxyURL, s.CertPool, payload)
|
||||
if err != nil {
|
||||
var opts []postOption
|
||||
if s.ProxyURL != "" {
|
||||
opts = append(opts, withProxy(s.ProxyURL))
|
||||
}
|
||||
if s.TLSConfig != nil {
|
||||
opts = append(opts, withTLSConfig(s.TLSConfig))
|
||||
}
|
||||
|
||||
if err := postMessage(ctx, s.URL, payload, opts...); err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -43,10 +42,7 @@ func Fuzz_MSTeams(f *testing.F) {
|
|||
}))
|
||||
defer ts.Close()
|
||||
|
||||
var cert x509.CertPool
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&cert)
|
||||
|
||||
teams, err := NewMSTeams(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", &cert)
|
||||
teams, err := NewMSTeams(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -4,27 +4,46 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/containrrr/shoutrrr"
|
||||
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
|
||||
)
|
||||
|
||||
const (
|
||||
telegramBaseURL = "https://api.telegram.org/bot%s"
|
||||
sendMessageMethodName = "sendMessage"
|
||||
)
|
||||
|
||||
type Telegram struct {
|
||||
Channel string
|
||||
Token string
|
||||
send func(url string, message string) error // this allows the send function to be overridden for testing
|
||||
url string
|
||||
ProxyURL string
|
||||
Channel string
|
||||
Token string
|
||||
}
|
||||
|
||||
func NewTelegram(channel, token string) (*Telegram, error) {
|
||||
// TelegramPayload represents the payload sent to Telegram Bot API
|
||||
// Reference: https://core.telegram.org/bots/api#sendmessage
|
||||
type TelegramPayload struct {
|
||||
ChatID string `json:"chat_id"` // Unique identifier for the target chat
|
||||
Text string `json:"text"` // Text of the message to be sent
|
||||
ParseMode string `json:"parse_mode"` // Mode for parsing entities in the message text
|
||||
}
|
||||
|
||||
func NewTelegram(proxyURL, channel, token string) (*Telegram, error) {
|
||||
if channel == "" {
|
||||
return nil, errors.New("empty Telegram channel")
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
return nil, errors.New("empty Telegram token")
|
||||
}
|
||||
|
||||
return &Telegram{
|
||||
Channel: channel,
|
||||
Token: token,
|
||||
send: shoutrrr.Send,
|
||||
url: fmt.Sprintf(telegramBaseURL, token),
|
||||
ProxyURL: proxyURL,
|
||||
Channel: channel,
|
||||
Token: token,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -46,9 +65,24 @@ func (t *Telegram) Post(ctx context.Context, event eventv1.Event) error {
|
|||
metadata = metadata + fmt.Sprintf("\\- *%s*: %s\n", escapeString(k), escapeString(v))
|
||||
}
|
||||
message := fmt.Sprintf("*%s*\n%s\n%s", escapeString(heading), escapeString(event.Message), metadata)
|
||||
url := fmt.Sprintf("telegram://%s@telegram?channels=%s&parseMode=markDownv2", t.Token, t.Channel)
|
||||
err := t.send(url, message)
|
||||
return err
|
||||
|
||||
payload := TelegramPayload{
|
||||
ChatID: t.Channel,
|
||||
Text: message,
|
||||
ParseMode: "MarkdownV2", // https://core.telegram.org/bots/api#markdownv2-style
|
||||
}
|
||||
|
||||
apiURL, err := url.JoinPath(t.url, sendMessageMethodName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to construct API URL: %w", err)
|
||||
}
|
||||
|
||||
var opts []postOption
|
||||
if t.ProxyURL != "" {
|
||||
opts = append(opts, withProxy(t.ProxyURL))
|
||||
}
|
||||
|
||||
return postMessage(ctx, apiURL, payload, opts...)
|
||||
}
|
||||
|
||||
// The telegram API requires that some special characters are escaped
|
||||
|
|
|
@ -31,22 +31,21 @@ import (
|
|||
|
||||
func TestTelegram_Post(t *testing.T) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
require.Equal(t, http.MethodPost, r.Method)
|
||||
require.Equal(t, "/sendMessage", r.URL.Path)
|
||||
require.Equal(t, "application/json", r.Header.Get("Content-Type"))
|
||||
|
||||
b, err := io.ReadAll(r.Body)
|
||||
require.NoError(t, err)
|
||||
|
||||
var payload = WebexPayload{}
|
||||
var payload TelegramPayload
|
||||
err = json.Unmarshal(b, &payload)
|
||||
require.NoError(t, err)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
telegram, err := NewTelegram("channel", "token")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "channel", payload.ChatID)
|
||||
require.Equal(t, "MarkdownV2", payload.ParseMode)
|
||||
|
||||
telegram.send = func(url, message string) error {
|
||||
require.Equal(t, "telegram://token@telegram?channels=channel&parseMode=markDownv2", url)
|
||||
|
||||
lines := strings.Split(message, "\n")
|
||||
lines := strings.Split(payload.Text, "\n")
|
||||
require.Len(t, lines, 5)
|
||||
slices.Sort(lines[2:4])
|
||||
require.Equal(t, "*💫 gitrepository/webapp/gitops\\-system*", lines[0])
|
||||
|
@ -57,8 +56,14 @@ func TestTelegram_Post(t *testing.T) {
|
|||
"",
|
||||
}, lines[2:])
|
||||
|
||||
return nil
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
telegram, err := NewTelegram("", "channel", "token")
|
||||
require.NoError(t, err)
|
||||
|
||||
telegram.url = ts.URL
|
||||
|
||||
ev := testEvent()
|
||||
ev.Metadata["kubernetes.io/somekey"] = "some.value"
|
||||
|
|
|
@ -55,14 +55,6 @@ func formatNameAndDescription(event eventv1.Event) (string, string) {
|
|||
return name, desc
|
||||
}
|
||||
|
||||
// generateCommitStatusID returns a unique ID per cluster based on the Provider UID,
|
||||
// involved object kind and name.
|
||||
func generateCommitStatusID(providerUID string, event eventv1.Event) string {
|
||||
uidParts := strings.Split(providerUID, "-")
|
||||
id := fmt.Sprintf("%s/%s/%s", event.InvolvedObject.Kind, event.InvolvedObject.Name, uidParts[0])
|
||||
return strings.ToLower(id)
|
||||
}
|
||||
|
||||
func splitCamelcase(src string) (entries []string) {
|
||||
// don't split invalid utf8
|
||||
if !utf8.ValidString(src) {
|
||||
|
|
|
@ -150,33 +150,3 @@ func TestUtil_BasicAuth(t *testing.T) {
|
|||
s := basicAuth(username, password)
|
||||
require.Equal(t, "dXNlcjpwYXNzd29yZA==", s)
|
||||
}
|
||||
|
||||
func TestUtil_GenerateCommitStatusID(t *testing.T) {
|
||||
statusIDTests := []struct {
|
||||
name string
|
||||
providerUID string
|
||||
event eventv1.Event
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "simple event case",
|
||||
providerUID: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
event: eventv1.Event{
|
||||
InvolvedObject: corev1.ObjectReference{
|
||||
Kind: "Kustomization",
|
||||
Name: "gitops-system",
|
||||
},
|
||||
Reason: "ApplySucceeded",
|
||||
},
|
||||
want: "kustomization/gitops-system/0c9c2e41",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range statusIDTests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
id := generateCommitStatusID(tt.providerUID, tt.event)
|
||||
|
||||
require.Equal(t, tt.want, id)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
@ -51,9 +51,8 @@ type Webex struct {
|
|||
Token string
|
||||
|
||||
// optional: use a proxy as needed
|
||||
ProxyURL string
|
||||
// optional: x509 cert is no longer needed to post to a webex space
|
||||
CertPool *x509.CertPool
|
||||
ProxyURL string
|
||||
TLSConfig *tls.Config
|
||||
}
|
||||
|
||||
// WebexPayload holds the message text
|
||||
|
@ -63,7 +62,7 @@ type WebexPayload struct {
|
|||
}
|
||||
|
||||
// NewWebex validates the Webex URL and returns a Webex object
|
||||
func NewWebex(hookURL, proxyURL string, certPool *x509.CertPool, channel string, token string) (*Webex, error) {
|
||||
func NewWebex(hookURL, proxyURL string, tlsConfig *tls.Config, channel string, token string) (*Webex, error) {
|
||||
|
||||
_, err := url.ParseRequestURI(hookURL)
|
||||
if err != nil {
|
||||
|
@ -71,11 +70,11 @@ func NewWebex(hookURL, proxyURL string, certPool *x509.CertPool, channel string,
|
|||
}
|
||||
|
||||
return &Webex{
|
||||
URL: hookURL,
|
||||
ProxyURL: proxyURL,
|
||||
CertPool: certPool,
|
||||
RoomId: channel,
|
||||
Token: token,
|
||||
URL: hookURL,
|
||||
ProxyURL: proxyURL,
|
||||
RoomId: channel,
|
||||
Token: token,
|
||||
TLSConfig: tlsConfig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -108,10 +107,21 @@ func (s *Webex) Post(ctx context.Context, event eventv1.Event) error {
|
|||
Markdown: s.CreateMarkdown(&event),
|
||||
}
|
||||
|
||||
if err := postMessage(ctx, s.URL, s.ProxyURL, s.CertPool, payload, func(request *retryablehttp.Request) {
|
||||
request.Header.Add("Authorization", "Bearer "+s.Token)
|
||||
}); err != nil {
|
||||
opts := []postOption{
|
||||
withRequestModifier(func(req *retryablehttp.Request) {
|
||||
req.Header.Add("Authorization", "Bearer "+s.Token)
|
||||
}),
|
||||
}
|
||||
if s.ProxyURL != "" {
|
||||
opts = append(opts, withProxy(s.ProxyURL))
|
||||
}
|
||||
if s.TLSConfig != nil {
|
||||
opts = append(opts, withTLSConfig(s.TLSConfig))
|
||||
}
|
||||
|
||||
if err := postMessage(ctx, s.URL, payload, opts...); err != nil {
|
||||
return fmt.Errorf("postMessage failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ package notifier
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -42,10 +42,10 @@ func Fuzz_Webex(f *testing.F) {
|
|||
}))
|
||||
defer ts.Close()
|
||||
|
||||
var cert x509.CertPool
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&cert)
|
||||
var tlsConfig tls.Config
|
||||
_ = fuzz.NewConsumer(seed).GenerateStruct(&tlsConfig)
|
||||
|
||||
webex, err := NewWebex(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", &cert, channel, token)
|
||||
webex, err := NewWebex(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", &tlsConfig, channel, token)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package server
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
@ -37,7 +36,10 @@ import (
|
|||
"sigs.k8s.io/yaml"
|
||||
|
||||
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
|
||||
"github.com/fluxcd/pkg/auth"
|
||||
"github.com/fluxcd/pkg/cache"
|
||||
"github.com/fluxcd/pkg/masktoken"
|
||||
"github.com/fluxcd/pkg/runtime/secrets"
|
||||
|
||||
apiv1 "github.com/fluxcd/notification-controller/api/v1"
|
||||
apiv1beta3 "github.com/fluxcd/notification-controller/api/v1beta3"
|
||||
|
@ -79,7 +81,12 @@ func (s *EventServer) handleEvent() func(w http.ResponseWriter, r *http.Request)
|
|||
// Dispatch notifications.
|
||||
for i := range alerts {
|
||||
alert := &alerts[i]
|
||||
alertLogger := eventLogger.WithValues(alert.Kind, client.ObjectKeyFromObject(alert))
|
||||
alertLogger := eventLogger.WithValues(
|
||||
"alert", map[string]string{
|
||||
"name": alert.Name,
|
||||
"namespace": alert.Namespace,
|
||||
"providerName": alert.Spec.ProviderRef.Name,
|
||||
})
|
||||
ctx := log.IntoContext(ctx, alertLogger)
|
||||
if err := s.dispatchNotification(ctx, event, alert); err != nil {
|
||||
alertLogger.Error(err, "failed to dispatch notification")
|
||||
|
@ -251,109 +258,191 @@ func (s *EventServer) getNotificationParams(ctx context.Context, event *eventv1.
|
|||
return nil, nil, "", 0, nil
|
||||
}
|
||||
|
||||
sender, token, err := createNotifier(ctx, s.kubeClient, provider)
|
||||
// Check object-level workload identity feature gate.
|
||||
if provider.Spec.ServiceAccountName != "" && !auth.IsObjectLevelWorkloadIdentityEnabled() {
|
||||
return nil, nil, "", 0, fmt.Errorf(
|
||||
"to use spec.serviceAccountName for provider authentication please enable the %s feature gate in the controller",
|
||||
auth.FeatureGateObjectLevelWorkloadIdentity)
|
||||
}
|
||||
|
||||
// Create a copy of the event and combine event metadata
|
||||
notification := *event.DeepCopy()
|
||||
s.combineEventMetadata(ctx, ¬ification, alert)
|
||||
|
||||
// Create a commit status for the given provider and event, if applicable.
|
||||
commitStatus, err := createCommitStatus(ctx, &provider, ¬ification, alert)
|
||||
if err != nil {
|
||||
return nil, nil, "", 0, fmt.Errorf("failed to create commit status: %w", err)
|
||||
}
|
||||
|
||||
sender, token, err := createNotifier(ctx, s.kubeClient, &provider, commitStatus, s.tokenCache)
|
||||
if err != nil {
|
||||
return nil, nil, "", 0, fmt.Errorf("failed to initialize notifier for provider '%s': %w", provider.Name, err)
|
||||
}
|
||||
|
||||
notification := *event.DeepCopy()
|
||||
s.combineEventMetadata(ctx, ¬ification, alert)
|
||||
|
||||
return sender, ¬ification, token, provider.GetTimeout(), nil
|
||||
}
|
||||
|
||||
// createNotifier returns a notifier.Interface for the given Provider.
|
||||
func createNotifier(ctx context.Context, kubeClient client.Client, provider apiv1beta3.Provider) (notifier.Interface, string, error) {
|
||||
logger := log.FromContext(ctx)
|
||||
// createCommitStatus creates a commit status for the given provider and event.
|
||||
// If the provider has a commitStatusExpr, it will be used to compute a commit status.
|
||||
// Otherwise, a default commit status will be generated using the Provider UID and event metadata.
|
||||
// If the provider is not a git provider, the commit status will be an empty string.
|
||||
// If the commitStatusExpr fails to compile or is invalid, an error will be returned.
|
||||
func createCommitStatus(ctx context.Context, provider *apiv1beta3.Provider, event *eventv1.Event, alert *apiv1beta3.Alert) (commitStatus string, err error) {
|
||||
if !isGitProvider(provider.Spec.Type) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if provider.Spec.CommitStatusExpr != "" {
|
||||
commitStatus, err = newCommitStatus(ctx, provider.Spec.CommitStatusExpr, event, alert, provider)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to evaluate the spec.commitStatusExpr CEL expression for the event: %w", err)
|
||||
}
|
||||
} else {
|
||||
commitStatus = generateDefaultCommitStatus(string(provider.UID), *event)
|
||||
}
|
||||
|
||||
return commitStatus, nil
|
||||
}
|
||||
|
||||
// extractAuthFromSecret extracts notification-controller specific fields from the provider's secret
|
||||
// that are not handled by pkg/runtime/secrets (address, proxy, token, headers).
|
||||
// StandardizedSecret fields like BasicAuth, TLS, and ProxySecretRef are handled separately.
|
||||
func extractAuthFromSecret(ctx context.Context, kubeClient client.Client, provider *apiv1beta3.Provider) ([]notifier.Option, map[string][]byte, error) {
|
||||
options := []notifier.Option{}
|
||||
|
||||
secretName := types.NamespacedName{Namespace: provider.Namespace, Name: provider.Spec.SecretRef.Name}
|
||||
var secret corev1.Secret
|
||||
if err := kubeClient.Get(ctx, secretName, &secret); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to read secret: %w", err)
|
||||
}
|
||||
|
||||
if val, ok := secret.Data["address"]; ok {
|
||||
if len(val) > 2048 {
|
||||
return nil, nil, fmt.Errorf("invalid address in secret: address exceeds maximum length of %d bytes", 2048)
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := secret.Data["proxy"]; ok {
|
||||
deprecatedProxy := strings.TrimSpace(string(val))
|
||||
if _, err := url.Parse(deprecatedProxy); err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid 'proxy' in secret '%s'", secretName.String())
|
||||
}
|
||||
log.FromContext(ctx).Error(nil, "warning: specifying proxy with 'proxy' key in the referenced secret is deprecated, use spec.proxySecretRef with 'address' key instead. Support for the 'proxy' key will be removed in v1.")
|
||||
options = append(options, notifier.WithProxyURL(deprecatedProxy))
|
||||
}
|
||||
|
||||
if val, ok := secret.Data[secrets.KeyToken]; ok {
|
||||
options = append(options, notifier.WithToken(strings.TrimSpace(string(val))))
|
||||
}
|
||||
|
||||
if h, ok := secret.Data["headers"]; ok {
|
||||
headers := make(map[string]string)
|
||||
if err := yaml.Unmarshal(h, &headers); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to read headers from secret: %w", err)
|
||||
}
|
||||
options = append(options, notifier.WithHeaders(headers))
|
||||
}
|
||||
|
||||
if user, ok := secret.Data["username"]; ok {
|
||||
if pass, ok := secret.Data["password"]; ok {
|
||||
options = append(options, notifier.WithUsername(strings.TrimSpace(string(user))))
|
||||
options = append(options, notifier.WithPassword(strings.TrimSpace(string(pass))))
|
||||
}
|
||||
}
|
||||
|
||||
return options, secret.Data, nil
|
||||
}
|
||||
|
||||
// createNotifier constructs a notifier interface from the provider configuration,
|
||||
// handling authentication, proxy settings, and TLS configuration.
|
||||
func createNotifier(ctx context.Context, kubeClient client.Client, provider *apiv1beta3.Provider,
|
||||
commitStatus string, tokenCache *cache.TokenCache) (notifier.Interface, string, error) {
|
||||
options := []notifier.Option{
|
||||
notifier.WithTokenClient(kubeClient),
|
||||
notifier.WithProviderName(provider.Name),
|
||||
notifier.WithProviderNamespace(provider.Namespace),
|
||||
}
|
||||
|
||||
if commitStatus != "" {
|
||||
options = append(options, notifier.WithCommitStatus(commitStatus))
|
||||
}
|
||||
|
||||
if provider.Spec.Channel != "" {
|
||||
options = append(options, notifier.WithChannel(provider.Spec.Channel))
|
||||
}
|
||||
|
||||
if provider.Spec.Username != "" {
|
||||
options = append(options, notifier.WithUsername(provider.Spec.Username))
|
||||
}
|
||||
|
||||
if provider.Spec.ServiceAccountName != "" {
|
||||
options = append(options, notifier.WithServiceAccount(provider.Spec.ServiceAccountName))
|
||||
}
|
||||
|
||||
if tokenCache != nil {
|
||||
options = append(options, notifier.WithTokenCache(tokenCache))
|
||||
}
|
||||
|
||||
// TODO: Remove deprecated proxy handling when Provider v1 is released.
|
||||
if provider.Spec.Proxy != "" {
|
||||
log.FromContext(ctx).Error(nil, "warning: spec.proxy is deprecated, please use spec.proxySecretRef instead. Support for this field will be removed in v1.")
|
||||
options = append(options, notifier.WithProxyURL(provider.Spec.Proxy))
|
||||
}
|
||||
|
||||
webhook := provider.Spec.Address
|
||||
username := provider.Spec.Username
|
||||
proxy := provider.Spec.Proxy
|
||||
token := ""
|
||||
password := ""
|
||||
headers := make(map[string]string)
|
||||
if provider.Spec.SecretRef != nil {
|
||||
var secret corev1.Secret
|
||||
secretName := types.NamespacedName{Namespace: provider.Namespace, Name: provider.Spec.SecretRef.Name}
|
||||
var token string
|
||||
var secretData map[string][]byte
|
||||
|
||||
err := kubeClient.Get(ctx, secretName, &secret)
|
||||
if provider.Spec.SecretRef != nil {
|
||||
secretOptions, sData, err := extractAuthFromSecret(ctx, kubeClient, provider)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to read secret: %w", err)
|
||||
return nil, "", err
|
||||
}
|
||||
secretData = sData
|
||||
options = append(options, secretOptions...)
|
||||
|
||||
if secretData != nil {
|
||||
options = append(options, notifier.WithSecretData(secretData))
|
||||
}
|
||||
|
||||
if val, ok := secret.Data["address"]; ok {
|
||||
if len(val) > 2048 {
|
||||
return nil, "", fmt.Errorf("invalid address in secret: address exceeds maximum length of %d bytes", 2048)
|
||||
}
|
||||
if val, ok := secretData["address"]; ok {
|
||||
webhook = strings.TrimSpace(string(val))
|
||||
}
|
||||
|
||||
if val, ok := secret.Data["password"]; ok {
|
||||
password = strings.TrimSpace(string(val))
|
||||
}
|
||||
|
||||
if val, ok := secret.Data["proxy"]; ok {
|
||||
proxy = strings.TrimSpace(string(val))
|
||||
_, err := url.Parse(proxy)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("invalid proxy in secret '%s': %w", proxy, err)
|
||||
}
|
||||
}
|
||||
|
||||
if val, ok := secret.Data["token"]; ok {
|
||||
if val, ok := secretData[secrets.KeyToken]; ok {
|
||||
token = strings.TrimSpace(string(val))
|
||||
}
|
||||
|
||||
if val, ok := secret.Data["username"]; ok {
|
||||
username = strings.TrimSpace(string(val))
|
||||
}
|
||||
|
||||
if h, ok := secret.Data["headers"]; ok {
|
||||
err := yaml.Unmarshal(h, &headers)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to read headers from secret: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var certPool *x509.CertPool
|
||||
if provider.Spec.CertSecretRef != nil {
|
||||
var secret corev1.Secret
|
||||
secretName := types.NamespacedName{Namespace: provider.Namespace, Name: provider.Spec.CertSecretRef.Name}
|
||||
|
||||
err := kubeClient.Get(ctx, secretName, &secret)
|
||||
if provider.Spec.ProxySecretRef != nil {
|
||||
secretRef := types.NamespacedName{
|
||||
Name: provider.Spec.ProxySecretRef.Name,
|
||||
Namespace: provider.GetNamespace(),
|
||||
}
|
||||
proxyURL, err := secrets.ProxyURLFromSecretRef(ctx, kubeClient, secretRef)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to read cert secret: %w", err)
|
||||
}
|
||||
|
||||
switch secret.Type {
|
||||
case corev1.SecretTypeOpaque, corev1.SecretTypeTLS, "":
|
||||
default:
|
||||
return nil, "", fmt.Errorf("cannot use Secret '%s' to get TLS certificate: invalid Secret type: '%s'", secret.Name, secret.Type)
|
||||
}
|
||||
|
||||
caFile, ok := secret.Data["ca.crt"]
|
||||
if !ok {
|
||||
// TODO: Drop support for "caFile" field in v1 Provider API.
|
||||
caFile, ok = secret.Data["caFile"]
|
||||
if !ok {
|
||||
return nil, "", fmt.Errorf("no 'ca.crt' key found in Secret '%s'", secret.Name)
|
||||
}
|
||||
logger.Info("warning: specifying CA cert via 'caFile' is deprecated, please use 'ca.crt' instead")
|
||||
}
|
||||
|
||||
certPool = x509.NewCertPool()
|
||||
ok = certPool.AppendCertsFromPEM(caFile)
|
||||
if !ok {
|
||||
return nil, "", fmt.Errorf("could not append to cert pool")
|
||||
return nil, "", fmt.Errorf("failed to get proxy URL: %w", err)
|
||||
}
|
||||
options = append(options, notifier.WithProxyURL(proxyURL.String()))
|
||||
}
|
||||
|
||||
if webhook == "" {
|
||||
return nil, "", fmt.Errorf("provider has no address")
|
||||
if provider.Spec.CertSecretRef != nil {
|
||||
secretRef := types.NamespacedName{
|
||||
Name: provider.Spec.CertSecretRef.Name,
|
||||
Namespace: provider.GetNamespace(),
|
||||
}
|
||||
tlsConfig, err := secrets.TLSConfigFromSecretRef(ctx, kubeClient, secretRef)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to get TLS config: %w", err)
|
||||
}
|
||||
options = append(options, notifier.WithTLSConfig(tlsConfig))
|
||||
}
|
||||
|
||||
factory := notifier.NewFactory(webhook, proxy, username, provider.Spec.Channel, token, headers, certPool, password, string(provider.UID))
|
||||
if webhook != "" {
|
||||
options = append(options, notifier.WithURL(webhook))
|
||||
}
|
||||
|
||||
factory := notifier.NewFactory(ctx, options...)
|
||||
sender, err := factory.Notifier(provider.Spec.Type)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to initialize notifier: %w", err)
|
||||
|
|
|
@ -18,12 +18,21 @@ package server
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
@ -35,11 +44,15 @@ import (
|
|||
|
||||
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
|
||||
"github.com/fluxcd/pkg/apis/meta"
|
||||
"github.com/fluxcd/pkg/auth"
|
||||
|
||||
apiv1 "github.com/fluxcd/notification-controller/api/v1"
|
||||
apiv1beta3 "github.com/fluxcd/notification-controller/api/v1beta3"
|
||||
"github.com/fluxcd/notification-controller/internal/notifier"
|
||||
)
|
||||
|
||||
var fixedNow = time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
func TestFilterAlertsForEvent(t *testing.T) {
|
||||
testNamespace := "foo-ns"
|
||||
|
||||
|
@ -405,16 +418,18 @@ func TestGetNotificationParams(t *testing.T) {
|
|||
testEvent := &eventv1.Event{InvolvedObject: involvedObj}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
alertNamespace string
|
||||
alertSummary string
|
||||
alertEventMetadata map[string]string
|
||||
providerNamespace string
|
||||
providerSuspended bool
|
||||
secretNamespace string
|
||||
noCrossNSRefs bool
|
||||
eventMetadata map[string]string
|
||||
wantErr bool
|
||||
name string
|
||||
alertNamespace string
|
||||
alertSummary string
|
||||
alertEventMetadata map[string]string
|
||||
providerNamespace string
|
||||
providerSuspended bool
|
||||
providerServiceAccount string
|
||||
secretNamespace string
|
||||
noCrossNSRefs bool
|
||||
enableObjLevelWI bool
|
||||
eventMetadata map[string]string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "event src and alert in diff NS",
|
||||
|
@ -463,6 +478,18 @@ func TestGetNotificationParams(t *testing.T) {
|
|||
"ccc": "ddd",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "object level workload identity feature gate disabled",
|
||||
providerServiceAccount: "foo",
|
||||
enableObjLevelWI: false,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "object level workload identity feature gate enabled",
|
||||
providerServiceAccount: "foo",
|
||||
enableObjLevelWI: true,
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
@ -489,6 +516,7 @@ func TestGetNotificationParams(t *testing.T) {
|
|||
provider.Namespace = tt.providerNamespace
|
||||
}
|
||||
provider.Spec.Suspend = tt.providerSuspended
|
||||
provider.Spec.ServiceAccountName = tt.providerServiceAccount
|
||||
if tt.secretNamespace != "" {
|
||||
secret.Namespace = tt.secretNamespace
|
||||
}
|
||||
|
@ -496,6 +524,10 @@ func TestGetNotificationParams(t *testing.T) {
|
|||
event.Metadata = tt.eventMetadata
|
||||
}
|
||||
|
||||
if tt.enableObjLevelWI {
|
||||
t.Setenv(auth.EnvVarEnableObjectLevelWorkloadIdentity, "true")
|
||||
}
|
||||
|
||||
// Create fake objects and event server.
|
||||
scheme := runtime.NewScheme()
|
||||
g.Expect(apiv1beta3.AddToScheme(scheme)).ToNot(HaveOccurred())
|
||||
|
@ -528,21 +560,30 @@ func TestGetNotificationParams(t *testing.T) {
|
|||
func TestCreateNotifier(t *testing.T) {
|
||||
secretName := "foo-secret"
|
||||
certSecretName := "cert-secret"
|
||||
proxySecretName := "proxy-secret"
|
||||
|
||||
// Generate test certificates for mTLS testing
|
||||
caCert, clientCert, clientKey := generateTestCertificates(t)
|
||||
|
||||
// Helper to create expected TLS configs
|
||||
caPool := x509.NewCertPool()
|
||||
caPool.AppendCertsFromPEM(caCert)
|
||||
|
||||
clientCertPair, err := tls.X509KeyPair(clientCert, clientKey)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create client cert pair: %v", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
providerSpec *apiv1beta3.ProviderSpec
|
||||
secretType corev1.SecretType
|
||||
secretData map[string][]byte
|
||||
certSecretData map[string][]byte
|
||||
wantErr bool
|
||||
name string
|
||||
providerSpec *apiv1beta3.ProviderSpec
|
||||
secretType corev1.SecretType
|
||||
secretData map[string][]byte
|
||||
certSecretData map[string][]byte
|
||||
proxySecretData map[string][]byte
|
||||
wantErr bool
|
||||
wantTLSConfig *tls.Config
|
||||
}{
|
||||
{
|
||||
name: "no address, no secret ref",
|
||||
providerSpec: &apiv1beta3.ProviderSpec{
|
||||
Type: "slack",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "valid address, no secret ref",
|
||||
providerSpec: &apiv1beta3.ProviderSpec{
|
||||
|
@ -558,6 +599,7 @@ func TestCreateNotifier(t *testing.T) {
|
|||
},
|
||||
wantErr: true,
|
||||
},
|
||||
// TODO: Remove deprecated secret proxy key tests when Provider v1 is released.
|
||||
{
|
||||
name: "reference to secret with valid address, proxy, headers",
|
||||
providerSpec: &apiv1beta3.ProviderSpec{
|
||||
|
@ -605,6 +647,7 @@ func TestCreateNotifier(t *testing.T) {
|
|||
"address": []byte("https://example.com"),
|
||||
},
|
||||
},
|
||||
// TODO: Remove deprecated spec.proxy field tests when Provider v1 is released.
|
||||
{
|
||||
name: "invalid spec proxy overridden by valid secret ref proxy",
|
||||
providerSpec: &apiv1beta3.ProviderSpec{
|
||||
|
@ -659,19 +702,9 @@ func TestCreateNotifier(t *testing.T) {
|
|||
CertSecretRef: &meta.LocalObjectReference{Name: certSecretName},
|
||||
},
|
||||
certSecretData: map[string][]byte{
|
||||
// Based on https://pkg.go.dev/crypto/tls#X509KeyPair example.
|
||||
"ca.crt": []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw
|
||||
DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow
|
||||
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d
|
||||
7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B
|
||||
5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr
|
||||
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1
|
||||
NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l
|
||||
Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
|
||||
6MF9+Yw1Yy0t
|
||||
-----END CERTIFICATE-----`),
|
||||
"ca.crt": caCert,
|
||||
},
|
||||
wantTLSConfig: &tls.Config{RootCAs: caPool},
|
||||
},
|
||||
{
|
||||
name: "cert secret reference in caFile with valid CA",
|
||||
|
@ -681,19 +714,9 @@ Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
|
|||
CertSecretRef: &meta.LocalObjectReference{Name: certSecretName},
|
||||
},
|
||||
certSecretData: map[string][]byte{
|
||||
// Based on https://pkg.go.dev/crypto/tls#X509KeyPair example.
|
||||
"caFile": []byte(`-----BEGIN CERTIFICATE-----
|
||||
MIIBhTCCASugAwIBAgIQIRi6zePL6mKjOipn+dNuaTAKBggqhkjOPQQDAjASMRAw
|
||||
DgYDVQQKEwdBY21lIENvMB4XDTE3MTAyMDE5NDMwNloXDTE4MTAyMDE5NDMwNlow
|
||||
EjEQMA4GA1UEChMHQWNtZSBDbzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABD0d
|
||||
7VNhbWvZLWPuj/RtHFjvtJBEwOkhbN/BnnE8rnZR8+sbwnc/KhCk3FhnpHZnQz7B
|
||||
5aETbbIgmuvewdjvSBSjYzBhMA4GA1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggr
|
||||
BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MCkGA1UdEQQiMCCCDmxvY2FsaG9zdDo1
|
||||
NDUzgg4xMjcuMC4wLjE6NTQ1MzAKBggqhkjOPQQDAgNIADBFAiEA2zpJEPQyz6/l
|
||||
Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
|
||||
6MF9+Yw1Yy0t
|
||||
-----END CERTIFICATE-----`),
|
||||
"caFile": caCert,
|
||||
},
|
||||
wantTLSConfig: &tls.Config{RootCAs: caPool},
|
||||
},
|
||||
{
|
||||
name: "cert secret reference in both ca.crt and caFile",
|
||||
|
@ -730,6 +753,47 @@ Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
|
|||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "mTLS with standard keys",
|
||||
providerSpec: &apiv1beta3.ProviderSpec{
|
||||
Type: "generic",
|
||||
Address: "https://example.com",
|
||||
CertSecretRef: &meta.LocalObjectReference{Name: certSecretName},
|
||||
},
|
||||
certSecretData: map[string][]byte{
|
||||
"ca.crt": caCert,
|
||||
"tls.crt": clientCert,
|
||||
"tls.key": clientKey,
|
||||
},
|
||||
wantTLSConfig: &tls.Config{RootCAs: caPool, Certificates: []tls.Certificate{clientCertPair}},
|
||||
},
|
||||
{
|
||||
name: "mTLS with legacy keys",
|
||||
providerSpec: &apiv1beta3.ProviderSpec{
|
||||
Type: "generic",
|
||||
Address: "https://example.com",
|
||||
CertSecretRef: &meta.LocalObjectReference{Name: certSecretName},
|
||||
},
|
||||
certSecretData: map[string][]byte{
|
||||
"caFile": caCert,
|
||||
"certFile": clientCert,
|
||||
"keyFile": clientKey,
|
||||
},
|
||||
wantTLSConfig: &tls.Config{RootCAs: caPool, Certificates: []tls.Certificate{clientCertPair}},
|
||||
},
|
||||
{
|
||||
name: "client cert without CA",
|
||||
providerSpec: &apiv1beta3.ProviderSpec{
|
||||
Type: "generic",
|
||||
Address: "https://example.com",
|
||||
CertSecretRef: &meta.LocalObjectReference{Name: certSecretName},
|
||||
},
|
||||
certSecretData: map[string][]byte{
|
||||
"tls.crt": clientCert,
|
||||
"tls.key": clientKey,
|
||||
},
|
||||
wantTLSConfig: &tls.Config{Certificates: []tls.Certificate{clientCertPair}},
|
||||
},
|
||||
{
|
||||
name: "unsupported provider",
|
||||
providerSpec: &apiv1beta3.ProviderSpec{
|
||||
|
@ -760,6 +824,73 @@ Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
|
|||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "proxy from ProxySecretRef",
|
||||
providerSpec: &apiv1beta3.ProviderSpec{
|
||||
Type: "generic",
|
||||
Address: "https://example.com",
|
||||
ProxySecretRef: &meta.LocalObjectReference{Name: proxySecretName},
|
||||
},
|
||||
proxySecretData: map[string][]byte{
|
||||
"address": []byte("http://proxy.example.com:8080"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "proxy from ProxySecretRef with authentication",
|
||||
providerSpec: &apiv1beta3.ProviderSpec{
|
||||
Type: "generic",
|
||||
Address: "https://example.com",
|
||||
ProxySecretRef: &meta.LocalObjectReference{Name: proxySecretName},
|
||||
},
|
||||
proxySecretData: map[string][]byte{
|
||||
"address": []byte("http://proxy.example.com:8080"),
|
||||
"username": []byte("proxyuser"),
|
||||
"password": []byte("proxypass"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ProxySecretRef reference to non-existing secret",
|
||||
providerSpec: &apiv1beta3.ProviderSpec{
|
||||
Type: "generic",
|
||||
Address: "https://example.com",
|
||||
ProxySecretRef: &meta.LocalObjectReference{Name: "non-existing"},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "ProxySecretRef missing address field",
|
||||
providerSpec: &apiv1beta3.ProviderSpec{
|
||||
Type: "generic",
|
||||
Address: "https://example.com",
|
||||
ProxySecretRef: &meta.LocalObjectReference{Name: proxySecretName},
|
||||
},
|
||||
proxySecretData: map[string][]byte{
|
||||
"username": []byte("proxyuser"),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
// TODO: Remove deprecated spec.proxy field tests when Provider v1 is released.
|
||||
{
|
||||
name: "deprecated spec.proxy field",
|
||||
providerSpec: &apiv1beta3.ProviderSpec{
|
||||
Type: "generic",
|
||||
Address: "https://example.com",
|
||||
Proxy: "http://proxy.example.com:8080",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "provider type that does not require address field",
|
||||
providerSpec: &apiv1beta3.ProviderSpec{
|
||||
// Telegram generates URLs internally, so address field is not required
|
||||
Type: "telegram",
|
||||
Channel: "test-channel",
|
||||
SecretRef: &meta.LocalObjectReference{Name: secretName},
|
||||
},
|
||||
secretData: map[string][]byte{
|
||||
"token": []byte("test-token"),
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
@ -786,10 +917,243 @@ Wf86aX6PepsntZv2GYlA5UpabfT2EZICICpJ5h/iI+i341gBmLiAFQOyTDT+/wQc
|
|||
}
|
||||
builder.WithObjects(secret)
|
||||
}
|
||||
if tt.proxySecretData != nil {
|
||||
secret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: proxySecretName},
|
||||
Data: tt.proxySecretData,
|
||||
}
|
||||
builder.WithObjects(secret)
|
||||
}
|
||||
provider := apiv1beta3.Provider{Spec: *tt.providerSpec}
|
||||
|
||||
_, _, err := createNotifier(context.TODO(), builder.Build(), provider)
|
||||
notifier, _, err := createNotifier(context.TODO(), builder.Build(), &provider, "", nil)
|
||||
g.Expect(err != nil).To(Equal(tt.wantErr))
|
||||
|
||||
if !tt.wantErr && tt.wantTLSConfig != nil {
|
||||
g.Expect(notifier).ToNot(BeNil(), "Expected notifier to be created but got nil")
|
||||
|
||||
// Get TLS configuration from notifier
|
||||
tlsConfig := getNotifierTLSConfig(notifier)
|
||||
if tlsConfig == nil {
|
||||
// Notifier doesn't support TLS via postMessage pattern, skip the check
|
||||
return
|
||||
}
|
||||
|
||||
g.Expect(tlsConfig).ToNot(BeNil(), "Expected TLS configuration but got nil")
|
||||
if tt.wantTLSConfig.RootCAs != nil {
|
||||
g.Expect(tlsConfig.RootCAs).ToNot(BeNil())
|
||||
} else {
|
||||
g.Expect(tlsConfig.RootCAs).To(BeNil())
|
||||
}
|
||||
|
||||
g.Expect(tlsConfig.Certificates).To(HaveLen(len(tt.wantTLSConfig.Certificates)))
|
||||
if len(tt.wantTLSConfig.Certificates) > 0 {
|
||||
g.Expect(tlsConfig.Certificates[0]).To(Equal(tt.wantTLSConfig.Certificates[0]))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCreateCommitStatus(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
provider apiv1beta3.Provider
|
||||
notification eventv1.Event
|
||||
alert *apiv1beta3.Alert
|
||||
wantCommitStatus string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "non-git provider: slack",
|
||||
provider: apiv1beta3.Provider{
|
||||
Spec: apiv1beta3.ProviderSpec{
|
||||
Type: "slack",
|
||||
},
|
||||
},
|
||||
wantCommitStatus: "",
|
||||
},
|
||||
{
|
||||
name: "non-git provider: msteams",
|
||||
provider: apiv1beta3.Provider{
|
||||
Spec: apiv1beta3.ProviderSpec{
|
||||
Type: "msteams",
|
||||
},
|
||||
},
|
||||
wantCommitStatus: "",
|
||||
},
|
||||
{
|
||||
name: "git provider without commit status expression",
|
||||
provider: apiv1beta3.Provider{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
},
|
||||
Spec: apiv1beta3.ProviderSpec{
|
||||
Type: "github",
|
||||
},
|
||||
},
|
||||
notification: eventv1.Event{
|
||||
InvolvedObject: corev1.ObjectReference{
|
||||
Kind: "Kustomization",
|
||||
Name: "gitops-system",
|
||||
},
|
||||
Reason: "ApplySucceeded",
|
||||
},
|
||||
wantCommitStatus: "kustomization/gitops-system/0c9c2e41",
|
||||
},
|
||||
{
|
||||
name: "gitlab provider without commit status expression",
|
||||
provider: apiv1beta3.Provider{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
},
|
||||
Spec: apiv1beta3.ProviderSpec{
|
||||
Type: "gitlab",
|
||||
},
|
||||
},
|
||||
notification: eventv1.Event{
|
||||
InvolvedObject: corev1.ObjectReference{
|
||||
Kind: "HelmRelease",
|
||||
Name: "gitops-system",
|
||||
},
|
||||
Reason: "ApplySucceeded",
|
||||
},
|
||||
wantCommitStatus: "helmrelease/gitops-system/0c9c2e41",
|
||||
},
|
||||
{
|
||||
name: "git provider with commit status expression",
|
||||
provider: apiv1beta3.Provider{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
},
|
||||
Spec: apiv1beta3.ProviderSpec{
|
||||
Type: "github",
|
||||
CommitStatusExpr: "event.involvedObject.kind + '/' + event.involvedObject.name + '/' + event.metadata.environment + '/' + provider.metadata.uid",
|
||||
},
|
||||
},
|
||||
notification: eventv1.Event{
|
||||
InvolvedObject: corev1.ObjectReference{
|
||||
Kind: "Kustomization",
|
||||
Name: "gitops-system",
|
||||
},
|
||||
Reason: "ApplySucceeded",
|
||||
Metadata: map[string]string{
|
||||
"environment": "production",
|
||||
},
|
||||
},
|
||||
alert: &apiv1beta3.Alert{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-alert",
|
||||
},
|
||||
},
|
||||
wantCommitStatus: "Kustomization/gitops-system/production/0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
},
|
||||
{
|
||||
name: "git provider with commit status expression using first value of provider UID",
|
||||
provider: apiv1beta3.Provider{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
},
|
||||
Spec: apiv1beta3.ProviderSpec{
|
||||
Type: "github",
|
||||
CommitStatusExpr: "event.involvedObject.kind + '/' + event.involvedObject.name + '/' + event.metadata.environment + '/' + provider.metadata.uid.split('-').first().value()",
|
||||
},
|
||||
},
|
||||
notification: eventv1.Event{
|
||||
InvolvedObject: corev1.ObjectReference{
|
||||
Kind: "Kustomization",
|
||||
Name: "gitops-system",
|
||||
},
|
||||
Metadata: map[string]string{
|
||||
"environment": "production",
|
||||
},
|
||||
},
|
||||
alert: &apiv1beta3.Alert{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-alert",
|
||||
},
|
||||
},
|
||||
wantCommitStatus: "Kustomization/gitops-system/production/0c9c2e41",
|
||||
},
|
||||
{
|
||||
name: "git provider with commit status expression using event, alert, and provider",
|
||||
provider: apiv1beta3.Provider{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
},
|
||||
Spec: apiv1beta3.ProviderSpec{
|
||||
Type: "github",
|
||||
CommitStatusExpr: "event.involvedObject.kind + '/' + event.involvedObject.name + '/' + event.metadata.environment + '/' + provider.metadata.uid + '/' + alert.metadata.name",
|
||||
},
|
||||
},
|
||||
notification: eventv1.Event{
|
||||
InvolvedObject: corev1.ObjectReference{
|
||||
Kind: "Kustomization",
|
||||
Name: "gitops-system",
|
||||
},
|
||||
Metadata: map[string]string{
|
||||
"environment": "production",
|
||||
},
|
||||
},
|
||||
alert: &apiv1beta3.Alert{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-alert",
|
||||
},
|
||||
},
|
||||
wantCommitStatus: "Kustomization/gitops-system/production/0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a/test-alert",
|
||||
},
|
||||
{
|
||||
name: "git provider with invalid commit status expression referencing non-existent event metadata",
|
||||
notification: eventv1.Event{
|
||||
InvolvedObject: corev1.ObjectReference{
|
||||
Kind: "Kustomization",
|
||||
Name: "gitops-system",
|
||||
},
|
||||
Metadata: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
provider: apiv1beta3.Provider{
|
||||
Spec: apiv1beta3.ProviderSpec{
|
||||
Type: "github",
|
||||
CommitStatusExpr: "event.involvedObject.kind + '/' + event.involvedObject.name + '/' + event.metadata.notfound + '/' + provider.metadata.uid",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "git provider with invalid commit status expression",
|
||||
notification: eventv1.Event{
|
||||
InvolvedObject: corev1.ObjectReference{
|
||||
Kind: "Kustomization",
|
||||
Name: "gitops-system",
|
||||
},
|
||||
Metadata: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
provider: apiv1beta3.Provider{
|
||||
Spec: apiv1beta3.ProviderSpec{
|
||||
Type: "github",
|
||||
CommitStatusExpr: "event.involvedObject.kind == 'Kustomization'",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
// Create fake objects and event server.
|
||||
scheme := runtime.NewScheme()
|
||||
g.Expect(apiv1beta3.AddToScheme(scheme)).ToNot(HaveOccurred())
|
||||
g.Expect(corev1.AddToScheme(scheme)).ToNot(HaveOccurred())
|
||||
|
||||
commitStatus, err := createCommitStatus(context.TODO(), &tt.provider, &tt.notification, tt.alert)
|
||||
g.Expect(err != nil).To(Equal(tt.wantErr))
|
||||
g.Expect(commitStatus).To(Equal(tt.wantCommitStatus))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1152,3 +1516,110 @@ func Test_excludeInternalMetadata(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// generateTestCertificates generates test certificates for mTLS testing.
|
||||
// TODO: Move to pkg/runtime/secrets test helpers after mTLS implementation is complete
|
||||
func generateTestCertificates(t *testing.T) (caCert, clientCert, clientKey []byte) {
|
||||
t.Helper()
|
||||
|
||||
caPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to generate CA private key: %v", err)
|
||||
}
|
||||
|
||||
caTemplate := x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Test CA"},
|
||||
Country: []string{"US"},
|
||||
Province: []string{""},
|
||||
Locality: []string{"San Francisco"},
|
||||
StreetAddress: []string{""},
|
||||
PostalCode: []string{""},
|
||||
},
|
||||
NotBefore: fixedNow,
|
||||
NotAfter: fixedNow.Add(365 * 24 * time.Hour),
|
||||
IsCA: true,
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||
BasicConstraintsValid: true,
|
||||
}
|
||||
|
||||
caCertDER, err := x509.CreateCertificate(rand.Reader, &caTemplate, &caTemplate, &caPrivKey.PublicKey, caPrivKey)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create CA certificate: %v", err)
|
||||
}
|
||||
|
||||
clientPrivKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to generate client private key: %v", err)
|
||||
}
|
||||
|
||||
clientTemplate := x509.Certificate{
|
||||
SerialNumber: big.NewInt(2),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Test Client"},
|
||||
Country: []string{"US"},
|
||||
Province: []string{""},
|
||||
Locality: []string{"San Francisco"},
|
||||
StreetAddress: []string{""},
|
||||
PostalCode: []string{""},
|
||||
},
|
||||
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback},
|
||||
DNSNames: []string{"localhost"},
|
||||
NotBefore: fixedNow,
|
||||
NotAfter: fixedNow.Add(365 * 24 * time.Hour),
|
||||
SubjectKeyId: []byte{1, 2, 3, 4, 6},
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature,
|
||||
}
|
||||
|
||||
clientCertDER, err := x509.CreateCertificate(rand.Reader, &clientTemplate, &caTemplate, &clientPrivKey.PublicKey, caPrivKey)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create client certificate: %v", err)
|
||||
}
|
||||
|
||||
caCertPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: caCertDER,
|
||||
})
|
||||
|
||||
clientCertPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: clientCertDER,
|
||||
})
|
||||
|
||||
clientKeyPEM := pem.EncodeToMemory(&pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(clientPrivKey),
|
||||
})
|
||||
|
||||
return caCertPEM, clientCertPEM, clientKeyPEM
|
||||
}
|
||||
|
||||
// getNotifierTLSConfig extracts TLSConfig from postMessage-based notifiers for testing
|
||||
func getNotifierTLSConfig(n notifier.Interface) *tls.Config {
|
||||
switch v := n.(type) {
|
||||
case *notifier.Forwarder:
|
||||
return v.TLSConfig
|
||||
case *notifier.Slack:
|
||||
return v.TLSConfig
|
||||
case *notifier.Alertmanager:
|
||||
return v.TLSConfig
|
||||
case *notifier.Grafana:
|
||||
return v.TLSConfig
|
||||
case *notifier.Matrix:
|
||||
return v.TLSConfig
|
||||
case *notifier.Opsgenie:
|
||||
return v.TLSConfig
|
||||
case *notifier.PagerDuty:
|
||||
return v.TLSConfig
|
||||
case *notifier.Rocket:
|
||||
return v.TLSConfig
|
||||
case *notifier.MSTeams:
|
||||
return v.TLSConfig
|
||||
case *notifier.Webex:
|
||||
return v.TLSConfig
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
|
||||
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
|
||||
"github.com/fluxcd/pkg/cache"
|
||||
)
|
||||
|
||||
// +kubebuilder:rbac:groups="",resources=events,verbs=create;patch
|
||||
|
@ -53,11 +54,14 @@ type EventServer struct {
|
|||
kubeClient client.Client
|
||||
noCrossNamespaceRefs bool
|
||||
exportHTTPPathMetrics bool
|
||||
tokenCache *cache.TokenCache
|
||||
kuberecorder.EventRecorder
|
||||
}
|
||||
|
||||
// NewEventServer returns an HTTP server that handles events
|
||||
func NewEventServer(port string, logger logr.Logger, kubeClient client.Client, eventRecorder kuberecorder.EventRecorder, noCrossNamespaceRefs bool, exportHTTPPathMetrics bool) *EventServer {
|
||||
func NewEventServer(port string, logger logr.Logger, kubeClient client.Client,
|
||||
eventRecorder kuberecorder.EventRecorder, noCrossNamespaceRefs bool,
|
||||
exportHTTPPathMetrics bool, tokenCache *cache.TokenCache) *EventServer {
|
||||
return &EventServer{
|
||||
port: port,
|
||||
logger: logger.WithName("event-server"),
|
||||
|
@ -65,6 +69,7 @@ func NewEventServer(port string, logger logr.Logger, kubeClient client.Client, e
|
|||
EventRecorder: eventRecorder,
|
||||
noCrossNamespaceRefs: noCrossNamespaceRefs,
|
||||
exportHTTPPathMetrics: exportHTTPPathMetrics,
|
||||
tokenCache: tokenCache,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ func TestEventServer(t *testing.T) {
|
|||
t.Fatalf("failed to create memory storage")
|
||||
}
|
||||
eventServer := NewEventServer("127.0.0.1:"+eventServerPort,
|
||||
log.Log, kclient, record.NewFakeRecorder(32), true, true)
|
||||
log.Log, kclient, record.NewFakeRecorder(32), true, true, nil)
|
||||
stopCh := make(chan struct{})
|
||||
go eventServer.ListenAndServe(stopCh, eventMdlw, store)
|
||||
defer close(stopCh)
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
Copyright 2025 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
|
||||
"github.com/fluxcd/pkg/runtime/cel"
|
||||
"github.com/google/cel-go/common/types"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
apiv1beta3 "github.com/fluxcd/notification-controller/api/v1beta3"
|
||||
)
|
||||
|
||||
// newCommitStatusExpression creates a new CEL expression for the commit status ID.
|
||||
func newCommitStatusExpression(s string) (*cel.Expression, error) {
|
||||
return cel.NewExpression(s,
|
||||
cel.WithCompile(),
|
||||
cel.WithOutputType(types.StringType),
|
||||
cel.WithStructVariables("event", "alert", "provider"))
|
||||
}
|
||||
|
||||
// generateDefaultCommitStatus returns a unique string per cluster based on the Provider UID,
|
||||
// involved object kind and name.
|
||||
func generateDefaultCommitStatus(providerUID string, event eventv1.Event) string {
|
||||
uidParts := strings.Split(providerUID, "-")
|
||||
id := fmt.Sprintf("%s/%s/%s", event.InvolvedObject.Kind, event.InvolvedObject.Name, uidParts[0])
|
||||
return strings.ToLower(id)
|
||||
}
|
||||
|
||||
// newCommitStatus evaluates the commit status expression.
|
||||
func newCommitStatus(ctx context.Context, expr string, notification *eventv1.Event, alert *apiv1beta3.Alert, provider *apiv1beta3.Provider) (string, error) {
|
||||
celExpr, err := newCommitStatusExpression(expr)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to compile expression: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
eventMap map[string]any
|
||||
providerMap map[string]any
|
||||
alertMap map[string]any
|
||||
)
|
||||
|
||||
eventMap, err = runtime.DefaultUnstructuredConverter.ToUnstructured(notification)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to convert event to map: %w", err)
|
||||
}
|
||||
|
||||
providerMap, err = runtime.DefaultUnstructuredConverter.ToUnstructured(provider)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to convert provider object to map: %w", err)
|
||||
}
|
||||
|
||||
alertMap, err = runtime.DefaultUnstructuredConverter.ToUnstructured(alert)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to convert alert object to map: %w", err)
|
||||
}
|
||||
|
||||
vars := map[string]any{
|
||||
"event": eventMap,
|
||||
"provider": providerMap,
|
||||
"alert": alertMap,
|
||||
}
|
||||
|
||||
result, err := celExpr.EvaluateString(ctx, vars)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// isGitProvider returns true if the provider type is a Git provider.
|
||||
func isGitProvider(providerType string) bool {
|
||||
gitProviderTypes := []string{
|
||||
apiv1beta3.GitHubProvider,
|
||||
apiv1beta3.GitLabProvider,
|
||||
apiv1beta3.GiteaProvider,
|
||||
apiv1beta3.BitbucketServerProvider,
|
||||
apiv1beta3.BitbucketProvider,
|
||||
apiv1beta3.AzureDevOpsProvider,
|
||||
}
|
||||
|
||||
return slices.Contains(gitProviderTypes, providerType)
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
Copyright 2025 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
apiv1beta3 "github.com/fluxcd/notification-controller/api/v1beta3"
|
||||
eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/require"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func Test_newCommitStatus(t *testing.T) {
|
||||
tests := []struct {
|
||||
expression string
|
||||
notification *eventv1.Event
|
||||
alert *apiv1beta3.Alert
|
||||
provider *apiv1beta3.Provider
|
||||
wantResult string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
expression: "event.involvedObject.kind + '/' + event.involvedObject.name + '/' + event.metadata.environment + '/' + provider.metadata.uid + '/' + alert.metadata.name",
|
||||
notification: &eventv1.Event{
|
||||
InvolvedObject: corev1.ObjectReference{
|
||||
Kind: "Kustomization",
|
||||
Name: "gitops-system",
|
||||
},
|
||||
Metadata: map[string]string{
|
||||
"environment": "production",
|
||||
},
|
||||
},
|
||||
alert: &apiv1beta3.Alert{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-alert",
|
||||
},
|
||||
},
|
||||
provider: &apiv1beta3.Provider{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
},
|
||||
},
|
||||
wantResult: "Kustomization/gitops-system/production/0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a/test-alert",
|
||||
},
|
||||
{
|
||||
expression: "(event.involvedObject.kind + '/' + event.involvedObject.name + '/' + event.metadata.environment + '/' + provider.metadata.uid + '/' + alert.metadata.name).lowerAscii()",
|
||||
notification: &eventv1.Event{
|
||||
InvolvedObject: corev1.ObjectReference{
|
||||
Kind: "Kustomization",
|
||||
Name: "gitops-system",
|
||||
},
|
||||
Metadata: map[string]string{
|
||||
"environment": "production",
|
||||
},
|
||||
},
|
||||
alert: &apiv1beta3.Alert{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-alert",
|
||||
},
|
||||
},
|
||||
provider: &apiv1beta3.Provider{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
},
|
||||
},
|
||||
wantResult: "kustomization/gitops-system/production/0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a/test-alert",
|
||||
},
|
||||
{
|
||||
expression: "event.involvedObject.kind + '/' + event.involvedObject.name + '/' + notification.metadata.name",
|
||||
notification: &eventv1.Event{
|
||||
InvolvedObject: corev1.ObjectReference{
|
||||
Kind: "Kustomization",
|
||||
Name: "gitops-system",
|
||||
},
|
||||
Metadata: map[string]string{
|
||||
"environment": "production",
|
||||
},
|
||||
},
|
||||
alert: &apiv1beta3.Alert{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-alert",
|
||||
},
|
||||
},
|
||||
provider: &apiv1beta3.Provider{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
expression: "kustomization.metadata.name + '/' + provider.metadata.uid",
|
||||
notification: &eventv1.Event{
|
||||
InvolvedObject: corev1.ObjectReference{
|
||||
Kind: "Kustomization",
|
||||
Name: "gitops-system",
|
||||
},
|
||||
},
|
||||
provider: &apiv1beta3.Provider{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
UID: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
},
|
||||
},
|
||||
alert: &apiv1beta3.Alert{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "test-alert",
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.expression, func(t *testing.T) {
|
||||
g := NewWithT(t)
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
g.Expect(apiv1beta3.AddToScheme(scheme)).ToNot(HaveOccurred())
|
||||
g.Expect(corev1.AddToScheme(scheme)).ToNot(HaveOccurred())
|
||||
|
||||
result, err := newCommitStatus(context.Background(), tt.expression, tt.notification, tt.alert, tt.provider)
|
||||
g.Expect(err != nil).To(Equal(tt.wantErr))
|
||||
g.Expect(result).To(Equal(tt.wantResult))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_generateDefaultCommitStatus(t *testing.T) {
|
||||
statusIDTests := []struct {
|
||||
name string
|
||||
providerUID string
|
||||
event eventv1.Event
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "simple event case",
|
||||
providerUID: "0c9c2e41-d2f9-4f9b-9c41-bebc1984d67a",
|
||||
event: eventv1.Event{
|
||||
InvolvedObject: corev1.ObjectReference{
|
||||
Kind: "Kustomization",
|
||||
Name: "gitops-system",
|
||||
},
|
||||
Reason: "ApplySucceeded",
|
||||
},
|
||||
want: "kustomization/gitops-system/0c9c2e41",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range statusIDTests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
id := generateDefaultCommitStatus(tt.providerUID, tt.event)
|
||||
|
||||
require.Equal(t, tt.want, id)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -445,7 +445,7 @@ func (s *ReceiverServer) validate(ctx context.Context, receiver apiv1.Receiver,
|
|||
case apiv1.NexusReceiver:
|
||||
signature := r.Header.Get("X-Nexus-Webhook-Signature")
|
||||
if len(signature) == 0 {
|
||||
return fmt.Errorf("Nexus signature is missing from header")
|
||||
return fmt.Errorf("the Nexus signature is missing from header")
|
||||
}
|
||||
|
||||
b, err := io.ReadAll(r.Body)
|
||||
|
@ -583,7 +583,7 @@ func authenticateGCRRequest(c *http.Client, bearer string, tokenIndex int) (err
|
|||
}
|
||||
|
||||
if len(bearer) < tokenIndex {
|
||||
return fmt.Errorf("Authorization header is missing or malformed: %v", bearer)
|
||||
return fmt.Errorf("the Authorization header is missing or malformed: %v", bearer)
|
||||
}
|
||||
|
||||
token := bearer[tokenIndex:]
|
||||
|
|
49
main.go
49
main.go
|
@ -29,14 +29,16 @@ import (
|
|||
"k8s.io/apimachinery/pkg/runtime"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
|
||||
"k8s.io/utils/pointer"
|
||||
"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"
|
||||
"sigs.k8s.io/controller-runtime/pkg/config"
|
||||
crtlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||
ctrlmetrics "sigs.k8s.io/controller-runtime/pkg/metrics"
|
||||
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
|
||||
|
||||
"github.com/fluxcd/pkg/auth"
|
||||
pkgcache "github.com/fluxcd/pkg/cache"
|
||||
"github.com/fluxcd/pkg/runtime/acl"
|
||||
"github.com/fluxcd/pkg/runtime/client"
|
||||
helper "github.com/fluxcd/pkg/runtime/controller"
|
||||
|
@ -73,6 +75,10 @@ func init() {
|
|||
}
|
||||
|
||||
func main() {
|
||||
const (
|
||||
tokenCacheDefaultMaxSize = 100
|
||||
)
|
||||
|
||||
var (
|
||||
eventsAddr string
|
||||
receiverAddr string
|
||||
|
@ -88,6 +94,7 @@ func main() {
|
|||
rateLimiterOptions helper.RateLimiterOptions
|
||||
featureGates feathelper.FeatureGates
|
||||
exportHTTPPathMetrics bool
|
||||
tokenCacheOptions pkgcache.TokenFlags
|
||||
)
|
||||
|
||||
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
|
||||
|
@ -106,6 +113,7 @@ func main() {
|
|||
aclOptions.BindFlags(flag.CommandLine)
|
||||
rateLimiterOptions.BindFlags(flag.CommandLine)
|
||||
featureGates.BindFlags(flag.CommandLine)
|
||||
tokenCacheOptions.BindFlags(flag.CommandLine, tokenCacheDefaultMaxSize)
|
||||
|
||||
flag.Parse()
|
||||
|
||||
|
@ -116,6 +124,14 @@ func main() {
|
|||
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()
|
||||
}
|
||||
|
||||
watchNamespace := ""
|
||||
if !watchAllNamespaces {
|
||||
watchNamespace = os.Getenv("RUNTIME_NAMESPACE")
|
||||
|
@ -143,7 +159,7 @@ func main() {
|
|||
LeaderElectionID: fmt.Sprintf("%s-leader-election", controllerName),
|
||||
Logger: ctrl.Log,
|
||||
Controller: config.Controller{
|
||||
RecoverPanic: pointer.Bool(true),
|
||||
RecoverPanic: ptr.To(true),
|
||||
MaxConcurrentReconciles: concurrent,
|
||||
},
|
||||
Client: ctrlclient.Options{
|
||||
|
@ -160,7 +176,7 @@ func main() {
|
|||
if watchNamespace != "" {
|
||||
mgrConfig.Cache = ctrlcache.Options{
|
||||
DefaultNamespaces: map[string]ctrlcache.Config{
|
||||
watchNamespace: ctrlcache.Config{},
|
||||
watchNamespace: {},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -175,10 +191,23 @@ func main() {
|
|||
|
||||
metricsH := helper.NewMetrics(mgr, metrics.MustMakeRecorder(), apiv1.NotificationFinalizer)
|
||||
|
||||
var tokenCache *pkgcache.TokenCache
|
||||
if tokenCacheOptions.MaxSize > 0 {
|
||||
var err error
|
||||
tokenCache, err = pkgcache.NewTokenCache(tokenCacheOptions.MaxSize,
|
||||
pkgcache.WithMaxDuration(tokenCacheOptions.MaxDuration),
|
||||
pkgcache.WithMetricsRegisterer(ctrlmetrics.Registry),
|
||||
pkgcache.WithMetricsPrefix("gotk_token_"))
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to create token cache")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if err = (&controller.ProviderReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
ControllerName: controllerName,
|
||||
EventRecorder: mgr.GetEventRecorderFor(controllerName),
|
||||
Client: mgr.GetClient(),
|
||||
EventRecorder: mgr.GetEventRecorderFor(controllerName),
|
||||
TokenCache: tokenCache,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "Provider")
|
||||
os.Exit(1)
|
||||
|
@ -219,10 +248,10 @@ func main() {
|
|||
eventMdlw := middleware.New(middleware.Config{
|
||||
Recorder: prommetrics.NewRecorder(prommetrics.Config{
|
||||
Prefix: "gotk_event",
|
||||
Registry: crtlmetrics.Registry,
|
||||
Registry: ctrlmetrics.Registry,
|
||||
}),
|
||||
})
|
||||
eventServer := server.NewEventServer(eventsAddr, ctrl.Log, mgr.GetClient(), mgr.GetEventRecorderFor(controllerName), aclOptions.NoCrossNamespaceRefs, exportHTTPPathMetrics)
|
||||
eventServer := server.NewEventServer(eventsAddr, ctrl.Log, mgr.GetClient(), mgr.GetEventRecorderFor(controllerName), aclOptions.NoCrossNamespaceRefs, exportHTTPPathMetrics, tokenCache)
|
||||
go eventServer.ListenAndServe(ctx.Done(), eventMdlw, store)
|
||||
|
||||
setupLog.Info("starting webhook receiver server", "addr", receiverAddr)
|
||||
|
@ -230,7 +259,7 @@ func main() {
|
|||
receiverMdlw := middleware.New(middleware.Config{
|
||||
Recorder: prommetrics.NewRecorder(prommetrics.Config{
|
||||
Prefix: "gotk_receiver",
|
||||
Registry: crtlmetrics.Registry,
|
||||
Registry: ctrlmetrics.Registry,
|
||||
}),
|
||||
})
|
||||
go receiverServer.ListenAndServe(ctx.Done(), receiverMdlw)
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
FROM gcr.io/oss-fuzz-base/base-builder-go
|
||||
|
||||
RUN wget https://go.dev/dl/go1.23.0.linux-amd64.tar.gz \
|
||||
RUN wget https://go.dev/dl/go1.24.0.linux-amd64.tar.gz \
|
||||
&& mkdir temp-go \
|
||||
&& rm -rf /root/.go/* \
|
||||
&& tar -C temp-go/ -xzf go1.23.0.linux-amd64.tar.gz \
|
||||
&& tar -C temp-go/ -xzf go1.24.0.linux-amd64.tar.gz \
|
||||
&& mv temp-go/go/* /root/.go/
|
||||
|
||||
ENV SRC=$GOPATH/src/github.com/fluxcd/notification-controller
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
Copyright 2025 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package testlistener
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// New creates a TCP listener on a random port and returns
|
||||
// the listener, the address and the port of this listener.
|
||||
// It also registers a cleanup function to close the listener
|
||||
// when the test ends.
|
||||
func New(t *testing.T) (net.Listener, string, int) {
|
||||
t.Helper()
|
||||
|
||||
lis, err := net.Listen("tcp", "localhost:0")
|
||||
assert.NoError(t, err)
|
||||
t.Cleanup(func() { lis.Close() })
|
||||
|
||||
addr := lis.Addr().String()
|
||||
addrParts := strings.Split(addr, ":")
|
||||
portStr := addrParts[len(addrParts)-1]
|
||||
port, err := strconv.Atoi(portStr)
|
||||
assert.NoError(t, err)
|
||||
|
||||
return lis, addr, port
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
Copyright 2025 The Flux authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package testproxy
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/elazarl/goproxy"
|
||||
|
||||
testlistener "github.com/fluxcd/notification-controller/tests/listener"
|
||||
)
|
||||
|
||||
// New creates a new goproxy server on a random port and returns
|
||||
// the address and the port of this server. It also registers a
|
||||
// cleanup functions to close the server and the listener when
|
||||
// the test ends.
|
||||
func New(t *testing.T) (string, int) {
|
||||
t.Helper()
|
||||
|
||||
lis, addr, port := testlistener.New(t)
|
||||
|
||||
handler := goproxy.NewProxyHttpServer()
|
||||
handler.Verbose = true
|
||||
|
||||
server := &http.Server{
|
||||
Addr: addr,
|
||||
Handler: handler,
|
||||
}
|
||||
go server.Serve(lis)
|
||||
t.Cleanup(func() { server.Close() })
|
||||
|
||||
return addr, port
|
||||
}
|
Loading…
Reference in New Issue