mirror of
https://github.com/fluxcd/flagger.git
synced 2026-02-14 18:10:00 +00:00
Compare commits
181 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4cc9bf616 | ||
|
|
ca3fd315cd | ||
|
|
d9d910b0cf | ||
|
|
9090802dcc | ||
|
|
70c4c528ed | ||
|
|
3ce87af077 | ||
|
|
1709b076e0 | ||
|
|
f3b240ca82 | ||
|
|
4821a687c1 | ||
|
|
931dd7fa6b | ||
|
|
8018353d54 | ||
|
|
ffddfd9c24 | ||
|
|
3a27fd147f | ||
|
|
1a727d294c | ||
|
|
b170bf479c | ||
|
|
ae5e39bb3d | ||
|
|
7fcba13050 | ||
|
|
8fedc5cdd7 | ||
|
|
fce1781ebf | ||
|
|
461abc9e10 | ||
|
|
6b89d56074 | ||
|
|
bb7ad65462 | ||
|
|
6fcbe192a7 | ||
|
|
253123bdff | ||
|
|
ae72e15049 | ||
|
|
c30e6552d7 | ||
|
|
60cb38a773 | ||
|
|
6031abc3a9 | ||
|
|
e4c355e772 | ||
|
|
f9fe9a1635 | ||
|
|
34830b2448 | ||
|
|
9ed5fcdaa3 | ||
|
|
3dc0658a80 | ||
|
|
751f52ec25 | ||
|
|
e1839fd9c3 | ||
|
|
b445c8eeaf | ||
|
|
3d99d2fddd | ||
|
|
b77230227f | ||
|
|
ea219506a5 | ||
|
|
fab45404e6 | ||
|
|
0dd9fe9301 | ||
|
|
278e7d31bc | ||
|
|
c7dad5b532 | ||
|
|
b672363a37 | ||
|
|
939c32b390 | ||
|
|
e08cc1d798 | ||
|
|
28291d42d8 | ||
|
|
5c481cd6f9 | ||
|
|
eeeac3543a | ||
|
|
0743ef72b3 | ||
|
|
ffbc25efda | ||
|
|
16f54923b2 | ||
|
|
f9f10e842e | ||
|
|
56200f6d0f | ||
|
|
2cf112841c | ||
|
|
a67c36ed99 | ||
|
|
27daa2ca46 | ||
|
|
ed38a79545 | ||
|
|
6f165a10de | ||
|
|
89c1ddee79 | ||
|
|
1b8e7653d3 | ||
|
|
98f8514258 | ||
|
|
d9c8a09d3e | ||
|
|
2ac22f831f | ||
|
|
e0de40dcb0 | ||
|
|
8f9bb5b1bc | ||
|
|
f21bc1de3e | ||
|
|
1fc7ac5847 | ||
|
|
1dc270c2e6 | ||
|
|
50d1331ba6 | ||
|
|
bc78156535 | ||
|
|
0df8af8d04 | ||
|
|
633f639383 | ||
|
|
d03cc73386 | ||
|
|
eaf5bb992c | ||
|
|
22618ccb11 | ||
|
|
f5af225ffc | ||
|
|
40a34199fe | ||
|
|
d7357a7377 | ||
|
|
12ee6cbc86 | ||
|
|
f1c8807c0d | ||
|
|
8276bfa5a5 | ||
|
|
2c4b7a69a2 | ||
|
|
660ed7486b | ||
|
|
21acd7e3d6 | ||
|
|
40e2802c3d | ||
|
|
d99d37b219 | ||
|
|
45618b90db | ||
|
|
ff4051f728 | ||
|
|
2ea13a477b | ||
|
|
03d4acc77f | ||
|
|
16a607549e | ||
|
|
b57afd3b0f | ||
|
|
9000136233 | ||
|
|
14543cc8bf | ||
|
|
642ef6bb7d | ||
|
|
3ebbfb0a54 | ||
|
|
a52f497370 | ||
|
|
64b50813ff | ||
|
|
9244d6de65 | ||
|
|
3b6b550d64 | ||
|
|
282f2b36f0 | ||
|
|
0a76f808b8 | ||
|
|
a85887de3c | ||
|
|
febc327673 | ||
|
|
6d5aabff05 | ||
|
|
51d0bb2c92 | ||
|
|
dc947fb164 | ||
|
|
0138e2e6c4 | ||
|
|
d4bd0f2ef8 | ||
|
|
30f4b25925 | ||
|
|
25fd9be1db | ||
|
|
4d497b2a9d | ||
|
|
0ef356706a | ||
|
|
ebf43ef104 | ||
|
|
7754cdb89a | ||
|
|
c6b5b39187 | ||
|
|
a6a7a20737 | ||
|
|
c04ff05aa4 | ||
|
|
b4bc93d0a8 | ||
|
|
6ee00e14f9 | ||
|
|
a7d90c227f | ||
|
|
4c0a26b675 | ||
|
|
d4f766285d | ||
|
|
66fcea7581 | ||
|
|
9bfc531da0 | ||
|
|
398fc90cc0 | ||
|
|
682230e8c0 | ||
|
|
92daf5174c | ||
|
|
2ba00a33a7 | ||
|
|
8f838388e8 | ||
|
|
7cd14761d5 | ||
|
|
e99add460f | ||
|
|
b88e080a66 | ||
|
|
9941843385 | ||
|
|
a159421290 | ||
|
|
43cb4bc8e9 | ||
|
|
b719427337 | ||
|
|
b6ac5e19aa | ||
|
|
6a090bca51 | ||
|
|
e07a2618c2 | ||
|
|
9fcb6e9c93 | ||
|
|
a88e06db17 | ||
|
|
401d0490da | ||
|
|
3d1aedeb44 | ||
|
|
4015103815 | ||
|
|
74b98dab00 | ||
|
|
01dfa06891 | ||
|
|
90054b3b27 | ||
|
|
cff2032ac0 | ||
|
|
2d5e289142 | ||
|
|
f38183bfd1 | ||
|
|
c09a61a198 | ||
|
|
417f035afb | ||
|
|
28f2ab7bdb | ||
|
|
d6433a16b5 | ||
|
|
9b39cf16f1 | ||
|
|
d2cfcbde1a | ||
|
|
133fdecf56 | ||
|
|
3490d60e89 | ||
|
|
97d1ef0f18 | ||
|
|
ce976e28f0 | ||
|
|
adc60596f5 | ||
|
|
cf04e28774 | ||
|
|
ba29384dd4 | ||
|
|
86a4514932 | ||
|
|
61d81ff35a | ||
|
|
588f91ab7b | ||
|
|
24b968029e | ||
|
|
0ab3c07017 | ||
|
|
2d89870b14 | ||
|
|
8e86366484 | ||
|
|
e5dfbf4adc | ||
|
|
04f5c68a83 | ||
|
|
52293a35ad | ||
|
|
553184b82b | ||
|
|
6289f8e371 | ||
|
|
5e6815d531 | ||
|
|
66d69f3d22 | ||
|
|
9a0c6e7e54 | ||
|
|
2ddbaf3324 |
@@ -5,7 +5,6 @@ redirects:
|
||||
usage/progressive-delivery: tutorials/istio-progressive-delivery.md
|
||||
usage/ab-testing: tutorials/istio-ab-testing.md
|
||||
usage/blue-green: tutorials/kubernetes-blue-green.md
|
||||
usage/appmesh-progressive-delivery: tutorials/appmesh-progressive-delivery.md
|
||||
usage/linkerd-progressive-delivery: tutorials/linkerd-progressive-delivery.md
|
||||
usage/contour-progressive-delivery: tutorials/contour-progressive-delivery.md
|
||||
usage/gloo-progressive-delivery: tutorials/gloo-progressive-delivery.md
|
||||
@@ -13,7 +12,7 @@ redirects:
|
||||
usage/skipper-progressive-delivery: tutorials/skipper-progressive-delivery.md
|
||||
usage/crossover-progressive-delivery: tutorials/crossover-progressive-delivery.md
|
||||
usage/traefik-progressive-delivery: tutorials/traefik-progressive-delivery.md
|
||||
usage/osm-progressive-delivery: tutorials/osm-progressive-delivery.md
|
||||
usage/kuma-progressive-delivery: tutorials/kuma-progressive-delivery.md
|
||||
usage/gatewayapi-progressive-delivery: tutorials/gatewayapi-progressive-delivery.md
|
||||
usage/apisix-progressive-delivery: tutorials/apisix-progressive-delivery.md
|
||||
usage/knative-progressive-delivery: tutorials/knative-progressive-delivery.md
|
||||
|
||||
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -1 +1 @@
|
||||
* @stefanprodan
|
||||
* @stefanprodan @aryan9600
|
||||
|
||||
13
.github/workflows/build.yaml
vendored
13
.github/workflows/build.yaml
vendored
@@ -16,22 +16,23 @@ jobs:
|
||||
build-flagger:
|
||||
runs-on:
|
||||
group: "Default Larger Runners"
|
||||
labels: ubuntu-latest-16-cores
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
go-version: 1.25.x
|
||||
cache-dependency-path: |
|
||||
**/go.sum
|
||||
**/go.mod
|
||||
- name: Download modules
|
||||
run: |
|
||||
go mod download
|
||||
go install golang.org/x/tools/cmd/goimports
|
||||
go install golang.org/x/tools/cmd/goimports@latest
|
||||
- name: Run linters
|
||||
run: make test-fmt test-codegen
|
||||
run: make fmt test-codegen
|
||||
- name: Verify CRDs
|
||||
run: make verify-crd
|
||||
- name: Run tests
|
||||
@@ -44,7 +45,7 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
uses: codecov/codecov-action@v5
|
||||
with:
|
||||
file: ./coverage.txt
|
||||
- name: Build container image
|
||||
|
||||
14
.github/workflows/e2e.yaml
vendored
14
.github/workflows/e2e.yaml
vendored
@@ -16,6 +16,7 @@ jobs:
|
||||
e2e-test:
|
||||
runs-on:
|
||||
group: "Default Larger Runners"
|
||||
labels: ubuntu-latest-16-cores
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
@@ -34,21 +35,22 @@ jobs:
|
||||
- gatewayapi
|
||||
- keda
|
||||
- apisix
|
||||
- knative
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Setup Kubernetes
|
||||
uses: helm/kind-action@v1.9.0
|
||||
uses: helm/kind-action@v1.12.0
|
||||
if: matrix.provider != 'skipper'
|
||||
with:
|
||||
version: v0.20.0
|
||||
version: v0.23.0
|
||||
cluster_name: kind
|
||||
node_image: kindest/node:v1.27.3@sha256:9dd3392d79af1b084671b05bcf65b21de476256ad1dcc853d9f3b10b4ac52dde
|
||||
node_image: kindest/node:v1.30.0@sha256:047357ac0cfea04663786a612ba1eaba9702bef25227a794b52890dd8bcd692e
|
||||
- name: Setup Kubernetes for skipper
|
||||
uses: helm/kind-action@v1.9.0
|
||||
uses: helm/kind-action@v1.12.0
|
||||
if: matrix.provider == 'skipper'
|
||||
with:
|
||||
version: v0.20.0
|
||||
version: v0.23.0
|
||||
cluster_name: kind
|
||||
node_image: kindest/node:v1.24.12@sha256:0bdca26bd7fe65c823640b14253ea7bac4baad9336b332c94850f84d8102f873
|
||||
- name: Build container image
|
||||
|
||||
2
.github/workflows/helm.yaml
vendored
2
.github/workflows/helm.yaml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Publish Helm charts
|
||||
uses: stefanprodan/helm-gh-pages@v1.7.0
|
||||
with:
|
||||
|
||||
6
.github/workflows/push-ld.yml
vendored
6
.github/workflows/push-ld.yml
vendored
@@ -16,8 +16,8 @@ jobs:
|
||||
id-token: write
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: sigstore/cosign-installer@v3.4.0
|
||||
- uses: actions/checkout@v5
|
||||
- uses: sigstore/cosign-installer@v3.10.0
|
||||
- name: Prepare
|
||||
id: prep
|
||||
run: |
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
type=raw,value=${{ steps.prep.outputs.VERSION }}
|
||||
- name: Publish image
|
||||
id: build-push
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
push: true
|
||||
builder: ${{ steps.buildx.outputs.name }}
|
||||
|
||||
16
.github/workflows/release.yml
vendored
16
.github/workflows/release.yml
vendored
@@ -27,13 +27,13 @@ jobs:
|
||||
id-token: write # needed for keyless signing
|
||||
packages: write # needed for ghcr access
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
go-version: 1.25.x
|
||||
- uses: fluxcd/flux2/action@main
|
||||
- uses: sigstore/cosign-installer@v3.4.0
|
||||
- uses: sigstore/cosign-installer@v3.10.0
|
||||
- name: Prepare
|
||||
id: prep
|
||||
run: |
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
type=raw,value=${{ steps.prep.outputs.VERSION }}
|
||||
- name: Publish image
|
||||
id: build-push
|
||||
uses: docker/build-push-action@v5
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
sbom: true
|
||||
provenance: true
|
||||
@@ -121,11 +121,11 @@ jobs:
|
||||
- uses: anchore/sbom-action/download-syft@v0
|
||||
- name: Create release and SBOM
|
||||
id: run-goreleaser
|
||||
uses: goreleaser/goreleaser-action@v5
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
with:
|
||||
version: latest
|
||||
args: release --release-notes=notes.md --rm-dist --skip-validate
|
||||
args: release --release-notes=notes.md --clean --skip=validate
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Generate SLSA metadata
|
||||
@@ -146,7 +146,7 @@ jobs:
|
||||
actions: read # for detecting the Github Actions environment.
|
||||
id-token: write # for creating OIDC tokens for signing.
|
||||
contents: write # for uploading attestations to GitHub releases.
|
||||
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.10.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-flagger.outputs.hashes }}"
|
||||
|
||||
17
.github/workflows/scan.yml
vendored
17
.github/workflows/scan.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
permissions:
|
||||
security-events: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Run FOSSA scan and upload build data
|
||||
uses: fossa-contrib/fossa-action@v3
|
||||
with:
|
||||
@@ -25,22 +25,21 @@ jobs:
|
||||
fossa-api-key: 5ee8bf422db1471e0bcf2bcb289185de
|
||||
github-token: ${{ github.token }}
|
||||
scan-codeql:
|
||||
runs-on:
|
||||
group: "Default Larger Runners"
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
security-events: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: 1.22.x
|
||||
go-version: 1.25.x
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v3
|
||||
uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: go
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v3
|
||||
uses: github/codeql-action/analyze@v4
|
||||
|
||||
230
CHANGELOG.md
230
CHANGELOG.md
@@ -2,6 +2,234 @@
|
||||
|
||||
All notable changes to this project are documented in this file.
|
||||
|
||||
## 1.42.0
|
||||
|
||||
**Release date:** 2025-10-16
|
||||
|
||||
This release comes with enhancements to Gateway API support, new metrics capabilities, and various bug fixes.
|
||||
|
||||
Flagger now supports Gateway API v1.4.0 and adds CORS policy configuration for Gateway API HTTPRoutes.
|
||||
For more information, please see the [Gateway API tutorial](https://docs.flagger.app/main/tutorials/gatewayapi-progressive-delivery#customising-the-httproute).
|
||||
|
||||
Session affinity support has been enhanced with [cookie attributes](https://docs.flagger.app/main/usage/deployment-strategies#configuring-additional-cookie-attributes)
|
||||
configuration for better control over session management.
|
||||
|
||||
A new `.spec.service.unmanagedMetadata` field has been added to the Canary API to allow 3rd-party
|
||||
controllers to set labels and annotations on the Kubernetes Services created by Flagger.
|
||||
|
||||
When running Flagger on Kubernetes 1.33 or later, users can now specify the traffic distribution
|
||||
using the new `.spec.service.trafficDistribution` Canary field. Depending on the Kubernetes version
|
||||
the traffic distribution can be set to `PreferClose`, `PreferSameZone` or `PreferSameNode`.
|
||||
See the [Kubernetes Service docs](https://kubernetes.io/docs/concepts/services-networking/service/#traffic-distribution) for more details.
|
||||
|
||||
This release is built with Go 1.25. The Kubernetes dependencies have been updated to 1.34,
|
||||
and the Traefik API has been migrated from `traefik.containo.us` to `traefik.io`.
|
||||
|
||||
#### Improvements
|
||||
- Update Gateway API to v1.4.0
|
||||
[#1842](https://github.com/fluxcd/flagger/pull/1842)
|
||||
- Add support for CORS policy to Gateway API
|
||||
[#1843](https://github.com/fluxcd/flagger/pull/1843)
|
||||
- Add support for setting traffic distribution
|
||||
[#1844](https://github.com/fluxcd/flagger/pull/1844)
|
||||
- Add count metrics for canary successes and failures
|
||||
[#1812](https://github.com/fluxcd/flagger/pull/1812)
|
||||
- Add support for cookie attributes in session affinity
|
||||
[#1826](https://github.com/fluxcd/flagger/pull/1826)
|
||||
- Add `unmanagedMetadata` to canary service specification
|
||||
[#1823](https://github.com/fluxcd/flagger/pull/1823)
|
||||
- Update dependencies to Kubernetes 1.34
|
||||
[#1832](https://github.com/fluxcd/flagger/pull/1832)
|
||||
- Build with Go 1.25
|
||||
[#1832](https://github.com/fluxcd/flagger/pull/1832)
|
||||
- Update Traefik API from traefik.containo.us to traefik.io
|
||||
[#1835](https://github.com/fluxcd/flagger/pull/1835)
|
||||
- Update GitOps install docs to latest Flux APIs
|
||||
[#1845](https://github.com/fluxcd/flagger/pull/1845)
|
||||
- loadtester: add pod security context
|
||||
[#1803](https://github.com/fluxcd/flagger/pull/1803)
|
||||
- Release loadtester 0.36.0
|
||||
[#1846](https://github.com/fluxcd/flagger/pull/1846)
|
||||
|
||||
#### Fixes
|
||||
- Fix: Gateway router should wait for accepted condition
|
||||
[#1791](https://github.com/fluxcd/flagger/pull/1791)
|
||||
- Fix: Send succeeded webhooks with correct phase
|
||||
[#1792](https://github.com/fluxcd/flagger/pull/1792)
|
||||
- Fix: Honor event webhook timeout
|
||||
[#1797](https://github.com/fluxcd/flagger/pull/1797)
|
||||
- Fix: Default namespace for cross-namespace ref validation
|
||||
[#1828](https://github.com/fluxcd/flagger/pull/1828)
|
||||
- Fix: APISIX E2E test
|
||||
[#1831](https://github.com/fluxcd/flagger/pull/1831)
|
||||
- Fix: Correct typo in AutoscalerReference type name
|
||||
[#1739](https://github.com/fluxcd/flagger/pull/1739)
|
||||
|
||||
## 1.41.0
|
||||
|
||||
**Release date:** 2025-04-02
|
||||
|
||||
This release comes with major features and minor bug fixes.
|
||||
|
||||
Flagger now supports Knative as a networking provider. This works a bit
|
||||
differently than compared to other service meshes/ingresses. Flagger does not
|
||||
generate any Kubernetes objects. It instead modifies the Knative service itself
|
||||
to configure weighted traffic routing. To learn more, please see the [tutorial](https://docs.flagger.app/tutorials/knative-progressive-delivery).
|
||||
|
||||
The session affinity canary release strategy has also been improved. Flagger can
|
||||
now configure Gateway API HTTPRoutes to also set a cookie for the primary
|
||||
deployment's response. For more info, see the [strategy docs](https://docs.flagger.app/usage/deployment-strategies#canary-release-with-session-affinity).
|
||||
|
||||
Furthermore, there's a new `.spec.service.headless` field which when set to
|
||||
true, tells Flagger to generate headless Kubernetes services. Also, support has
|
||||
been added for adding headers to the request Flagger sends to Prometheus for
|
||||
collecting metrics during an analysis via the `.spec.headers` field in the
|
||||
`MetricTemplate` object.
|
||||
|
||||
Finally, both Flagger and the load tester have been updated to use Go 1.24 and
|
||||
their dependencies have been updated as well.
|
||||
|
||||
#### Improvements
|
||||
- Allow headers to be added to Prometheus requests
|
||||
[#1757](https://github.com/fluxcd/flagger/pull/1757)
|
||||
- feat: Add support for primary backend cookies in session affinity (Gateway API)
|
||||
[#1783](https://github.com/fluxcd/flagger/pull/1783)
|
||||
- Update Go dependencies
|
||||
[#1787](https://github.com/fluxcd/flagger/pull/1787)
|
||||
- Build with Go 1.24
|
||||
[#1784](https://github.com/fluxcd/flagger/pull/1784)
|
||||
- Add support for Knative
|
||||
[#1682](https://github.com/fluxcd/flagger/pull/1682)
|
||||
- chart: add support for deploymentLabels
|
||||
[#1707](https://github.com/fluxcd/flagger/pull/1707)
|
||||
- chart: add support for deploymentLabels
|
||||
[#1707](https://github.com/fluxcd/flagger/pull/1707)
|
||||
- feat: add option to generate headless services
|
||||
[#1755](https://github.com/fluxcd/flagger/pull/1755)
|
||||
|
||||
#### Fixes
|
||||
- Fix: Do not evaluate incomplete samples from Datadog
|
||||
[#1763](https://github.com/fluxcd/flagger/pull/1763)
|
||||
- Prevent primary HPA collision for KEDA scaled objects when migrating from an HPA
|
||||
[#1677](https://github.com/fluxcd/flagger/pull/1677)
|
||||
|
||||
## 1.40.0
|
||||
|
||||
**Release date:** 2024-12-17
|
||||
|
||||
This release comes with support for Splunk Observability (formerly SignalFx) as a metrics provider.
|
||||
For more information on how to write `MetricTemplates` for Splunk, please see the
|
||||
[Splunk metrics tutorial](https://docs.flagger.app/usage/metrics#s#splunk).
|
||||
|
||||
Starting with this version, Flagger is compatible with the
|
||||
[AWS Gateway API Controller](https://www.gateway-api-controller.eks.aws.dev/latest/).
|
||||
|
||||
Both Flagger and the load tester Go dependencies have been updated to fix various CVEs.
|
||||
|
||||
#### Improvements
|
||||
- Add Splunk as a metrics provider
|
||||
[#1733](https://github.com/fluxcd/flagger/pull/1733)
|
||||
- Preserve HTTPRoute annotations injected by AWS Gateway API
|
||||
[#1746](https://github.com/fluxcd/flagger/pull/1746)
|
||||
- Automate `zz_generated.deepcopy.go` updates with make codegen
|
||||
[#1735](https://github.com/fluxcd/flagger/pull/1735)
|
||||
- Update dependencies
|
||||
[#1744](https://github.com/fluxcd/flagger/pull/1744)
|
||||
|
||||
## 1.39.0
|
||||
|
||||
**Release date:** 2024-11-26
|
||||
|
||||
This release comes with fixes and improvements. There is a new
|
||||
`.spec.analysis.webhooks[].disableTLS` field which disables TLS verification
|
||||
for that webhook request.
|
||||
A bug in the Gateway API provider was fixed which could lead to unecessary restarts.
|
||||
|
||||
This release is built with Go 1.23. Lastly, all Go dependencies, Alpine and
|
||||
Kubernetes libraries were updated.
|
||||
|
||||
#### Improvements
|
||||
- Add validation for `primaryScalerReplicas` field in the CRD
|
||||
[#1702](https://github.com/fluxcd/flagger/pull/1702)
|
||||
- feat: add `disableTLS` option for webhooks request
|
||||
[#1709](https://github.com/fluxcd/flagger/pull/1709)
|
||||
- Update dependencies to Kubernetes v1.31.3
|
||||
[#1723](https://github.com/fluxcd/flagger/pull/1723)
|
||||
- Update generated client for Kubernetes 1.31
|
||||
[#1725](https://github.com/fluxcd/flagger/pull/1725)
|
||||
- Build with Go 1.23
|
||||
[#1726](https://github.com/fluxcd/flagger/pull/1726)
|
||||
|
||||
#### Fixes
|
||||
- Gateway API: Sort header filters to avoid canary restarts
|
||||
[#1713](https://github.com/fluxcd/flagger/pull/1713)
|
||||
- fix: fix codegen script and update generated code
|
||||
[#1724](https://github.com/fluxcd/flagger/pull/1724)
|
||||
- fix(helm): podinfo fails to create the hpa object
|
||||
[#1721](https://github.com/fluxcd/flagger/pull/1721)
|
||||
|
||||
## 1.38.0
|
||||
|
||||
**Release date:** 2024-07-30
|
||||
|
||||
This release comes with several fixes and improvements. There is a new [Keptn
|
||||
metrics provider](https://docs.flagger.app/usage/metrics#keptn) that can be used
|
||||
for flexible grading logic and analysis.
|
||||
The loadtester chart now supports ServiceAccount annotations and the Flagger
|
||||
chart now supports specifying `honorLabels` for the PodMonitor.
|
||||
|
||||
Support for Kuma has been fixed and verified against Kuma 2.7.5. Also, the
|
||||
Deployment scaling has been updated to use `Patch` instead of `Update` to avoid
|
||||
intermittent conflict errors. Furthermore, a potential panic that could be
|
||||
caused due to Prometheus returning a range vector has been fixed. Also, the
|
||||
`request-duration` inbuilt query for Nginx has been updated to be more accurate.
|
||||
|
||||
Lastly, all Go dependencies, Alpine and Kubernetes libraries were updated.
|
||||
|
||||
#### Important
|
||||
|
||||
The update to Kubernetes libraries also brings an unwanted side-effect. Due to
|
||||
a change in upstream Kubernetes, sidecar support is done through a new field,
|
||||
which may be utilized by other services in your cluster. This would change the
|
||||
hash calculated by Flagger between runs and trigger an unwanted Canary
|
||||
analysis. Unfortunately, this is unavoidable. To get around this, users could
|
||||
set the `.spec.suspend` field to be true before updating to this version and
|
||||
switch it back when they update their application.
|
||||
|
||||
#### Improvements
|
||||
- Bumps golang.org/x/net to v0.23.0
|
||||
[#1628](https://github.com/fluxcd/flagger/pull/1628)
|
||||
- feat: implement a Keptn metrics provider
|
||||
[#1630](https://github.com/fluxcd/flagger/pull/1630)
|
||||
- Update dependencies to Kubernetes 1.30
|
||||
[#1638](https://github.com/fluxcd/flagger/pull/1638)
|
||||
- loadtester: add support for annotation on service account
|
||||
[#1649](https://github.com/fluxcd/flagger/pull/1649)
|
||||
- Bump golang.org/x/net to v0.25.0 and other deps.
|
||||
[#1653](https://github.com/fluxcd/flagger/pull/1653)
|
||||
- Update Go dependencies and Alpine
|
||||
[#1656](https://github.com/fluxcd/flagger/pull/1656)
|
||||
- Helm - Add podMonitor.honor labels
|
||||
[#1676](https://github.com/fluxcd/flagger/pull/1676)
|
||||
- kuma: bump e2e version to 2.7.5
|
||||
[#1683](https://github.com/fluxcd/flagger/pull/1683)
|
||||
- Release loadtester 0.33.0
|
||||
[#1690](https://github.com/fluxcd/flagger/pull/1690)
|
||||
- Bump google.golang.org/grpc from 1.64.0 to 1.64.1
|
||||
[#1675](https://github.com/fluxcd/flagger/pull/1675)
|
||||
|
||||
#### Fixes
|
||||
- Use `Patch` instead of `Update` for Deployment scaling
|
||||
[#1634](https://github.com/fluxcd/flagger/pull/1634)
|
||||
- block panic when prom returns range vector
|
||||
[#1637](https://github.com/fluxcd/flagger/pull/1637)
|
||||
- Fix removal of empty keys from flagger chart
|
||||
[#1657](https://github.com/fluxcd/flagger/pull/1657)
|
||||
- doc: fix KEDA doc regarding namespaces
|
||||
[#1666](https://github.com/fluxcd/flagger/pull/1666)
|
||||
- Fix Nginx request-duration query
|
||||
[#1686](https://github.com/fluxcd/flagger/pull/1686)
|
||||
|
||||
## 1.37.0
|
||||
|
||||
**Release date:** 2024-03-26
|
||||
@@ -409,7 +637,7 @@ routed to the canary workload pods.
|
||||
|
||||
**Release date:** 2022-12-15
|
||||
|
||||
This release comes with support for Apachae APISIX. For more details see the
|
||||
This release comes with support for Apache APISIX. For more details see the
|
||||
[tutorial](https://fluxcd.io/flagger/tutorials/apisix-progressive-delivery).
|
||||
|
||||
#### Improvements
|
||||
|
||||
14
Dockerfile
14
Dockerfile
@@ -1,4 +1,11 @@
|
||||
FROM golang:1.22-alpine as builder
|
||||
ARG GO_VERSION=1.25
|
||||
ARG XX_VERSION=1.6.1
|
||||
|
||||
FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS builder
|
||||
|
||||
# copy build utilities
|
||||
COPY --from=xx / /
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
ARG REVISON
|
||||
@@ -17,11 +24,12 @@ COPY cmd/ cmd/
|
||||
COPY pkg/ pkg/
|
||||
|
||||
# build
|
||||
RUN CGO_ENABLED=0 go build \
|
||||
ENV CGO_ENABLED=0
|
||||
RUN xx-go build \
|
||||
-ldflags "-s -w -X github.com/fluxcd/flagger/pkg/version.REVISION=${REVISON}" \
|
||||
-a -o flagger ./cmd/flagger
|
||||
|
||||
FROM alpine:3.19
|
||||
FROM alpine:3.22
|
||||
|
||||
RUN apk --no-cache add ca-certificates
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM golang:1.22-alpine as builder
|
||||
FROM golang:1.25-alpine AS builder
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
ARG TARGETARCH
|
||||
@@ -6,19 +6,19 @@ ARG REVISION
|
||||
|
||||
RUN apk --no-cache add alpine-sdk perl curl bash tar
|
||||
|
||||
RUN HELM3_VERSION=3.14.3 && \
|
||||
RUN HELM3_VERSION=3.19.0 && \
|
||||
curl -sSL "https://get.helm.sh/helm-v${HELM3_VERSION}-linux-${TARGETARCH}.tar.gz" | tar xvz && \
|
||||
chmod +x linux-${TARGETARCH}/helm && mv linux-${TARGETARCH}/helm /usr/local/bin/helm
|
||||
|
||||
RUN KUBECTL_VERSION=v1.28.0 && \
|
||||
RUN KUBECTL_VERSION=v1.34.1 && \
|
||||
curl -LO "https://dl.k8s.io/release/${KUBECTL_VERSION}/bin/linux/${TARGETARCH}/kubectl" && \
|
||||
chmod +x kubectl && mv kubectl /usr/local/bin/kubectl
|
||||
|
||||
RUN GRPC_HEALTH_PROBE_VERSION=v0.4.19 && \
|
||||
RUN GRPC_HEALTH_PROBE_VERSION=v0.4.35 && \
|
||||
wget -qO /usr/local/bin/grpc_health_probe https://github.com/grpc-ecosystem/grpc-health-probe/releases/download/${GRPC_HEALTH_PROBE_VERSION}/grpc_health_probe-linux-${TARGETARCH} && \
|
||||
chmod +x /usr/local/bin/grpc_health_probe
|
||||
|
||||
RUN GHZ_VERSION=0.117.0 && \
|
||||
RUN GHZ_VERSION=0.120.0 && \
|
||||
curl -sSL "https://github.com/bojand/ghz/archive/refs/tags/v${GHZ_VERSION}.tar.gz" | tar xz -C /tmp && \
|
||||
cd /tmp/ghz-${GHZ_VERSION}/cmd/ghz && GOARCH=$TARGETARCH go build . && mv ghz /usr/local/bin && \
|
||||
chmod +x /usr/local/bin/ghz
|
||||
@@ -39,7 +39,7 @@ COPY pkg/ pkg/
|
||||
# build
|
||||
RUN CGO_ENABLED=0 go build -o loadtester ./cmd/loadtester/*
|
||||
|
||||
FROM bash:5.0
|
||||
FROM bash:5.2
|
||||
|
||||
ARG TARGETPLATFORM
|
||||
|
||||
@@ -49,7 +49,7 @@ apk --no-cache add ca-certificates curl jq libgcc wrk hey git
|
||||
|
||||
WORKDIR /home/app
|
||||
|
||||
COPY --from=bats/bats:v1.1.0 /opt/bats/ /opt/bats/
|
||||
COPY --from=bats/bats:1.11.1 /opt/bats/ /opt/bats/
|
||||
RUN ln -s /opt/bats/bin/bats /usr/local/bin/
|
||||
|
||||
COPY --from=builder /usr/local/bin/helm /usr/local/bin/
|
||||
|
||||
14
Makefile
14
Makefile
@@ -6,19 +6,13 @@ build:
|
||||
CGO_ENABLED=0 go build -a -o ./bin/flagger ./cmd/flagger
|
||||
|
||||
tidy:
|
||||
rm -f go.sum; go mod tidy -compat=1.22
|
||||
rm -f go.sum; go mod tidy -compat=1.25
|
||||
|
||||
vet:
|
||||
go vet ./...
|
||||
|
||||
fmt:
|
||||
go mod tidy
|
||||
gofmt -l -s -w ./
|
||||
goimports -l -w ./
|
||||
|
||||
test-fmt:
|
||||
gofmt -l -s ./ | grep ".*\.go"; if [ "$$?" = "0" ]; then exit 1; fi
|
||||
goimports -l ./ | grep ".*\.go"; if [ "$$?" = "0" ]; then exit 1; fi
|
||||
go fmt ./...
|
||||
|
||||
codegen:
|
||||
./hack/update-codegen.sh
|
||||
@@ -26,10 +20,10 @@ codegen:
|
||||
test-codegen:
|
||||
./hack/verify-codegen.sh
|
||||
|
||||
test: test-fmt test-codegen
|
||||
test: fmt test-codegen
|
||||
go test ./...
|
||||
|
||||
test-coverage: test-fmt test-codegen
|
||||
test-coverage: fmt test-codegen
|
||||
go test -coverprofile cover.out ./...
|
||||
go tool cover -html=cover.out
|
||||
rm cover.out
|
||||
|
||||
99
README.md
99
README.md
@@ -1,4 +1,4 @@
|
||||
# flagger
|
||||
# Flagger
|
||||
|
||||
[](https://github.com/fluxcd/flagger/releases)
|
||||
[](https://bestpractices.coreinfrastructure.org/projects/4783)
|
||||
@@ -16,39 +16,26 @@ by gradually shifting traffic to the new version while measuring metrics and run
|
||||
Flagger implements several deployment strategies (Canary releases, A/B testing, Blue/Green mirroring)
|
||||
and integrates with various Kubernetes ingress controllers, service mesh, and monitoring solutions.
|
||||
|
||||
Flagger is a [Cloud Native Computing Foundation](https://cncf.io/) project
|
||||
Flagger is a [Cloud Native Computing Foundation](https://cncf.io/) graduated project
|
||||
and part of the [Flux](https://fluxcd.io) family of GitOps tools.
|
||||
|
||||
### Documentation
|
||||
|
||||
Flagger documentation can be found at [fluxcd.io/flagger](https://fluxcd.io/flagger/).
|
||||
The Flagger documentation can be found at [docs.flagger.app](https://docs.flagger.app/main).
|
||||
|
||||
* Install
|
||||
* [Flagger install on Kubernetes](https://fluxcd.io/flagger/install/flagger-install-on-kubernetes)
|
||||
* [Flagger Install with Flux](https://docs.flagger.app/main/install/flagger-install-with-flux)
|
||||
* Usage
|
||||
* [How it works](https://fluxcd.io/flagger/usage/how-it-works)
|
||||
* [Deployment strategies](https://fluxcd.io/flagger/usage/deployment-strategies)
|
||||
* [Metrics analysis](https://fluxcd.io/flagger/usage/metrics)
|
||||
* [Webhooks](https://fluxcd.io/flagger/usage/webhooks)
|
||||
* [Alerting](https://fluxcd.io/flagger/usage/alerting)
|
||||
* [Monitoring](https://fluxcd.io/flagger/usage/monitoring)
|
||||
* Tutorials
|
||||
* [App Mesh](https://fluxcd.io/flagger/tutorials/appmesh-progressive-delivery)
|
||||
* [Istio](https://fluxcd.io/flagger/tutorials/istio-progressive-delivery)
|
||||
* [Linkerd](https://fluxcd.io/flagger/tutorials/linkerd-progressive-delivery)
|
||||
* [Open Service Mesh (OSM)](https://dfluxcd.io/flagger/tutorials/osm-progressive-delivery)
|
||||
* [Kuma Service Mesh](https://fluxcd.io/flagger/tutorials/kuma-progressive-delivery)
|
||||
* [Contour](https://fluxcd.io/flagger/tutorials/contour-progressive-delivery)
|
||||
* [Gloo](https://fluxcd.io/flagger/tutorials/gloo-progressive-delivery)
|
||||
* [NGINX Ingress](https://fluxcd.io/flagger/tutorials/nginx-progressive-delivery)
|
||||
* [Skipper](https://fluxcd.io/flagger/tutorials/skipper-progressive-delivery)
|
||||
* [Traefik](https://fluxcd.io/flagger/tutorials/traefik-progressive-delivery)
|
||||
* [Gateway API](https://fluxcd.io/flagger/tutorials/gatewayapi-progressive-delivery/)
|
||||
* [Kubernetes Blue/Green](https://fluxcd.io/flagger/tutorials/kubernetes-blue-green)
|
||||
* [How it works](https://docs.flagger.app/main/usage/how-it-works)
|
||||
* [Deployment strategies](https://docs.flagger.app/main/usage/deployment-strategies)
|
||||
* [Metrics analysis](https://docs.flagger.app/main/usage/metrics)
|
||||
* [Webhooks](https://docs.flagger.app/main/usage/webhooks)
|
||||
* [Alerting](https://docs.flagger.app/main/usage/alerting)
|
||||
* [Monitoring](https://docs.flagger.app/main/usage/monitoring)
|
||||
|
||||
### Adopters
|
||||
|
||||
**Our list of production users has moved to <https://fluxcd.io/adopters/#flagger>**.
|
||||
The list of production users can be found at [fluxcd.io/adopters/#flagger](https://fluxcd.io/adopters/#flagger).
|
||||
|
||||
If you are using Flagger, please
|
||||
[submit a PR to add your organization](https://github.com/fluxcd/website/blob/main/data/adopters/2-flagger.yaml) to the list!
|
||||
@@ -72,8 +59,8 @@ metadata:
|
||||
namespace: test
|
||||
spec:
|
||||
# service mesh provider (optional)
|
||||
# can be: kubernetes, istio, linkerd, appmesh, nginx, skipper, contour, gloo, supergloo, traefik, osm
|
||||
# for SMI TrafficSplit can be: smi:v1alpha1, smi:v1alpha2, smi:v1alpha3
|
||||
# can be: kubernetes, istio, linkerd, kuma, knative, nginx, contour, gloo, traefik, skipper
|
||||
# for Gateway API implementations: gatewayapi:v1 and gatewayapi:v1beta1
|
||||
provider: istio
|
||||
# deployment reference
|
||||
targetRef:
|
||||
@@ -178,23 +165,23 @@ spec:
|
||||
name: on-call-msteams
|
||||
```
|
||||
|
||||
For more details on how the canary analysis and promotion works please [read the docs](https://fluxcd.io/flagger/usage/how-it-works).
|
||||
For more details on how the canary analysis and promotion works please [read the docs](https://docs.flagger.app/usage/how-it-works).
|
||||
|
||||
### Features
|
||||
|
||||
**Service Mesh**
|
||||
|
||||
| Feature | App Mesh | Istio | Linkerd | Kuma | OSM | Kubernetes CNI |
|
||||
|--------------------------------------------|--------------------|--------------------|--------------------|--------------------|--------------------|--------------------|
|
||||
| Canary deployments (weighted traffic) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: |
|
||||
| A/B testing (headers and cookies routing) | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: |
|
||||
| Blue/Green deployments (traffic switch) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Blue/Green deployments (traffic mirroring) | :heavy_minus_sign: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: |
|
||||
| Webhooks (acceptance/load testing) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Manual gating (approve/pause/resume) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Request success rate check (L7 metric) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: |
|
||||
| Request duration check (L7 metric) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: |
|
||||
| Custom metric checks | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Feature | Istio | Linkerd | Kuma | Knative | Kubernetes CNI |
|
||||
|--------------------------------------------|--------------------|--------------------|--------------------|--------------------|--------------------|
|
||||
| Canary deployments (weighted traffic) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: |
|
||||
| A/B testing (headers and cookies routing) | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: |
|
||||
| Blue/Green deployments (traffic switch) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_check_mark: |
|
||||
| Blue/Green deployments (traffic mirroring) | :heavy_check_mark: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: |
|
||||
| Webhooks (acceptance/load testing) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Manual gating (approve/pause/resume) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Request success rate check (L7 metric) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_check_mark: |
|
||||
| Request duration check (L7 metric) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_minus_sign: | :heavy_check_mark: |
|
||||
| Custom metric checks | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
|
||||
**Ingress**
|
||||
|
||||
@@ -211,29 +198,26 @@ For more details on how the canary analysis and promotion works please [read the
|
||||
|
||||
**Networking Interface**
|
||||
|
||||
| Feature | Gateway API | SMI |
|
||||
|-----------------------------------------------|--------------------|--------------------|
|
||||
| Canary deployments (weighted traffic) | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| A/B testing (headers and cookies routing) | :heavy_check_mark: | :heavy_minus_sign: |
|
||||
| Blue/Green deployments (traffic switch) | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Blue/Green deployments (traffic mirrroring) | :heavy_minus_sign: | :heavy_minus_sign: |
|
||||
| Webhooks (acceptance/load testing) | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Manual gating (approve/pause/resume) | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Request success rate check (L7 metric) | :heavy_minus_sign: | :heavy_minus_sign: |
|
||||
| Request duration check (L7 metric) | :heavy_minus_sign: | :heavy_minus_sign: |
|
||||
| Custom metric checks | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Feature | Gateway API | SMI |
|
||||
|--------------------------------------------|--------------------|--------------------|
|
||||
| Canary deployments (weighted traffic) | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Canary deployments with session affinity | :heavy_check_mark: | :heavy_minus_sign: |
|
||||
| A/B testing (headers and cookies routing) | :heavy_check_mark: | :heavy_minus_sign: |
|
||||
| Blue/Green deployments (traffic switch) | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Blue/Green deployments (traffic mirroring) | :heavy_minus_sign: | :heavy_minus_sign: |
|
||||
| Webhooks (acceptance/load testing) | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Manual gating (approve/pause/resume) | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Request success rate check (L7 metric) | :heavy_minus_sign: | :heavy_minus_sign: |
|
||||
| Request duration check (L7 metric) | :heavy_minus_sign: | :heavy_minus_sign: |
|
||||
| Custom metric checks | :heavy_check_mark: | :heavy_check_mark: |
|
||||
|
||||
For all [Gateway API](https://gateway-api.sigs.k8s.io/) implementations like
|
||||
[Contour](https://projectcontour.io/guides/gateway-api/) or
|
||||
[Istio](https://istio.io/latest/docs/tasks/traffic-management/ingress/gateway-api/)
|
||||
and [SMI](https://smi-spec.io) compatible service mesh solutions like
|
||||
[Nginx Service Mesh](https://docs.nginx.com/nginx-service-mesh/),
|
||||
[Prometheus MetricTemplates](https://docs.flagger.app/usage/metrics#prometheus)
|
||||
For all the [Gateway API](https://gateway-api.sigs.k8s.io/) compatible ingress controllers and service meshes,
|
||||
the [Prometheus MetricTemplates](https://docs.flagger.app/usage/metrics#prometheus)
|
||||
can be used to implement the request success rate and request duration checks.
|
||||
|
||||
### Roadmap
|
||||
|
||||
#### [GitOps Toolkit](https://github.com/fluxcd/flux2) compatibility
|
||||
#### [GitOps Toolkit](https://fluxcd.io/flux/components/) compatibility
|
||||
|
||||
- Migrate Flagger to Kubernetes controller-runtime and [kubebuilder](https://github.com/kubernetes-sigs/kubebuilder)
|
||||
- Make the Canary status compatible with [kstatus](https://github.com/kubernetes-sigs/cli-utils)
|
||||
@@ -242,8 +226,7 @@ can be used to implement the request success rate and request duration checks.
|
||||
|
||||
#### Integrations
|
||||
|
||||
- Add support for ingress controllers like HAProxy, ALB, and Apache APISIX
|
||||
- Add support for Knative Serving
|
||||
- Migrate Linkerd, Kuma and other service mesh integrations to Gateway API
|
||||
|
||||
### Contributing
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: test
|
||||
spec:
|
||||
provider: appmesh
|
||||
progressDeadlineSeconds: 600
|
||||
targetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: podinfo
|
||||
autoscalerRef:
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
name: podinfo
|
||||
service:
|
||||
port: 80
|
||||
targetPort: 9898
|
||||
meshName: global
|
||||
retries:
|
||||
attempts: 3
|
||||
perTryTimeout: 5s
|
||||
retryOn: "gateway-error,client-error,stream-error"
|
||||
timeout: 35s
|
||||
match:
|
||||
- uri:
|
||||
prefix: /
|
||||
rewrite:
|
||||
uri: /
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 10
|
||||
iterations: 10
|
||||
match:
|
||||
- headers:
|
||||
x-canary:
|
||||
exact: "insider"
|
||||
metrics:
|
||||
- name: request-success-rate
|
||||
thresholdRange:
|
||||
min: 99
|
||||
interval: 1m
|
||||
- name: request-duration
|
||||
thresholdRange:
|
||||
max: 500
|
||||
interval: 30s
|
||||
webhooks:
|
||||
- name: conformance-test
|
||||
type: pre-rollout
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 15s
|
||||
metadata:
|
||||
type: "bash"
|
||||
cmd: "curl -sd 'test' http://podinfo-canary.test/token | grep token"
|
||||
- name: load-test
|
||||
type: rollout
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
metadata:
|
||||
type: cmd
|
||||
cmd: "hey -z 1m -q 10 -c 2 -H 'X-Canary: insider' http://podinfo-canary.test/"
|
||||
@@ -1,59 +0,0 @@
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: test
|
||||
spec:
|
||||
provider: appmesh
|
||||
progressDeadlineSeconds: 600
|
||||
targetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: podinfo
|
||||
autoscalerRef:
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
name: podinfo
|
||||
service:
|
||||
port: 80
|
||||
targetPort: http
|
||||
meshName: global
|
||||
retries:
|
||||
attempts: 3
|
||||
perTryTimeout: 5s
|
||||
retryOn: "gateway-error,client-error,stream-error"
|
||||
timeout: 35s
|
||||
match:
|
||||
- uri:
|
||||
prefix: /
|
||||
rewrite:
|
||||
uri: /
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 10
|
||||
maxWeight: 50
|
||||
stepWeight: 5
|
||||
metrics:
|
||||
- name: request-success-rate
|
||||
thresholdRange:
|
||||
min: 99
|
||||
interval: 1m
|
||||
- name: request-duration
|
||||
thresholdRange:
|
||||
max: 500
|
||||
interval: 30s
|
||||
webhooks:
|
||||
- name: conformance-test
|
||||
type: pre-rollout
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 15s
|
||||
metadata:
|
||||
type: "bash"
|
||||
cmd: "curl -sd 'test' http://podinfo-canary.test/token | grep token"
|
||||
- name: load-test
|
||||
type: rollout
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
metadata:
|
||||
type: cmd
|
||||
cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test/"
|
||||
@@ -1,42 +0,0 @@
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: test
|
||||
spec:
|
||||
provider: osm
|
||||
targetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: podinfo
|
||||
progressDeadlineSeconds: 600
|
||||
service:
|
||||
port: 9898
|
||||
targetPort: 9898
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 10
|
||||
stepWeights: [5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55]
|
||||
metrics:
|
||||
- name: request-success-rate
|
||||
thresholdRange:
|
||||
min: 99
|
||||
interval: 1m
|
||||
- name: request-duration
|
||||
thresholdRange:
|
||||
max: 500
|
||||
interval: 30s
|
||||
webhooks:
|
||||
- name: acceptance-test
|
||||
type: pre-rollout
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 15s
|
||||
metadata:
|
||||
type: bash
|
||||
cmd: "curl -sd 'test' http://podinfo-canary.test:9898/token | grep token"
|
||||
- name: load-test
|
||||
type: rollout
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
metadata:
|
||||
cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/"
|
||||
@@ -1,43 +0,0 @@
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: test
|
||||
spec:
|
||||
provider: osm
|
||||
targetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: podinfo
|
||||
progressDeadlineSeconds: 600
|
||||
service:
|
||||
port: 9898
|
||||
targetPort: 9898
|
||||
analysis:
|
||||
interval: 15s
|
||||
threshold: 10
|
||||
maxWeight: 50
|
||||
stepWeight: 5
|
||||
metrics:
|
||||
- name: request-success-rate
|
||||
thresholdRange:
|
||||
min: 99
|
||||
interval: 1m
|
||||
- name: request-duration
|
||||
thresholdRange:
|
||||
max: 500
|
||||
interval: 30s
|
||||
webhooks:
|
||||
- name: acceptance-test
|
||||
type: pre-rollout
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 15s
|
||||
metadata:
|
||||
type: bash
|
||||
cmd: "curl -sd 'test' http://podinfo-canary.test:9898/token | grep token"
|
||||
- name: load-test
|
||||
type: rollout
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
metadata:
|
||||
cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/"
|
||||
@@ -80,7 +80,6 @@ spec:
|
||||
type: object
|
||||
required:
|
||||
- targetRef
|
||||
- service
|
||||
- analysis
|
||||
properties:
|
||||
provider:
|
||||
@@ -129,9 +128,11 @@ spec:
|
||||
type: object
|
||||
properties:
|
||||
minReplicas:
|
||||
type: number
|
||||
type: integer
|
||||
minimum: 1
|
||||
maxReplicas:
|
||||
type: number
|
||||
type: integer
|
||||
minimum: 1
|
||||
ingressRef:
|
||||
description: Ingress selector
|
||||
type: object
|
||||
@@ -190,11 +191,21 @@ spec:
|
||||
appProtocol:
|
||||
description: Application protocol of the port
|
||||
type: string
|
||||
trafficDistribution:
|
||||
description: Traffic distribution of the service
|
||||
type: string
|
||||
enum:
|
||||
- PreferClose
|
||||
- PreferSameZone
|
||||
- PreferSameNode
|
||||
targetPort:
|
||||
description: Container target port name
|
||||
x-kubernetes-int-or-string: true
|
||||
portDiscovery:
|
||||
description: Enable port dicovery
|
||||
description: Enable port discovery
|
||||
type: boolean
|
||||
headless:
|
||||
description: Headless if set to true, generates headless Kubernetes services.
|
||||
type: boolean
|
||||
timeout:
|
||||
description: HTTP or gRPC request timeout
|
||||
@@ -905,6 +916,18 @@ spec:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
unmanagedMetadata:
|
||||
description: UnmanagedMetadata is a list of metadata keys that should be ignored by Flagger.
|
||||
type: object
|
||||
properties:
|
||||
annotations:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
labels:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
skipAnalysis:
|
||||
description: Skip analysis and promote canary
|
||||
type: boolean
|
||||
@@ -1135,6 +1158,9 @@ spec:
|
||||
retries:
|
||||
description: Number of retries for this webhook
|
||||
type: number
|
||||
disableTLS:
|
||||
description: Disable TLS verification for this webhook
|
||||
type: boolean
|
||||
metadata:
|
||||
description: Metadata (key-value pairs) for this webhook
|
||||
type: object
|
||||
@@ -1148,10 +1174,35 @@ spec:
|
||||
cookieName:
|
||||
description: CookieName is the key that will be used for the session affinity cookie.
|
||||
type: string
|
||||
primaryCookieName:
|
||||
description: CookieName is the key that will be used for the session affinity cookie.
|
||||
type: string
|
||||
domain:
|
||||
description: Domain defines the host to which the cookie will be sent.
|
||||
type: string
|
||||
httpOnly:
|
||||
description: HttpOnly forbids JavaScript from accessing the cookie, for example, through the Document.cookie property.
|
||||
type: boolean
|
||||
maxAge:
|
||||
description: MaxAge indicates the number of seconds until the session affinity cookie will expire.
|
||||
default: 86400
|
||||
type: number
|
||||
partitioned:
|
||||
description: Partitioned indicates that the cookie should be stored using partitioned storage.
|
||||
type: boolean
|
||||
path:
|
||||
description: Path indicates the path that must exist in the requested URL for the browser to send the Cookie header.
|
||||
type: string
|
||||
sameSite:
|
||||
description: SameSite controls whether or not a cookie is sent with cross-site requests.
|
||||
type: string
|
||||
enum:
|
||||
- Strict
|
||||
- Lax
|
||||
- None
|
||||
secure:
|
||||
description: "Secure indicates that the cookie is sent to the server only when a request is made with the https: scheme (except on localhost)"
|
||||
type: boolean
|
||||
status:
|
||||
description: CanaryStatus defines the observed state of a canary.
|
||||
type: object
|
||||
@@ -1199,6 +1250,9 @@ spec:
|
||||
sessionAffinityCookie:
|
||||
description: Session affinity cookie of the current canary run
|
||||
type: string
|
||||
primarySessionAffinityCookie:
|
||||
description: Primary session affinity cookie of the current canary run
|
||||
type: string
|
||||
previousSessionAffinityCookie:
|
||||
description: Session affinity cookie of the previous canary run
|
||||
type: string
|
||||
@@ -1298,9 +1352,18 @@ spec:
|
||||
- newrelic
|
||||
- graphite
|
||||
- dynatrace
|
||||
- keptn
|
||||
- splunk
|
||||
address:
|
||||
description: API address of this provider
|
||||
type: string
|
||||
headers:
|
||||
description: Headers to add to HTTP(S) requests
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
secretRef:
|
||||
description: Kubernetes secret reference containing the provider credentials
|
||||
type: object
|
||||
|
||||
@@ -22,7 +22,7 @@ spec:
|
||||
serviceAccountName: flagger
|
||||
containers:
|
||||
- name: flagger
|
||||
image: ghcr.io/fluxcd/flagger:1.37.0
|
||||
image: ghcr.io/fluxcd/flagger:1.42.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
apiVersion: v1
|
||||
name: flagger
|
||||
version: 1.37.0
|
||||
appVersion: 1.37.0
|
||||
version: 1.42.0
|
||||
appVersion: 1.42.0
|
||||
kubeVersion: ">=1.19.0-0"
|
||||
engine: gotpl
|
||||
description: Flagger is a progressive delivery operator for Kubernetes
|
||||
@@ -19,7 +19,6 @@ keywords:
|
||||
- appmesh
|
||||
- linkerd
|
||||
- kuma
|
||||
- osm
|
||||
- smi
|
||||
- gloo
|
||||
- contour
|
||||
|
||||
@@ -59,15 +59,6 @@ $ helm upgrade -i flagger flagger/flagger \
|
||||
```
|
||||
|
||||
|
||||
To install Flagger for **Open Service Mesh** (requires OSM to have been installed with Prometheus):
|
||||
|
||||
```console
|
||||
$ helm upgrade -i flagger flagger/flagger \
|
||||
--namespace=osm-system \
|
||||
--set meshProvider=osm \
|
||||
--set metricsServer=http://osm-prometheus.osm-system.svc:7070
|
||||
```
|
||||
|
||||
To install Flagger for **Kuma Service Mesh** (requires Kuma to have been installed with Prometheus):
|
||||
|
||||
```console
|
||||
@@ -114,6 +105,15 @@ $ helm upgrade -i flagger flagger/flagger \
|
||||
--set meshProvider=traefik
|
||||
```
|
||||
|
||||
If you need to add labels to the flagger deployment or pods, you can pass the labels as parameters as shown below.
|
||||
|
||||
```console
|
||||
helm upgrade -i flagger flagger/flagger \
|
||||
<other parameters> \
|
||||
--set podLabels.<labelName>=<labelValue> \
|
||||
--set deploymentLabels.<labelName>=<labelValue>
|
||||
```
|
||||
|
||||
The [configuration](#configuration) section lists the parameters that can be configured during installation.
|
||||
|
||||
## Uninstalling the Chart
|
||||
@@ -157,6 +157,7 @@ The following tables lists the configurable parameters of the Flagger chart and
|
||||
| `podMonitor.namespace` | Namespace where the PodMonitor is created | the same namespace |
|
||||
| `podMonitor.interval` | Interval at which metrics should be scraped | `15s` |
|
||||
| `podMonitor.podMonitor` | Additional labels to add to the PodMonitor | `{}` |
|
||||
| `podMonitor.honorLabels` | If `true`, label conflicts are resolved by keeping label values from the scraped data and ignoring the conflicting server-side labels | `false` |
|
||||
| `leaderElection.enabled` | If `true`, Flagger will run in HA mode | `false` |
|
||||
| `leaderElection.replicaCount` | Number of replicas | `1` |
|
||||
| `serviceAccount.create` | If `true`, Flagger will create service account | `true` |
|
||||
@@ -185,6 +186,8 @@ The following tables lists the configurable parameters of the Flagger chart and
|
||||
| `podDisruptionBudget.minAvailable` | The minimal number of available replicas that will be set in the PodDisruptionBudget | `1` |
|
||||
| `noCrossNamespaceRefs` | If `true`, cross namespace references to custom resources will be disabled | `false` |
|
||||
| `namespace` | When specified, Flagger will restrict itself to watching Canary objects from that namespace | `""` |
|
||||
| `deploymentLabels` | Labels to add to Flagger deployment | `{}` |
|
||||
| `podLabels` | Labels to add to pods of Flagger deployment | `{}` |
|
||||
|
||||
Specify each parameter using the `--set key=value[,key=value]` argument to `helm upgrade`. For example,
|
||||
|
||||
|
||||
@@ -80,7 +80,6 @@ spec:
|
||||
type: object
|
||||
required:
|
||||
- targetRef
|
||||
- service
|
||||
- analysis
|
||||
properties:
|
||||
provider:
|
||||
@@ -129,9 +128,11 @@ spec:
|
||||
type: object
|
||||
properties:
|
||||
minReplicas:
|
||||
type: number
|
||||
type: integer
|
||||
minimum: 1
|
||||
maxReplicas:
|
||||
type: number
|
||||
type: integer
|
||||
minimum: 1
|
||||
ingressRef:
|
||||
description: Ingress selector
|
||||
type: object
|
||||
@@ -190,11 +191,21 @@ spec:
|
||||
appProtocol:
|
||||
description: Application protocol of the port
|
||||
type: string
|
||||
trafficDistribution:
|
||||
description: Traffic distribution of the service
|
||||
type: string
|
||||
enum:
|
||||
- PreferClose
|
||||
- PreferSameZone
|
||||
- PreferSameNode
|
||||
targetPort:
|
||||
description: Container target port name
|
||||
x-kubernetes-int-or-string: true
|
||||
portDiscovery:
|
||||
description: Enable port dicovery
|
||||
description: Enable port discovery
|
||||
type: boolean
|
||||
headless:
|
||||
description: Headless if set to true, generates headless Kubernetes services.
|
||||
type: boolean
|
||||
timeout:
|
||||
description: HTTP or gRPC request timeout
|
||||
@@ -905,6 +916,18 @@ spec:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
unmanagedMetadata:
|
||||
description: UnmanagedMetadata is a list of metadata keys that should be ignored by Flagger.
|
||||
type: object
|
||||
properties:
|
||||
annotations:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
labels:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
skipAnalysis:
|
||||
description: Skip analysis and promote canary
|
||||
type: boolean
|
||||
@@ -1135,6 +1158,9 @@ spec:
|
||||
retries:
|
||||
description: Number of retries for this webhook
|
||||
type: number
|
||||
disableTLS:
|
||||
description: Disable TLS verification for this webhook
|
||||
type: boolean
|
||||
metadata:
|
||||
description: Metadata (key-value pairs) for this webhook
|
||||
type: object
|
||||
@@ -1148,10 +1174,35 @@ spec:
|
||||
cookieName:
|
||||
description: CookieName is the key that will be used for the session affinity cookie.
|
||||
type: string
|
||||
primaryCookieName:
|
||||
description: CookieName is the key that will be used for the session affinity cookie.
|
||||
type: string
|
||||
domain:
|
||||
description: Domain defines the host to which the cookie will be sent.
|
||||
type: string
|
||||
httpOnly:
|
||||
description: HttpOnly forbids JavaScript from accessing the cookie, for example, through the Document.cookie property.
|
||||
type: boolean
|
||||
maxAge:
|
||||
description: MaxAge indicates the number of seconds until the session affinity cookie will expire.
|
||||
default: 86400
|
||||
type: number
|
||||
partitioned:
|
||||
description: Partitioned indicates that the cookie should be stored using partitioned storage.
|
||||
type: boolean
|
||||
path:
|
||||
description: Path indicates the path that must exist in the requested URL for the browser to send the Cookie header.
|
||||
type: string
|
||||
sameSite:
|
||||
description: SameSite controls whether or not a cookie is sent with cross-site requests.
|
||||
type: string
|
||||
enum:
|
||||
- Strict
|
||||
- Lax
|
||||
- None
|
||||
secure:
|
||||
description: "Secure indicates that the cookie is sent to the server only when a request is made with the https: scheme (except on localhost)"
|
||||
type: boolean
|
||||
status:
|
||||
description: CanaryStatus defines the observed state of a canary.
|
||||
type: object
|
||||
@@ -1199,6 +1250,9 @@ spec:
|
||||
sessionAffinityCookie:
|
||||
description: Session affinity cookie of the current canary run
|
||||
type: string
|
||||
primarySessionAffinityCookie:
|
||||
description: Primary session affinity cookie of the current canary run
|
||||
type: string
|
||||
previousSessionAffinityCookie:
|
||||
description: Session affinity cookie of the previous canary run
|
||||
type: string
|
||||
@@ -1298,9 +1352,18 @@ spec:
|
||||
- newrelic
|
||||
- graphite
|
||||
- dynatrace
|
||||
- keptn
|
||||
- splunk
|
||||
address:
|
||||
description: API address of this provider
|
||||
type: string
|
||||
headers:
|
||||
description: Headers to add to HTTP(S) requests
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
secretRef:
|
||||
description: Kubernetes secret reference containing the provider credentials
|
||||
type: object
|
||||
|
||||
@@ -4,8 +4,8 @@ kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ template "flagger.serviceAccountName" . }}
|
||||
namespace: {{ .Release.Namespace }}
|
||||
annotations:
|
||||
{{- if .Values.serviceAccount.annotations }}
|
||||
annotations:
|
||||
{{ toYaml .Values.serviceAccount.annotations | indent 4 }}
|
||||
{{- end }}
|
||||
labels:
|
||||
|
||||
@@ -9,6 +9,11 @@ metadata:
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/version: {{ .Chart.AppVersion }}
|
||||
{{- if .Values.deploymentLabels }}
|
||||
{{- range $key, $value := .Values.deploymentLabels }}
|
||||
{{ $key }}: {{ $value | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- with .Values.annotations }}
|
||||
annotations:
|
||||
{{- toYaml . | nindent 4 }}
|
||||
@@ -48,12 +53,12 @@ spec:
|
||||
imagePullSecrets:
|
||||
- name: {{ .Values.image.pullSecret }}
|
||||
{{- end }}
|
||||
{{- if .Values.controlplane.kubeconfig.secretName }}
|
||||
volumes:
|
||||
{{- if .Values.controlplane.kubeconfig.secretName }}
|
||||
- name: kubeconfig
|
||||
secret:
|
||||
secretName: "{{ .Values.controlplane.kubeconfig.secretName }}"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if .Values.additionalVolumes }}
|
||||
{{- toYaml .Values.additionalVolumes | nindent 8 -}}
|
||||
{{- end }}
|
||||
@@ -66,11 +71,11 @@ spec:
|
||||
securityContext:
|
||||
{{ toYaml .Values.securityContext.context | indent 12 }}
|
||||
{{- end }}
|
||||
{{- if .Values.controlplane.kubeconfig.secretName }}
|
||||
volumeMounts:
|
||||
{{- if .Values.controlplane.kubeconfig.secretName }}
|
||||
- name: kubeconfig
|
||||
mountPath: "/tmp/controlplane"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
|
||||
@@ -17,6 +17,7 @@ spec:
|
||||
- interval: {{ .Values.podMonitor.interval }}
|
||||
path: /metrics
|
||||
port: http
|
||||
honorLabels: {{ .Values.podMonitor.honorLabels }}
|
||||
namespaceSelector:
|
||||
matchNames:
|
||||
- {{ .Release.Namespace }}
|
||||
|
||||
@@ -197,7 +197,7 @@ rules:
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- traefik.containo.us
|
||||
- traefik.io
|
||||
resources:
|
||||
- traefikservices
|
||||
verbs:
|
||||
@@ -259,10 +259,36 @@ rules:
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- metrics.keptn.sh
|
||||
resources:
|
||||
- keptnmetrics
|
||||
- analyses
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- nonResourceURLs:
|
||||
- /version
|
||||
verbs:
|
||||
- get
|
||||
- apiGroups:
|
||||
- serving.knative.dev
|
||||
resources:
|
||||
- services
|
||||
verbs:
|
||||
- get
|
||||
- update
|
||||
- apiGroups:
|
||||
- serving.knative.dev
|
||||
resources:
|
||||
- revisions
|
||||
verbs:
|
||||
- get
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
image:
|
||||
repository: ghcr.io/fluxcd/flagger
|
||||
tag: 1.37.0
|
||||
tag: 1.42.0
|
||||
pullPolicy: IfNotPresent
|
||||
pullSecret:
|
||||
|
||||
@@ -32,7 +32,7 @@ serviceMonitor:
|
||||
# Set labels for the ServiceMonitor, use this to define your scrape label for Prometheus Operator
|
||||
# labels:
|
||||
|
||||
# accepted values are kubernetes, istio, linkerd, appmesh, contour, nginx, gloo, skipper, traefik, apisix, osm
|
||||
# accepted values are kubernetes, istio, linkerd, appmesh, contour, nginx, gloo, skipper, traefik, apisix
|
||||
meshProvider: ""
|
||||
|
||||
# single namespace restriction
|
||||
@@ -82,6 +82,7 @@ podMonitor:
|
||||
namespace:
|
||||
interval: 15s
|
||||
additionalLabels: {}
|
||||
honorLabels: false
|
||||
|
||||
#env:
|
||||
#- name: SLACK_URL
|
||||
@@ -194,8 +195,12 @@ podDisruptionBudget:
|
||||
enabled: false
|
||||
minAvailable: 1
|
||||
|
||||
# Additional labels to be added to pods
|
||||
podLabels: {}
|
||||
|
||||
# Additional labels to be added to deployments
|
||||
deploymentLabels: { }
|
||||
|
||||
noCrossNamespaceRefs: false
|
||||
|
||||
#Placeholder to supply additional volumes to the flagger pod
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
apiVersion: v1
|
||||
name: loadtester
|
||||
version: 0.32.0
|
||||
appVersion: 0.32.0
|
||||
version: 0.36.0
|
||||
appVersion: 0.36.0
|
||||
kubeVersion: ">=1.19.0-0"
|
||||
engine: gotpl
|
||||
description: Flagger's load testing services based on rakyll/hey and bojand/ghz that generates traffic during canary analysis when configured as a webhook.
|
||||
@@ -19,7 +19,6 @@ keywords:
|
||||
- appmesh
|
||||
- linkerd
|
||||
- gloo
|
||||
- osm
|
||||
- smi
|
||||
- gitops
|
||||
- load testing
|
||||
|
||||
@@ -69,8 +69,10 @@ The following tables lists the configurable parameters of the load tester chart
|
||||
| `istio.tls.enabled` | Enable TLS in gateway ( TLS secrets should be in namespace ) | `false` |
|
||||
| `istio.tls.httpsRedirect` | Redirect traffic to TLS port | `false` |
|
||||
| `podPriorityClassName` | PriorityClass name for pod priority configuration | "" |
|
||||
| `securityContext.enabled` | Add securityContext to container | "" |
|
||||
| `securityContext.context` | securityContext to add | "" |
|
||||
| `securityContext.enabled` | Add securityContext to container | `false` |
|
||||
| `SecurityContext.context` | securityContext to add | "" |
|
||||
| `podSecurityContext.enabled` | Add securityContext to pod | `false` |
|
||||
| `podSecurityContext.context` | securityContext to add | "" |
|
||||
| `podDisruptionBudget.enabled` | A PodDisruptionBudget will be created if `true` | `false` |
|
||||
| `podDisruptionBudget.minAvailable` | The minimal number of available replicas that will be set in the PodDisruptionBudget | `1` |
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ spec:
|
||||
appmesh.k8s.aws/ports: "444"
|
||||
openservicemesh.io/inbound-port-exclusion-list: "80, 8080"
|
||||
{{- if .Values.podAnnotations }}
|
||||
{{ toYaml .Values.podAnnotations | indent 8 }}
|
||||
{{- toYaml .Values.podAnnotations | nindent 8 }}
|
||||
{{- end }}
|
||||
spec:
|
||||
{{- if .Values.serviceAccountName }}
|
||||
@@ -39,7 +39,7 @@ spec:
|
||||
- name: {{ .Chart.Name }}
|
||||
{{- if .Values.securityContext.enabled }}
|
||||
securityContext:
|
||||
{{ toYaml .Values.securityContext.context | indent 12 }}
|
||||
{{- toYaml .Values.securityContext.context | nindent 12 }}
|
||||
{{- end }}
|
||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
@@ -102,3 +102,7 @@ spec:
|
||||
tolerations:
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
{{- if .Values.podSecurityContext.enabled }}
|
||||
securityContext:
|
||||
{{- toYaml .Values.podSecurityContext.context | nindent 12 }}
|
||||
{{- end }}
|
||||
|
||||
@@ -51,4 +51,7 @@ metadata:
|
||||
app.kubernetes.io/name: {{ template "loadtester.name" . }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
{{- if .Values.rbac.serviceAccountAnnotations }}
|
||||
annotations: {{ tpl (toYaml .Values.rbac.serviceAccountAnnotations) . | nindent 4 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -2,7 +2,7 @@ replicaCount: 1
|
||||
|
||||
image:
|
||||
repository: ghcr.io/fluxcd/flagger-loadtester
|
||||
tag: 0.32.0
|
||||
tag: 0.36.0
|
||||
pullPolicy: IfNotPresent
|
||||
pullSecret:
|
||||
|
||||
@@ -54,6 +54,8 @@ rbac:
|
||||
# resources: ["pods"]
|
||||
# verbs: ["list", "get"]
|
||||
rules: []
|
||||
# annotations to add to the service account
|
||||
serviceAccountAnnotations: {}
|
||||
|
||||
# name of an existing service account to use - if not creating rbac resources
|
||||
serviceAccountName: ""
|
||||
@@ -89,6 +91,12 @@ securityContext:
|
||||
runAsUser: 100
|
||||
runAsGroup: 101
|
||||
|
||||
podSecurityContext:
|
||||
enabled: false
|
||||
context:
|
||||
fsGroup: 101
|
||||
fsGroupChangePolicy: "OnRootMismatch"
|
||||
|
||||
podDisruptionBudget:
|
||||
enabled: false
|
||||
minAvailable: 1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
apiVersion: v1
|
||||
version: 6.1.3
|
||||
version: 6.1.4
|
||||
appVersion: 6.1.3
|
||||
name: podinfo
|
||||
engine: gotpl
|
||||
|
||||
@@ -20,12 +20,16 @@ spec:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
targetAverageUtilization: {{ .Values.hpa.cpu }}
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: {{ .Values.hpa.cpu }}
|
||||
{{- end }}
|
||||
{{- if .Values.hpa.memory }}
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
targetAverageValue: {{ .Values.hpa.memory }}
|
||||
target:
|
||||
type: AverageValue
|
||||
averageValue: {{ .Values.hpa.memory }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -51,6 +51,8 @@ import (
|
||||
"github.com/fluxcd/flagger/pkg/server"
|
||||
"github.com/fluxcd/flagger/pkg/signals"
|
||||
"github.com/fluxcd/flagger/pkg/version"
|
||||
|
||||
knative "knative.dev/serving/pkg/client/clientset/versioned"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -110,7 +112,7 @@ func init() {
|
||||
flag.BoolVar(&zapReplaceGlobals, "zap-replace-globals", false, "Whether to change the logging level of the global zap logger.")
|
||||
flag.StringVar(&zapEncoding, "zap-encoding", "json", "Zap logger encoding.")
|
||||
flag.StringVar(&namespace, "namespace", "", "Namespace that flagger would watch canary object.")
|
||||
flag.StringVar(&meshProvider, "mesh-provider", "istio", "Service mesh provider, can be istio, linkerd, appmesh, contour, gloo, nginx, skipper, traefik, apisix, osm or kuma.")
|
||||
flag.StringVar(&meshProvider, "mesh-provider", "istio", "Service mesh provider, can be istio, linkerd, appmesh, contour, knative, gloo, nginx, skipper, traefik, apisix, osm or kuma.")
|
||||
flag.StringVar(&selectorLabels, "selector-labels", "app,name,app.kubernetes.io/name", "List of pod labels that Flagger uses to create pod selectors.")
|
||||
flag.StringVar(&ingressAnnotationsPrefix, "ingress-annotations-prefix", "nginx.ingress.kubernetes.io", "Annotations prefix for NGINX ingresses.")
|
||||
flag.StringVar(&ingressClass, "ingress-class", "", "Ingress class used for annotating HTTPProxy objects.")
|
||||
@@ -166,6 +168,11 @@ func main() {
|
||||
logger.Fatalf("Error building flagger clientset: %s", err.Error())
|
||||
}
|
||||
|
||||
knativeClient, err := knative.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
logger.Fatalf("Error building knative clientset: %s", err.Error())
|
||||
}
|
||||
|
||||
// use a remote cluster for routing if a service mesh kubeconfig is specified
|
||||
if kubeconfigServiceMesh == "" {
|
||||
kubeconfigServiceMesh = kubeconfig
|
||||
@@ -221,7 +228,7 @@ func main() {
|
||||
setOwnerRefs = false
|
||||
}
|
||||
|
||||
routerFactory := router.NewFactory(cfg, kubeClient, flaggerClient, ingressAnnotationsPrefix, ingressClass, logger, meshClient, setOwnerRefs)
|
||||
routerFactory := router.NewFactory(cfg, kubeClient, flaggerClient, knativeClient, ingressAnnotationsPrefix, ingressClass, logger, meshClient, setOwnerRefs)
|
||||
|
||||
var configTracker canary.Tracker
|
||||
if enableConfigTracking {
|
||||
@@ -236,10 +243,11 @@ func main() {
|
||||
|
||||
includeLabelPrefixArray := strings.Split(includeLabelPrefix, ",")
|
||||
|
||||
canaryFactory := canary.NewFactory(kubeClient, flaggerClient, configTracker, labels, includeLabelPrefixArray, logger)
|
||||
canaryFactory := canary.NewFactory(kubeClient, flaggerClient, knativeClient, configTracker, labels, includeLabelPrefixArray, logger)
|
||||
|
||||
c := controller.NewController(
|
||||
kubeClient,
|
||||
knativeClient,
|
||||
flaggerClient,
|
||||
infos,
|
||||
controlLoopInterval,
|
||||
@@ -253,6 +261,7 @@ func main() {
|
||||
fromEnv("EVENT_WEBHOOK_URL", eventWebhook),
|
||||
clusterName,
|
||||
noCrossNamespaceRefs,
|
||||
cfg,
|
||||
)
|
||||
|
||||
// leader election context
|
||||
@@ -327,7 +336,7 @@ func startLeaderElection(ctx context.Context, run func(), ns string, kubeClient
|
||||
id = id + "_" + string(uuid.NewUUID())
|
||||
|
||||
lock, err := resourcelock.New(
|
||||
resourcelock.ConfigMapsLeasesResourceLock,
|
||||
resourcelock.LeasesResourceLock,
|
||||
ns,
|
||||
configMapName,
|
||||
kubeClient.CoreV1(),
|
||||
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
"github.com/fluxcd/flagger/pkg/signals"
|
||||
)
|
||||
|
||||
var VERSION = "0.32.0"
|
||||
var VERSION = "0.36.0"
|
||||
var (
|
||||
logLevel string
|
||||
port string
|
||||
|
||||
@@ -10,8 +10,7 @@ version in production by gradually shifting traffic to the new version while mea
|
||||
and running conformance tests.
|
||||
|
||||
Flagger implements several deployment strategies (Canary releases, A/B testing, Blue/Green mirroring)
|
||||
using a service mesh (App Mesh, Istio, Linkerd, Kuma, Open Service Mesh)
|
||||
or an ingress controller (Contour, Gloo, NGINX, Skipper, Traefik, APISIX) for traffic routing.
|
||||
using a service mesh or an ingress controller for traffic routing.
|
||||
For release analysis, Flagger can query Prometheus, InfluxDB, Datadog, New Relic, CloudWatch, Stackdriver
|
||||
or Graphite and for alerting it uses Slack, MS Teams, Discord and Rocket.
|
||||
|
||||
@@ -19,26 +18,23 @@ or Graphite and for alerting it uses Slack, MS Teams, Discord and Rocket.
|
||||
|
||||
Flagger can be configured with Kubernetes custom resources and is compatible with
|
||||
any CI/CD solutions made for Kubernetes. Since Flagger is declarative and reacts to Kubernetes events,
|
||||
it can be used in **GitOps** pipelines together with tools like [Flux](install/flagger-install-with-flux.md),
|
||||
JenkinsX, Carvel, Argo, etc.
|
||||
it can be used in **GitOps** pipelines together with tools like [Flux CD](install/flagger-install-with-flux.md).
|
||||
|
||||
Flagger is a [Cloud Native Computing Foundation](https://cncf.io/) project
|
||||
Flagger is a [Cloud Native Computing Foundation](https://cncf.io/) graduated project
|
||||
and part of [Flux](https://fluxcd.io) family of GitOps tools.
|
||||
|
||||
## Getting started
|
||||
|
||||
To get started with Flagger, choose one of the supported routing providers and
|
||||
[install](install/flagger-install-on-kubernetes.md) Flagger with Helm or Kustomize.
|
||||
[install](install/flagger-install-with-flux.md) Flagger with Flux CD.
|
||||
|
||||
After installing Flagger, you can follow one of these tutorials to get started:
|
||||
|
||||
**Service mesh tutorials**
|
||||
|
||||
* [Gateway API](tutorials/gatewayapi-progressive-delivery.md)
|
||||
* [Istio](tutorials/istio-progressive-delivery.md)
|
||||
* [Linkerd](tutorials/linkerd-progressive-delivery.md)
|
||||
* [AWS App Mesh](tutorials/appmesh-progressive-delivery.md)
|
||||
* [AWS App Mesh: Canary Deployment Using Flagger](https://www.eksworkshop.com/advanced/340_appmesh_flagger/)
|
||||
* [Open Service Mesh](tutorials/osm-progressive-delivery.md)
|
||||
* [Kuma](tutorials/kuma-progressive-delivery.md)
|
||||
|
||||
**Ingress controller tutorials**
|
||||
@@ -50,11 +46,5 @@ After installing Flagger, you can follow one of these tutorials to get started:
|
||||
* [Traefik](tutorials/traefik-progressive-delivery.md)
|
||||
* [Apache APISIX](tutorials/apisix-progressive-delivery.md)
|
||||
|
||||
**Hands-on GitOps workshops**
|
||||
|
||||
* [Istio](https://github.com/stefanprodan/gitops-istio)
|
||||
* [Linkerd](https://helm.workshop.flagger.dev)
|
||||
* [AWS App Mesh](https://eks.handson.flagger.dev)
|
||||
|
||||
The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation,
|
||||
please see our [Trademark Usage page](https://www.linuxfoundation.org/legal/trademark-usage).
|
||||
|
||||
@@ -7,9 +7,6 @@
|
||||
|
||||
* [Flagger Install on Kubernetes](install/flagger-install-on-kubernetes.md)
|
||||
* [Flagger Install with Flux](install/flagger-install-with-flux.md)
|
||||
* [Flagger Install on GKE Istio](install/flagger-install-on-google-cloud.md)
|
||||
* [Flagger Install on EKS App Mesh](install/flagger-install-on-eks-appmesh.md)
|
||||
* [Flagger Install on Alibaba ServiceMesh](install/flagger-install-on-alibaba-servicemesh.md)
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -22,19 +19,18 @@
|
||||
|
||||
## Tutorials
|
||||
|
||||
* [Gateway API Canary Deployments](tutorials/gatewayapi-progressive-delivery.md)
|
||||
* [Istio Canary Deployments](tutorials/istio-progressive-delivery.md)
|
||||
* [Istio A/B Testing](tutorials/istio-ab-testing.md)
|
||||
* [Linkerd Canary Deployments](tutorials/linkerd-progressive-delivery.md)
|
||||
* [App Mesh Canary Deployments](tutorials/appmesh-progressive-delivery.md)
|
||||
* [Kuma Canary Deployments](tutorials/kuma-progressive-delivery.md)
|
||||
* [Knative Canary Deployments](tutorials/knative-progressive-delivery.md)
|
||||
* [Contour Canary Deployments](tutorials/contour-progressive-delivery.md)
|
||||
* [Gloo Canary Deployments](tutorials/gloo-progressive-delivery.md)
|
||||
* [NGINX Canary Deployments](tutorials/nginx-progressive-delivery.md)
|
||||
* [Skipper Canary Deployments](tutorials/skipper-progressive-delivery.md)
|
||||
* [Traefik Canary Deployments](tutorials/traefik-progressive-delivery.md)
|
||||
* [Apache APISIX Canary Deployments](tutorials/apisix-progressive-delivery.md)
|
||||
* [Open Service Mesh Deployments](tutorials/osm-progressive-delivery.md)
|
||||
* [Kuma Canary Deployments](tutorials/kuma-progressive-delivery.md)
|
||||
* [Gateway API Canary Deployments](tutorials/gatewayapi-progressive-delivery.md)
|
||||
* [Blue/Green Deployments](tutorials/kubernetes-blue-green.md)
|
||||
* [Canary analysis with Prometheus Operator](tutorials/prometheus-operator.md)
|
||||
* [Canary analysis with KEDA ScaledObjects](tutorials/keda-scaledobject.md)
|
||||
|
||||
@@ -8,17 +8,13 @@ Flagger is written in Go and uses Go modules for dependency management.
|
||||
|
||||
On your dev machine install the following tools:
|
||||
|
||||
* go >= 1.19
|
||||
* git >;= 2.20
|
||||
* bash >= 5.0
|
||||
* make >= 3.81
|
||||
* kubectl >= 1.22
|
||||
* kustomize >= 4.4
|
||||
* go >= 1.25
|
||||
* kubectl >= 1.30
|
||||
* kustomize >= 5.0
|
||||
* helm >= 3.0
|
||||
* docker >= 19.03
|
||||
|
||||
You'll also need a Kubernetes cluster for testing Flagger.
|
||||
You can use Minikube, Kind, Docker desktop or any remote cluster (AKS/EKS/GKE/etc) Kubernetes version 1.22 or newer.
|
||||
You can use Minikube, Kind, Docker desktop or any remote cluster (AKS/EKS/GKE/etc).
|
||||
|
||||
To start contributing to Flagger, fork the [repository](https://github.com/fluxcd/flagger) on GitHub.
|
||||
|
||||
@@ -195,7 +191,6 @@ docker build -t test/flagger:latest .
|
||||
kind load docker-image test/flagger:latest
|
||||
```
|
||||
|
||||
|
||||
Run the Istio e2e tests:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
# Flagger Install on Alibaba ServiceMesh
|
||||
|
||||
This guide walks you through setting up Flagger on Alibaba ServiceMesh.
|
||||
|
||||
## Prerequisites
|
||||
- Created an ACK([Alibabacloud Container Service for Kubernetes](https://cs.console.aliyun.com)) cluster instance.
|
||||
- Create an ASM([Alibaba ServiceMesh](https://servicemesh.console.aliyun.com)) enterprise instance and add ACK cluster.
|
||||
|
||||
### Variables declaration
|
||||
- `$ACK_CONFIG`: the kubeconfig file path of ACK, which be treated as`$HOME/.kube/config` in the rest of guide.
|
||||
- `$MESH_CONFIG`: the kubeconfig file path of ASM.
|
||||
|
||||
### Enable Data-plane KubeAPI access in ASM
|
||||
|
||||
In the Alibaba Cloud Service Mesh (ASM) console, on the basic information page, make sure Data-plane KubeAPI access is enabled. When enabled, the Istio resources of the control plane can be managed through the Kubeconfig of the data plane cluster.
|
||||
|
||||
## Enable Prometheus
|
||||
|
||||
In the Alibaba Cloud Service Mesh (ASM) console, click Settings to enable the collection of Prometheus monitoring metrics. You can use the self-built Prometheus monitoring, or you can use the Alibaba Cloud ARMS Prometheus monitoring plug-in that has joined the ACK cluster, and use ARMS Prometheus to collect monitoring indicators.
|
||||
|
||||
## Install Flagger
|
||||
|
||||
Add Flagger Helm repository:
|
||||
|
||||
```bash
|
||||
helm repo add flagger https://flagger.app
|
||||
```
|
||||
|
||||
Install Flagger's Canary CRD:
|
||||
|
||||
```bash
|
||||
kubectl apply -f https://raw.githubusercontent.com/fluxcd/flagger/v1.21.0/artifacts/flagger/crd.yaml
|
||||
```
|
||||
## Deploy Flagger for Istio
|
||||
|
||||
### Add data plane cluster to Alibaba Cloud Service Mesh (ASM)
|
||||
|
||||
In the Alibaba Cloud Service Mesh (ASM) console, click Cluster & Workload Management, select the Kubernetes cluster, select the target ACK cluster, and add it to ASM.
|
||||
|
||||
### Prometheus address
|
||||
|
||||
If you are using Alibaba Cloud Container Service for Kubernetes (ACK) ARMS Prometheus monitoring, replace {Region-ID} in the link below with your region ID, such as cn-hangzhou. {ACKID} is the ACK ID of the data plane cluster that you added to Alibaba Cloud Service Mesh (ASM). Visit the following links to query the public and intranet addresses monitored by ACK's ARMS Prometheus:
|
||||
[https://arms.console.aliyun.com/#/promDetail/{Region-ID}/{ACK-ID}/setting](https://arms.console.aliyun.com/)
|
||||
|
||||
An example of an intranet address is as follows:
|
||||
[http://{Region-ID}-intranet.arms.aliyuncs.com:9090/api/v1/prometheus/{Prometheus-ID}/{u-id}/{ACK-ID}/{Region-ID}](https://arms.console.aliyun.com/)
|
||||
|
||||
## Deploy Flagger
|
||||
Replace the value of metricsServer with your Prometheus address.
|
||||
|
||||
```bash
|
||||
helm upgrade -i flagger flagger/flagger \
|
||||
--namespace=istio-system \
|
||||
--set crd.create=false \
|
||||
--set meshProvider=istio \
|
||||
--set metricsServer=http://prometheus:9090
|
||||
```
|
||||
@@ -1,151 +0,0 @@
|
||||
# Flagger Install on EKS App Mesh
|
||||
|
||||
This guide walks you through setting up Flagger and AWS App Mesh on EKS.
|
||||
|
||||
## App Mesh
|
||||
|
||||
The App Mesh integration with EKS is made out of the following components:
|
||||
|
||||
* Kubernetes custom resources
|
||||
* `mesh.appmesh.k8s.aws` defines a logical boundary for network traffic between the services
|
||||
* `virtualnode.appmesh.k8s.aws` defines a logical pointer to a Kubernetes workload
|
||||
* `virtualservice.appmesh.k8s.aws` defines the routing rules for a workload inside the mesh
|
||||
* CRD controller - keeps the custom resources in sync with the App Mesh control plane
|
||||
* Admission controller - injects the Envoy sidecar and assigns Kubernetes pods to App Mesh virtual nodes
|
||||
* Telemetry service - Prometheus instance that collects and stores Envoy's metrics
|
||||
|
||||
## Create a Kubernetes cluster
|
||||
|
||||
In order to create an EKS cluster you can use [eksctl](https://eksctl.io).
|
||||
Eksctl is an open source command-line utility made by Weaveworks in collaboration with Amazon.
|
||||
|
||||
On MacOS you can install eksctl with Homebrew:
|
||||
|
||||
```bash
|
||||
brew tap weaveworks/tap
|
||||
brew install weaveworks/tap/eksctl
|
||||
```
|
||||
|
||||
Create an EKS cluster with:
|
||||
|
||||
```bash
|
||||
eksctl create cluster --name=appmesh \
|
||||
--region=us-west-2 \
|
||||
--nodes 3 \
|
||||
--node-volume-size=120 \
|
||||
--appmesh-access
|
||||
```
|
||||
|
||||
The above command will create a two nodes cluster with
|
||||
App Mesh [IAM policy](https://docs.aws.amazon.com/app-mesh/latest/userguide/MESH_IAM_user_policies.html)
|
||||
attached to the EKS node instance role.
|
||||
|
||||
Verify the install with:
|
||||
|
||||
```bash
|
||||
kubectl get nodes
|
||||
```
|
||||
|
||||
## Install Helm
|
||||
|
||||
Install the [Helm](https://docs.helm.sh/using_helm/#installing-helm) v3 command-line tool:
|
||||
|
||||
```text
|
||||
brew install helm
|
||||
```
|
||||
|
||||
Add the EKS repository to Helm:
|
||||
|
||||
```bash
|
||||
helm repo add eks https://aws.github.io/eks-charts
|
||||
```
|
||||
|
||||
## Enable horizontal pod auto-scaling
|
||||
|
||||
Install the Horizontal Pod Autoscaler (HPA) metrics provider:
|
||||
|
||||
```bash
|
||||
helm upgrade -i metrics-server stable/metrics-server \
|
||||
--namespace kube-system \
|
||||
--set args[0]=--kubelet-preferred-address-types=InternalIP
|
||||
```
|
||||
|
||||
After a minute, the metrics API should report CPU and memory usage for pods. You can very the metrics API with:
|
||||
|
||||
```bash
|
||||
kubectl -n kube-system top pods
|
||||
```
|
||||
|
||||
## Install the App Mesh components
|
||||
|
||||
Install the App Mesh CRDs:
|
||||
|
||||
```bash
|
||||
kubectl apply -k github.com/aws/eks-charts/stable/appmesh-controller//crds?ref=master
|
||||
```
|
||||
|
||||
Create the `appmesh-system` namespace:
|
||||
|
||||
```bash
|
||||
kubectl create ns appmesh-system
|
||||
```
|
||||
|
||||
Install the App Mesh controller:
|
||||
|
||||
```bash
|
||||
helm upgrade -i appmesh-controller eks/appmesh-controller \
|
||||
--wait --namespace appmesh-system
|
||||
```
|
||||
|
||||
In order to collect the App Mesh metrics that Flagger needs to run the canary analysis,
|
||||
you'll need to setup a Prometheus instance to scrape the Envoy sidecars.
|
||||
|
||||
Install the App Mesh Prometheus:
|
||||
|
||||
```bash
|
||||
helm upgrade -i appmesh-prometheus eks/appmesh-prometheus \
|
||||
--wait --namespace appmesh-system
|
||||
```
|
||||
|
||||
## Install Flagger
|
||||
|
||||
Add Flagger Helm repository:
|
||||
|
||||
```bash
|
||||
helm repo add flagger https://flagger.app
|
||||
```
|
||||
|
||||
Install Flagger's Canary CRD:
|
||||
|
||||
```yaml
|
||||
kubectl apply -f https://raw.githubusercontent.com/fluxcd/flagger/main/artifacts/flagger/crd.yaml
|
||||
```
|
||||
|
||||
Deploy Flagger in the _**appmesh-system**_ namespace:
|
||||
|
||||
```bash
|
||||
helm upgrade -i flagger flagger/flagger \
|
||||
--namespace=appmesh-system \
|
||||
--set crd.create=false \
|
||||
--set meshProvider=appmesh:v1beta2 \
|
||||
--set metricsServer=http://appmesh-prometheus:9090
|
||||
```
|
||||
|
||||
## Install Grafana
|
||||
|
||||
Deploy App Mesh Grafana that comes with a dashboard for monitoring Flagger's canary releases:
|
||||
|
||||
```bash
|
||||
helm upgrade -i appmesh-grafana eks/appmesh-grafana \
|
||||
--namespace appmesh-system
|
||||
```
|
||||
|
||||
You can access Grafana using port forwarding:
|
||||
|
||||
```bash
|
||||
kubectl -n appmesh-system port-forward svc/appmesh-grafana 3000:3000
|
||||
```
|
||||
|
||||
Now that you have Flagger running, you can try the
|
||||
[App Mesh canary deployments tutorial](https://docs.flagger.app/usage/appmesh-progressive-delivery).
|
||||
|
||||
@@ -1,400 +0,0 @@
|
||||
# Flagger Install on GKE Istio
|
||||
|
||||
This guide walks you through setting up Flagger and Istio on Google Kubernetes Engine.
|
||||
|
||||

|
||||
|
||||
## Prerequisites
|
||||
|
||||
You will be creating a cluster on Google’s Kubernetes Engine \(GKE\), if you don’t have an account you can sign up [here](https://cloud.google.com/free/) for free credits.
|
||||
|
||||
Login into Google Cloud, create a project and enable billing for it.
|
||||
|
||||
Install the [gcloud](https://cloud.google.com/sdk/) command line utility and configure your project with `gcloud init`.
|
||||
|
||||
Set the default project \(replace `PROJECT_ID` with your own project\):
|
||||
|
||||
```text
|
||||
gcloud config set project PROJECT_ID
|
||||
```
|
||||
|
||||
Set the default compute region and zone:
|
||||
|
||||
```text
|
||||
gcloud config set compute/region us-central1
|
||||
gcloud config set compute/zone us-central1-a
|
||||
```
|
||||
|
||||
Enable the Kubernetes and Cloud DNS services for your project:
|
||||
|
||||
```text
|
||||
gcloud services enable container.googleapis.com
|
||||
gcloud services enable dns.googleapis.com
|
||||
```
|
||||
|
||||
Install the kubectl command-line tool:
|
||||
|
||||
```text
|
||||
gcloud components install kubectl
|
||||
```
|
||||
|
||||
## GKE cluster setup
|
||||
|
||||
Create a cluster with the Istio add-on:
|
||||
|
||||
```bash
|
||||
K8S_VERSION=$(gcloud container get-server-config --format=json \
|
||||
| jq -r '.validMasterVersions[0]')
|
||||
|
||||
gcloud beta container clusters create istio \
|
||||
--cluster-version=${K8S_VERSION} \
|
||||
--zone=us-central1-a \
|
||||
--num-nodes=2 \
|
||||
--machine-type=n1-highcpu-4 \
|
||||
--preemptible \
|
||||
--no-enable-cloud-logging \
|
||||
--no-enable-cloud-monitoring \
|
||||
--disk-size=30 \
|
||||
--enable-autorepair \
|
||||
--addons=HorizontalPodAutoscaling,Istio \
|
||||
--istio-config=auth=MTLS_PERMISSIVE
|
||||
```
|
||||
|
||||
The above command will create a default node pool consisting of two `n1-highcpu-4` \(vCPU: 4, RAM 3.60GB, DISK: 30GB\) preemptible VMs. Preemptible VMs are up to 80% cheaper than regular instances and are terminated and replaced after a maximum of 24 hours.
|
||||
|
||||
Set up credentials for `kubectl`:
|
||||
|
||||
```bash
|
||||
gcloud container clusters get-credentials istio
|
||||
```
|
||||
|
||||
Create a cluster admin role binding:
|
||||
|
||||
```bash
|
||||
kubectl create clusterrolebinding "cluster-admin-$(whoami)" \
|
||||
--clusterrole=cluster-admin \
|
||||
--user="$(gcloud config get-value core/account)"
|
||||
```
|
||||
|
||||
Validate your setup with:
|
||||
|
||||
```bash
|
||||
kubectl -n istio-system get svc
|
||||
```
|
||||
|
||||
In a couple of seconds GCP should allocate an external IP to the `istio-ingressgateway` service.
|
||||
|
||||
## Cloud DNS setup
|
||||
|
||||
You will need an internet domain and access to the registrar to change the name servers to Google Cloud DNS.
|
||||
|
||||
Create a managed zone named `istio` in Cloud DNS \(replace `example.com` with your domain\):
|
||||
|
||||
```bash
|
||||
gcloud dns managed-zones create \
|
||||
--dns-name="example.com." \
|
||||
--description="Istio zone" "istio"
|
||||
```
|
||||
|
||||
Look up your zone's name servers:
|
||||
|
||||
```bash
|
||||
gcloud dns managed-zones describe istio
|
||||
```
|
||||
|
||||
Update your registrar's name server records with the records returned by the above command.
|
||||
|
||||
Wait for the name servers to change \(replace `example.com` with your domain\):
|
||||
|
||||
```bash
|
||||
watch dig +short NS example.com
|
||||
```
|
||||
|
||||
Create a static IP address named `istio-gateway` using the Istio ingress IP:
|
||||
|
||||
```bash
|
||||
export GATEWAY_IP=$(kubectl -n istio-system get svc/istio-ingressgateway -ojson \
|
||||
| jq -r .status.loadBalancer.ingress[0].ip)
|
||||
|
||||
gcloud compute addresses create istio-gateway --addresses ${GATEWAY_IP} --region us-central1
|
||||
```
|
||||
|
||||
Create the following DNS records \(replace `example.com` with your domain\):
|
||||
|
||||
```bash
|
||||
DOMAIN="example.com"
|
||||
|
||||
gcloud dns record-sets transaction start --zone=istio
|
||||
|
||||
gcloud dns record-sets transaction add --zone=istio \
|
||||
--name="${DOMAIN}" --ttl=300 --type=A ${GATEWAY_IP}
|
||||
|
||||
gcloud dns record-sets transaction add --zone=istio \
|
||||
--name="www.${DOMAIN}" --ttl=300 --type=A ${GATEWAY_IP}
|
||||
|
||||
gcloud dns record-sets transaction add --zone=istio \
|
||||
--name="*.${DOMAIN}" --ttl=300 --type=A ${GATEWAY_IP}
|
||||
|
||||
gcloud dns record-sets transaction execute --zone istio
|
||||
```
|
||||
|
||||
Verify that the wildcard DNS is working \(replace `example.com` with your domain\):
|
||||
|
||||
```bash
|
||||
watch host test.example.com
|
||||
```
|
||||
|
||||
## Install Helm
|
||||
|
||||
Install the [Helm](https://docs.helm.sh/using_helm/#installing-helm) command-line tool:
|
||||
|
||||
```text
|
||||
brew install kubernetes-helm
|
||||
```
|
||||
|
||||
Create a service account and a cluster role binding for Tiller:
|
||||
|
||||
```bash
|
||||
kubectl -n kube-system create sa tiller
|
||||
|
||||
kubectl create clusterrolebinding tiller-cluster-rule \
|
||||
--clusterrole=cluster-admin \
|
||||
--serviceaccount=kube-system:tiller
|
||||
```
|
||||
|
||||
Deploy Tiller in the `kube-system` namespace:
|
||||
|
||||
```bash
|
||||
helm init --service-account tiller
|
||||
```
|
||||
|
||||
You should consider using SSL between Helm and Tiller, for more information on securing your Helm installation see [docs.helm.sh](https://docs.helm.sh/using_helm/#securing-your-helm-installation).
|
||||
|
||||
## Install cert-manager
|
||||
|
||||
Jetstack's [cert-manager](https://github.com/jetstack/cert-manager) is a Kubernetes operator that automatically creates and manages TLS certs issued by Let’s Encrypt.
|
||||
|
||||
You'll be using cert-manager to provision a wildcard certificate for the Istio ingress gateway.
|
||||
|
||||
Install cert-manager's CRDs:
|
||||
|
||||
```bash
|
||||
CERT_REPO=https://raw.githubusercontent.com/jetstack/cert-manager
|
||||
|
||||
kubectl apply -f ${CERT_REPO}/release-0.10/deploy/manifests/00-crds.yaml
|
||||
```
|
||||
|
||||
Create the cert-manager namespace and disable resource validation:
|
||||
|
||||
```bash
|
||||
kubectl create namespace cert-manager
|
||||
|
||||
kubectl label namespace cert-manager certmanager.k8s.io/disable-validation=true
|
||||
```
|
||||
|
||||
Install cert-manager with Helm:
|
||||
|
||||
```bash
|
||||
helm repo add jetstack https://charts.jetstack.io && \
|
||||
helm repo update && \
|
||||
helm upgrade -i cert-manager \
|
||||
--namespace cert-manager \
|
||||
--version v0.10.0 \
|
||||
jetstack/cert-manager
|
||||
```
|
||||
|
||||
## Istio Gateway TLS setup
|
||||
|
||||

|
||||
|
||||
Create a generic Istio Gateway to expose services outside the mesh on HTTPS:
|
||||
|
||||
```bash
|
||||
REPO=https://raw.githubusercontent.com/fluxcd/flagger/main
|
||||
|
||||
kubectl apply -f ${REPO}/artifacts/gke/istio-gateway.yaml
|
||||
```
|
||||
|
||||
Create a service account with Cloud DNS admin role \(replace `my-gcp-project` with your project ID\):
|
||||
|
||||
```bash
|
||||
GCP_PROJECT=my-gcp-project
|
||||
|
||||
gcloud iam service-accounts create dns-admin \
|
||||
--display-name=dns-admin \
|
||||
--project=${GCP_PROJECT}
|
||||
|
||||
gcloud iam service-accounts keys create ./gcp-dns-admin.json \
|
||||
--iam-account=dns-admin@${GCP_PROJECT}.iam.gserviceaccount.com \
|
||||
--project=${GCP_PROJECT}
|
||||
|
||||
gcloud projects add-iam-policy-binding ${GCP_PROJECT} \
|
||||
--member=serviceAccount:dns-admin@${GCP_PROJECT}.iam.gserviceaccount.com \
|
||||
--role=roles/dns.admin
|
||||
```
|
||||
|
||||
Create a Kubernetes secret with the GCP Cloud DNS admin key:
|
||||
|
||||
```bash
|
||||
kubectl create secret generic cert-manager-credentials \
|
||||
--from-file=./gcp-dns-admin.json \
|
||||
--namespace=istio-system
|
||||
```
|
||||
|
||||
Create a letsencrypt issuer for CloudDNS \(replace `email@example.com` with a valid email address and `my-gcp-project`with your project ID\):
|
||||
|
||||
```yaml
|
||||
apiVersion: certmanager.k8s.io/v1alpha1
|
||||
kind: Issuer
|
||||
metadata:
|
||||
name: letsencrypt-prod
|
||||
namespace: istio-system
|
||||
spec:
|
||||
acme:
|
||||
server: https://acme-v02.api.letsencrypt.org/directory
|
||||
email: email@example.com
|
||||
privateKeySecretRef:
|
||||
name: letsencrypt-prod
|
||||
dns01:
|
||||
providers:
|
||||
- name: cloud-dns
|
||||
clouddns:
|
||||
serviceAccountSecretRef:
|
||||
name: cert-manager-credentials
|
||||
key: gcp-dns-admin.json
|
||||
project: my-gcp-project
|
||||
```
|
||||
|
||||
Save the above resource as letsencrypt-issuer.yaml and then apply it:
|
||||
|
||||
```text
|
||||
kubectl apply -f ./letsencrypt-issuer.yaml
|
||||
```
|
||||
|
||||
Create a wildcard certificate \(replace `example.com` with your domain\):
|
||||
|
||||
```yaml
|
||||
apiVersion: certmanager.k8s.io/v1alpha1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: istio-gateway
|
||||
namespace: istio-system
|
||||
spec:
|
||||
secretName: istio-ingressgateway-certs
|
||||
issuerRef:
|
||||
name: letsencrypt-prod
|
||||
commonName: "*.example.com"
|
||||
acme:
|
||||
config:
|
||||
- dns01:
|
||||
provider: cloud-dns
|
||||
domains:
|
||||
- "*.example.com"
|
||||
- "example.com"
|
||||
```
|
||||
|
||||
Save the above resource as istio-gateway-cert.yaml and then apply it:
|
||||
|
||||
```text
|
||||
kubectl apply -f ./istio-gateway-cert.yaml
|
||||
```
|
||||
|
||||
In a couple of seconds cert-manager should fetch a wildcard certificate from letsencrypt.org:
|
||||
|
||||
```text
|
||||
kubectl -n istio-system describe certificate istio-gateway
|
||||
|
||||
Events:
|
||||
Type Reason Age From Message
|
||||
---- ------ ---- ---- -------
|
||||
Normal CertIssued 1m52s cert-manager Certificate issued successfully
|
||||
```
|
||||
|
||||
Recreate Istio ingress gateway pods:
|
||||
|
||||
```bash
|
||||
kubectl -n istio-system get pods -l istio=ingressgateway
|
||||
```
|
||||
|
||||
Note that Istio gateway doesn't reload the certificates from the TLS secret on cert-manager renewal. Since the GKE cluster is made out of preemptible VMs the gateway pods will be replaced once every 24h, if your not using preemptible nodes then you need to manually delete the gateway pods every two months before the certificate expires.
|
||||
|
||||
## Install Prometheus
|
||||
|
||||
The GKE Istio add-on does not include a Prometheus instance that scrapes the Istio telemetry service. Because Flagger uses the Istio HTTP metrics to run the canary analysis you have to deploy the following Prometheus configuration that's similar to the one that comes with the official Istio Helm chart.
|
||||
|
||||
Find the GKE Istio version with:
|
||||
|
||||
```bash
|
||||
kubectl -n istio-system get deploy istio-pilot -oyaml | grep image:
|
||||
```
|
||||
|
||||
Install Prometheus in istio-system namespace:
|
||||
|
||||
```bash
|
||||
kubectl -n istio-system apply -f \
|
||||
https://storage.googleapis.com/gke-release/istio/release/1.0.6-gke.3/patches/install-prometheus.yaml
|
||||
```
|
||||
|
||||
## Install Flagger and Grafana
|
||||
|
||||
Add Flagger Helm repository:
|
||||
|
||||
```bash
|
||||
helm repo add flagger https://flagger.app
|
||||
```
|
||||
|
||||
Install Flagger's Canary CRD:
|
||||
|
||||
```yaml
|
||||
kubectl apply -f https://raw.githubusercontent.com/fluxcd/flagger/main/artifacts/flagger/crd.yaml
|
||||
```
|
||||
|
||||
Deploy Flagger in the `istio-system` namespace with Slack notifications enabled:
|
||||
|
||||
```bash
|
||||
helm upgrade -i flagger flagger/flagger \
|
||||
--namespace=istio-system \
|
||||
--set crd.create=false \
|
||||
--set metricsServer=http://prometheus.istio-system:9090 \
|
||||
--set slack.url=https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK \
|
||||
--set slack.channel=general \
|
||||
--set slack.user=flagger
|
||||
```
|
||||
|
||||
Deploy Grafana in the `istio-system` namespace:
|
||||
|
||||
```bash
|
||||
helm upgrade -i flagger-grafana flagger/grafana \
|
||||
--namespace=istio-system \
|
||||
--set url=http://prometheus.istio-system:9090 \
|
||||
--set user=admin \
|
||||
--set password=replace-me
|
||||
```
|
||||
|
||||
Expose Grafana through the public gateway by creating a virtual service \(replace `example.com` with your domain\):
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.istio.io/v1beta1
|
||||
kind: VirtualService
|
||||
metadata:
|
||||
name: grafana
|
||||
namespace: istio-system
|
||||
spec:
|
||||
hosts:
|
||||
- "grafana.example.com"
|
||||
gateways:
|
||||
- istio-system/public-gateway
|
||||
http:
|
||||
- route:
|
||||
- destination:
|
||||
host: flagger-grafana
|
||||
```
|
||||
|
||||
Save the above resource as grafana-virtual-service.yaml and then apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f ./grafana-virtual-service.yaml
|
||||
```
|
||||
|
||||
Navigate to `http://grafana.example.com` in your browser and you should be redirected to the HTTPS version.
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
# Flagger Install on Kubernetes
|
||||
|
||||
This guide walks you through setting up Flagger on a Kubernetes cluster with Helm v3 or Kustomize.
|
||||
This guide walks you through setting up Flagger on a Kubernetes cluster with Helm or Kubectl.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Flagger requires a Kubernetes cluster **v1.16** or newer.
|
||||
See the [Flux install guide](flagger-install-with-flux.md) for installing Flagger and keeping it up to date the GitOps way.
|
||||
|
||||
## Install Flagger with Helm
|
||||
|
||||
@@ -61,29 +59,18 @@ helm upgrade -i flagger flagger/flagger \
|
||||
--set metricsServer=http://linkerd-prometheus:9090
|
||||
```
|
||||
|
||||
Deploy Flagger for App Mesh:
|
||||
|
||||
```bash
|
||||
helm upgrade -i flagger flagger/flagger \
|
||||
--namespace=appmesh-system \
|
||||
--set crd.create=false \
|
||||
--set meshProvider=appmesh \
|
||||
--set metricsServer=http://appmesh-prometheus:9090
|
||||
```
|
||||
|
||||
Deploy Flagger for **Open Service Mesh (OSM)** (requires OSM to have been installed with Prometheus):
|
||||
If you need to add labels to the flagger deployment or pods, you can pass the labels as parameters as shown below.
|
||||
|
||||
```console
|
||||
$ helm upgrade -i flagger flagger/flagger \
|
||||
--namespace=osm-system \
|
||||
--set crd.create=false \
|
||||
--set meshProvider=osm \
|
||||
--set metricsServer=http://osm-prometheus.osm-system.svc:7070
|
||||
helm upgrade -i flagger flagger/flagger \
|
||||
<other parameters> \
|
||||
--set podLabels.<labelName>=<labelValue> \
|
||||
--set deploymentLabels.<labelName>=<labelValue>
|
||||
```
|
||||
|
||||
You can install Flagger in any namespace as long as it can talk to the Prometheus service on port 9090.
|
||||
|
||||
For ingress controllers, the install instructions are:
|
||||
For ingress controllers, the installation instructions are:
|
||||
|
||||
* [Contour](https://docs.flagger.app/tutorials/contour-progressive-delivery)
|
||||
* [Gloo](https://docs.flagger.app/tutorials/gloo-progressive-delivery)
|
||||
@@ -92,20 +79,6 @@ For ingress controllers, the install instructions are:
|
||||
* [Traefik](https://docs.flagger.app/tutorials/traefik-progressive-delivery)
|
||||
* [APISIX](https://docs.flagger.app/tutorials/apisix-progressive-delivery)
|
||||
|
||||
You can use the helm template command and apply the generated yaml with kubectl:
|
||||
|
||||
```bash
|
||||
# generate
|
||||
helm fetch --untar --untardir . flagger/flagger &&
|
||||
helm template flagger ./flagger \
|
||||
--namespace=istio-system \
|
||||
--set metricsServer=http://prometheus.istio-system:9090 \
|
||||
> flagger.yaml
|
||||
|
||||
# apply
|
||||
kubectl apply -f flagger.yaml
|
||||
```
|
||||
|
||||
To uninstall the Flagger release with Helm run:
|
||||
|
||||
```text
|
||||
@@ -117,7 +90,7 @@ The command removes all the Kubernetes components associated with the chart and
|
||||
> **Note** that on uninstall the Canary CRD will not be removed. Deleting the CRD will make Kubernetes
|
||||
> remove all the objects owned by Flagger like Istio virtual services, Kubernetes deployments and ClusterIP services.
|
||||
|
||||
If you want to remove all the objects created by Flagger you have delete the Canary CRD with kubectl:
|
||||
If you want to remove all the objects created by Flagger you have to delete the Canary CRD with kubectl:
|
||||
|
||||
```text
|
||||
kubectl delete crd canaries.flagger.app
|
||||
@@ -137,73 +110,18 @@ helm upgrade -i flagger-grafana flagger/grafana \
|
||||
--set password=change-me
|
||||
```
|
||||
|
||||
Or use helm template command and apply the generated yaml with kubectl:
|
||||
|
||||
```bash
|
||||
# generate
|
||||
helm fetch --untar --untardir . flagger/grafana &&
|
||||
helm template flagger-grafana ./grafana \
|
||||
--namespace=istio-system \
|
||||
> flagger-grafana.yaml
|
||||
|
||||
# apply
|
||||
kubectl apply -f flagger-grafana.yaml
|
||||
```
|
||||
|
||||
You can access Grafana using port forwarding:
|
||||
|
||||
```bash
|
||||
kubectl -n istio-system port-forward svc/flagger-grafana 3000:80
|
||||
```
|
||||
|
||||
## Install Flagger with Kustomize
|
||||
## Install Flagger with Kubectl
|
||||
|
||||
As an alternative to Helm, Flagger can be installed with Kustomize **3.5.0** or newer.
|
||||
|
||||
**Service mesh specific installers**
|
||||
|
||||
Install Flagger for Istio:
|
||||
Install Flagger and Prometheus using the Kustomize overlay from the GitHub repository:
|
||||
|
||||
```bash
|
||||
kustomize build https://github.com/fluxcd/flagger/kustomize/istio?ref=main | kubectl apply -f -
|
||||
```
|
||||
|
||||
Install Flagger for AWS App Mesh:
|
||||
|
||||
```bash
|
||||
kustomize build https://github.com/fluxcd/flagger/kustomize/appmesh?ref=main | kubectl apply -f -
|
||||
```
|
||||
|
||||
This deploys Flagger and sets the metrics server URL to App Mesh's Prometheus instance.
|
||||
|
||||
Install Flagger for Linkerd:
|
||||
|
||||
```bash
|
||||
kustomize build https://github.com/fluxcd/flagger/kustomize/linkerd?ref=main | kubectl apply -f -
|
||||
```
|
||||
|
||||
This deploys Flagger in the `linkerd` namespace and sets the metrics server URL to Linkerd's Prometheus instance.
|
||||
|
||||
Install Flagger for Open Service Mesh:
|
||||
|
||||
```bash
|
||||
kustomize build https://github.com/fluxcd/flagger/kustomize/osm?ref=main | kubectl apply -f -
|
||||
```
|
||||
|
||||
This deploys Flagger in the `osm-system` namespace and sets the metrics server URL to OSM's Prometheus instance.
|
||||
|
||||
If you want to install a specific Flagger release, add the version number to the URL:
|
||||
|
||||
```bash
|
||||
kustomize build https://github.com/fluxcd/flagger/kustomize/linkerd?ref=v1.0.0 | kubectl apply -f -
|
||||
```
|
||||
|
||||
**Generic installer**
|
||||
|
||||
Install Flagger and Prometheus for Contour, Gloo, NGINX, Skipper, APISIX or Traefik ingress:
|
||||
|
||||
```bash
|
||||
kustomize build https://github.com/fluxcd/flagger/kustomize/kubernetes?ref=main | kubectl apply -f -
|
||||
kubectl apply -k https://github.com/fluxcd/flagger/kustomize/kubernetes?ref=main
|
||||
```
|
||||
|
||||
This deploys Flagger and Prometheus in the `flagger-system` namespace,
|
||||
@@ -212,20 +130,6 @@ sets the metrics server URL to `http://flagger-prometheus.flagger-system:9090` a
|
||||
The Prometheus instance has a two hours data retention and is configured to scrape all pods in your cluster
|
||||
that have the `prometheus.io/scrape: "true"` annotation.
|
||||
|
||||
To target a different provider you can specify it in the canary custom resource:
|
||||
|
||||
```yaml
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: app
|
||||
namespace: test
|
||||
spec:
|
||||
# can be: kubernetes, istio, linkerd, appmesh, nginx, skipper, gloo, traefik, osm, apisix
|
||||
# use the kubernetes provider for Blue/Green style deployments
|
||||
provider: nginx
|
||||
```
|
||||
|
||||
**Customized installer**
|
||||
|
||||
Create a kustomization file using Flagger as base and patch the container args:
|
||||
|
||||
@@ -35,46 +35,43 @@ metadata:
|
||||
toolkit.fluxcd.io/tenant: sre-team
|
||||
```
|
||||
|
||||
Define a Flux `HelmRepository` that points to where the Flagger Helm charts are stored:
|
||||
Define a Flux `OCIRepository` that points to where the Flagger Helm charts are stored:
|
||||
|
||||
```yaml
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta2
|
||||
kind: HelmRepository
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: OCIRepository
|
||||
metadata:
|
||||
name: flagger
|
||||
namespace: flagger-system
|
||||
spec:
|
||||
interval: 1h
|
||||
url: oci://ghcr.io/fluxcd/charts
|
||||
type: oci
|
||||
url: oci://ghcr.io/fluxcd/charts/flagger
|
||||
layerSelector:
|
||||
mediaType: "application/vnd.cncf.helm.chart.content.v1.tar+gzip"
|
||||
operation: copy
|
||||
ref:
|
||||
semver: "1.x" # update to the latest version
|
||||
```
|
||||
|
||||
Define a Flux `HelmRelease` that verifies and installs Flagger's latest version on the cluster:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2beta1
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: flagger
|
||||
namespace: flagger-system
|
||||
spec:
|
||||
interval: 1h
|
||||
interval: 12h
|
||||
releaseName: flagger
|
||||
install: # override existing Flagger CRDs
|
||||
crds: CreateReplace
|
||||
upgrade: # update Flagger CRDs
|
||||
crds: CreateReplace
|
||||
chart:
|
||||
spec:
|
||||
chart: flagger
|
||||
version: 1.x # update Flagger to the latest minor version
|
||||
interval: 6h # scan for new versions every six hours
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: flagger
|
||||
verify: # verify the chart signature with Cosign keyless
|
||||
provider: cosign
|
||||
chartRef:
|
||||
kind: OCIRepository
|
||||
name: flagger
|
||||
values:
|
||||
nodeSelector:
|
||||
kubernetes.io/os: linux
|
||||
@@ -88,7 +85,7 @@ After Flux reconciles the changes on your cluster, you can check if Flagger got
|
||||
```console
|
||||
$ helm list -n flagger-system
|
||||
NAME NAMESPACE REVISION STATUS CHART APP VERSION
|
||||
flagger flagger-system 1 deployed flagger-1.23.0 1.23.0
|
||||
flagger flagger-system 1 deployed flagger-1.42.0 1.42.0
|
||||
```
|
||||
|
||||
To uninstall Flagger, delete the `flagger.yaml` from your repository, then Flux will uninstall
|
||||
@@ -108,7 +105,7 @@ Define a Flux `OCIRepository` that points to where the Flagger Kustomize overlay
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1beta2
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: OCIRepository
|
||||
metadata:
|
||||
name: flagger-loadtester
|
||||
@@ -117,21 +114,20 @@ spec:
|
||||
interval: 6h # scan for new versions every six hours
|
||||
url: oci://ghcr.io/fluxcd/flagger-manifests
|
||||
ref:
|
||||
semver: 1.x # update to the latest version
|
||||
verify: # verify the artifact signature with Cosign keyless
|
||||
provider: cosign
|
||||
semver: "*" # update to the latest version
|
||||
```
|
||||
|
||||
Define a Flux `Kustomization` that deploys the Flagger load tester to the `apps` namespace:
|
||||
|
||||
```yaml
|
||||
---
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
|
||||
apiVersion: kustomize.toolkit.fluxcd.io/v1
|
||||
kind: Kustomization
|
||||
metadata:
|
||||
name: flagger-loadtester
|
||||
namespace: apps
|
||||
spec:
|
||||
targetNamespace: apps
|
||||
interval: 6h
|
||||
wait: true
|
||||
timeout: 5m
|
||||
@@ -140,7 +136,6 @@ spec:
|
||||
kind: OCIRepository
|
||||
name: flagger-loadtester
|
||||
path: ./tester
|
||||
targetNamespace: apps
|
||||
```
|
||||
|
||||
Copy the above manifests into a file called `flagger-loadtester.yaml`, place the YAML file
|
||||
|
||||
@@ -1,434 +0,0 @@
|
||||
# App Mesh Canary Deployments
|
||||
|
||||
This guide shows you how to use App Mesh and Flagger to automate canary deployments.
|
||||
You'll need an EKS cluster (Kubernetes >= 1.16) configured with App Mesh,
|
||||
you can find the installation guide [here](https://docs.flagger.app/install/flagger-install-on-eks-appmesh).
|
||||
|
||||
## Bootstrap
|
||||
|
||||
Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA),
|
||||
then creates a series of objects (Kubernetes deployments, ClusterIP services,
|
||||
App Mesh virtual nodes and services).
|
||||
These objects expose the application on the mesh and drive the canary analysis and promotion.
|
||||
The only App Mesh object you need to create by yourself is the mesh resource.
|
||||
|
||||
Create a mesh called `global`:
|
||||
|
||||
```bash
|
||||
cat << EOF | kubectl apply -f -
|
||||
apiVersion: appmesh.k8s.aws/v1beta2
|
||||
kind: Mesh
|
||||
metadata:
|
||||
name: global
|
||||
spec:
|
||||
namespaceSelector:
|
||||
matchLabels:
|
||||
appmesh.k8s.aws/sidecarInjectorWebhook: enabled
|
||||
EOF
|
||||
```
|
||||
|
||||
Create a test namespace with App Mesh sidecar injection enabled:
|
||||
|
||||
```bash
|
||||
cat << EOF | kubectl apply -f -
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: test
|
||||
labels:
|
||||
appmesh.k8s.aws/sidecarInjectorWebhook: enabled
|
||||
EOF
|
||||
```
|
||||
|
||||
Create a deployment and a horizontal pod autoscaler:
|
||||
|
||||
```bash
|
||||
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=main
|
||||
```
|
||||
|
||||
Deploy the load testing service to generate traffic during the canary analysis:
|
||||
|
||||
```bash
|
||||
helm upgrade -i flagger-loadtester flagger/loadtester \
|
||||
--namespace=test \
|
||||
--set appmesh.enabled=true \
|
||||
--set "appmesh.backends[0]=podinfo" \
|
||||
--set "appmesh.backends[1]=podinfo-canary"
|
||||
```
|
||||
|
||||
Create a canary definition:
|
||||
|
||||
```yaml
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
annotations:
|
||||
# Enable Envoy access logging to stdout.
|
||||
appmesh.flagger.app/accesslog: enabled
|
||||
name: podinfo
|
||||
namespace: test
|
||||
spec:
|
||||
# App Mesh API reference
|
||||
provider: appmesh:v1beta2
|
||||
# deployment reference
|
||||
targetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: podinfo
|
||||
# the maximum time in seconds for the canary deployment
|
||||
# to make progress before it is rollback (default 600s)
|
||||
progressDeadlineSeconds: 60
|
||||
# HPA reference (optional)
|
||||
autoscalerRef:
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
name: podinfo
|
||||
service:
|
||||
# container port
|
||||
port: 9898
|
||||
# App Mesh ingress timeout (optional)
|
||||
timeout: 15s
|
||||
# App Mesh retry policy (optional)
|
||||
retries:
|
||||
attempts: 3
|
||||
perTryTimeout: 5s
|
||||
retryOn: "gateway-error,client-error,stream-error"
|
||||
# App Mesh URI settings
|
||||
match:
|
||||
- uri:
|
||||
prefix: /
|
||||
rewrite:
|
||||
uri: /
|
||||
# define the canary analysis timing and KPIs
|
||||
analysis:
|
||||
# schedule interval (default 60s)
|
||||
interval: 1m
|
||||
# max number of failed metric checks before rollback
|
||||
threshold: 5
|
||||
# max traffic percentage routed to canary
|
||||
# percentage (0-100)
|
||||
maxWeight: 50
|
||||
# canary increment step
|
||||
# percentage (0-100)
|
||||
stepWeight: 5
|
||||
# App Mesh Prometheus checks
|
||||
metrics:
|
||||
- name: request-success-rate
|
||||
# minimum req success rate (non 5xx responses)
|
||||
# percentage (0-100)
|
||||
thresholdRange:
|
||||
min: 99
|
||||
interval: 1m
|
||||
- name: request-duration
|
||||
# maximum req duration P99
|
||||
# milliseconds
|
||||
thresholdRange:
|
||||
max: 500
|
||||
interval: 30s
|
||||
# testing (optional)
|
||||
webhooks:
|
||||
- name: acceptance-test
|
||||
type: pre-rollout
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 30s
|
||||
metadata:
|
||||
type: bash
|
||||
cmd: "curl -sd 'test' http://podinfo-canary.test:9898/token | grep token"
|
||||
- name: load-test
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
metadata:
|
||||
cmd: "hey -z 1m -q 10 -c 2 http://podinfo-canary.test:9898/"
|
||||
```
|
||||
|
||||
Save the above resource as podinfo-canary.yaml and then apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f ./podinfo-canary.yaml
|
||||
```
|
||||
|
||||
After a couple of seconds Flagger will create the canary objects:
|
||||
|
||||
```bash
|
||||
# applied
|
||||
deployment.apps/podinfo
|
||||
horizontalpodautoscaler.autoscaling/podinfo
|
||||
canary.flagger.app/podinfo
|
||||
|
||||
# generated Kubernetes objects
|
||||
deployment.apps/podinfo-primary
|
||||
horizontalpodautoscaler.autoscaling/podinfo-primary
|
||||
service/podinfo
|
||||
service/podinfo-canary
|
||||
service/podinfo-primary
|
||||
|
||||
# generated App Mesh objects
|
||||
virtualnode.appmesh.k8s.aws/podinfo-canary
|
||||
virtualnode.appmesh.k8s.aws/podinfo-primary
|
||||
virtualrouter.appmesh.k8s.aws/podinfo
|
||||
virtualrouter.appmesh.k8s.aws/podinfo-canary
|
||||
virtualservice.appmesh.k8s.aws/podinfo
|
||||
virtualservice.appmesh.k8s.aws/podinfo-canary
|
||||
```
|
||||
|
||||
After the bootstrap, the podinfo deployment will be scaled to zero and the traffic to `podinfo.test`
|
||||
will be routed to the primary pods.
|
||||
During the canary analysis, the `podinfo-canary.test` address can be used to target directly the canary pods.
|
||||
|
||||
App Mesh blocks all egress traffic by default.
|
||||
If your application needs to call another service, you have to create an App Mesh virtual service for it
|
||||
and add the virtual service name to the backend list.
|
||||
|
||||
```yaml
|
||||
service:
|
||||
port: 9898
|
||||
backends:
|
||||
- backend1
|
||||
- arn:aws:appmesh:eu-west-1:12345678910:mesh/my-mesh/virtualService/backend2
|
||||
```
|
||||
|
||||
## Setup App Mesh Gateway (optional)
|
||||
|
||||
In order to expose the podinfo app outside the mesh you can use the App Mesh Gateway.
|
||||
|
||||
Deploy the App Mesh Gateway behind an AWS NLB:
|
||||
|
||||
```bash
|
||||
helm upgrade -i appmesh-gateway eks/appmesh-gateway \
|
||||
--namespace test
|
||||
```
|
||||
|
||||
Find the gateway public address:
|
||||
|
||||
```bash
|
||||
export URL="http://$(kubectl -n test get svc/appmesh-gateway -ojson | jq -r ".status.loadBalancer.ingress[].hostname")"
|
||||
echo $URL
|
||||
```
|
||||
|
||||
Wait for the NLB to become active:
|
||||
|
||||
```bash
|
||||
watch curl -sS $URL
|
||||
```
|
||||
|
||||
Create a gateway route that points to the podinfo virtual service:
|
||||
|
||||
```yaml
|
||||
cat << EOF | kubectl apply -f -
|
||||
apiVersion: appmesh.k8s.aws/v1beta2
|
||||
kind: GatewayRoute
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: test
|
||||
spec:
|
||||
httpRoute:
|
||||
match:
|
||||
prefix: "/"
|
||||
action:
|
||||
target:
|
||||
virtualService:
|
||||
virtualServiceRef:
|
||||
name: podinfo
|
||||
EOF
|
||||
```
|
||||
|
||||
Open your browser and navigate to the ingress address to access podinfo UI.
|
||||
|
||||
## Automated canary promotion
|
||||
|
||||
A canary deployment is triggered by changes in any of the following objects:
|
||||
|
||||
* Deployment PodSpec (container image, command, ports, env, resources, etc)
|
||||
* ConfigMaps and Secrets mounted as volumes or mapped to environment variables
|
||||
|
||||
Trigger a canary deployment by updating the container image:
|
||||
|
||||
```bash
|
||||
kubectl -n test set image deployment/podinfo \
|
||||
podinfod=ghcr.io/stefanprodan/podinfo:6.0.1
|
||||
```
|
||||
|
||||
Flagger detects that the deployment revision changed and starts a new rollout:
|
||||
|
||||
```text
|
||||
kubectl -n test describe canary/podinfo
|
||||
|
||||
Status:
|
||||
Canary Weight: 0
|
||||
Failed Checks: 0
|
||||
Phase: Succeeded
|
||||
Events:
|
||||
New revision detected! Scaling up podinfo.test
|
||||
Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available
|
||||
Pre-rollout check acceptance-test passed
|
||||
Advance podinfo.test canary weight 5
|
||||
Advance podinfo.test canary weight 10
|
||||
Advance podinfo.test canary weight 15
|
||||
Advance podinfo.test canary weight 20
|
||||
Advance podinfo.test canary weight 25
|
||||
Advance podinfo.test canary weight 30
|
||||
Advance podinfo.test canary weight 35
|
||||
Advance podinfo.test canary weight 40
|
||||
Advance podinfo.test canary weight 45
|
||||
Advance podinfo.test canary weight 50
|
||||
Copying podinfo.test template spec to podinfo-primary.test
|
||||
Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available
|
||||
Routing all traffic to primary
|
||||
Promotion completed! Scaling down podinfo.test
|
||||
```
|
||||
|
||||
When the canary analysis starts, Flagger will call the pre-rollout webhooks before routing traffic to the canary.
|
||||
|
||||
**Note** that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis.
|
||||
|
||||
During the analysis the canary’s progress can be monitored with Grafana.
|
||||
The App Mesh dashboard URL is
|
||||
[http://localhost:3000/d/flagger-appmesh/appmesh-canary?refresh=10s&orgId=1&var-namespace=test&var-primary=podinfo-primary&var-canary=podinfo](http://localhost:3000/d/flagger-appmesh/appmesh-canary?refresh=10s&orgId=1&var-namespace=test&var-primary=podinfo-primary&var-canary=podinfo).
|
||||
|
||||

|
||||
|
||||
You can monitor all canaries with:
|
||||
|
||||
```bash
|
||||
watch kubectl get canaries --all-namespaces
|
||||
|
||||
NAMESPACE NAME STATUS WEIGHT
|
||||
test podinfo Progressing 15
|
||||
prod frontend Succeeded 0
|
||||
prod backend Failed 0
|
||||
```
|
||||
|
||||
If you’ve enabled the Slack notifications, you should receive the following messages:
|
||||
|
||||

|
||||
|
||||
## Automated rollback
|
||||
|
||||
During the canary analysis you can generate HTTP 500 errors or high latency to test if Flagger pauses the rollout.
|
||||
|
||||
Trigger a canary deployment:
|
||||
|
||||
```bash
|
||||
kubectl -n test set image deployment/podinfo \
|
||||
podinfod=ghcr.io/stefanprodan/podinfo:6.0.2
|
||||
```
|
||||
|
||||
Exec into the load tester pod with:
|
||||
|
||||
```bash
|
||||
kubectl -n test exec -it deploy/flagger-loadtester bash
|
||||
```
|
||||
|
||||
Generate HTTP 500 errors:
|
||||
|
||||
```bash
|
||||
hey -z 1m -c 5 -q 5 http://podinfo-canary.test:9898/status/500
|
||||
```
|
||||
|
||||
Generate latency:
|
||||
|
||||
```bash
|
||||
watch -n 1 curl http://podinfo-canary.test:9898/delay/1
|
||||
```
|
||||
|
||||
When the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary,
|
||||
the canary is scaled to zero and the rollout is marked as failed.
|
||||
|
||||
```text
|
||||
kubectl -n appmesh-system logs deploy/flagger -f | jq .msg
|
||||
|
||||
New revision detected! progressing canary analysis for podinfo.test
|
||||
Pre-rollout check acceptance-test passed
|
||||
Advance podinfo.test canary weight 5
|
||||
Advance podinfo.test canary weight 10
|
||||
Advance podinfo.test canary weight 15
|
||||
Halt podinfo.test advancement success rate 69.17% < 99%
|
||||
Halt podinfo.test advancement success rate 61.39% < 99%
|
||||
Halt podinfo.test advancement success rate 55.06% < 99%
|
||||
Halt podinfo.test advancement request duration 1.20s > 0.5s
|
||||
Halt podinfo.test advancement request duration 1.45s > 0.5s
|
||||
Rolling back podinfo.test failed checks threshold reached 5
|
||||
Canary failed! Scaling down podinfo.test
|
||||
```
|
||||
|
||||
If you’ve enabled the Slack notifications, you’ll receive a message if the progress deadline is exceeded,
|
||||
or if the analysis reached the maximum number of failed checks:
|
||||
|
||||

|
||||
|
||||
## A/B Testing
|
||||
|
||||
Besides weighted routing, Flagger can be configured to route traffic to the canary based on HTTP match conditions.
|
||||
In an A/B testing scenario, you'll be using HTTP headers or cookies to target a certain segment of your users.
|
||||
This is particularly useful for frontend applications that require session affinity.
|
||||
|
||||

|
||||
|
||||
Edit the canary analysis, remove the max/step weight and add the match conditions and iterations:
|
||||
|
||||
```yaml
|
||||
analysis:
|
||||
interval: 1m
|
||||
threshold: 5
|
||||
iterations: 10
|
||||
match:
|
||||
- headers:
|
||||
x-canary:
|
||||
exact: "insider"
|
||||
webhooks:
|
||||
- name: load-test
|
||||
url: http://flagger-loadtester.test/
|
||||
metadata:
|
||||
cmd: "hey -z 1m -q 10 -c 2 -H 'X-Canary: insider' http://podinfo.test:9898/"
|
||||
```
|
||||
|
||||
The above configuration will run an analysis for ten minutes targeting users that have a `X-Canary: insider` header.
|
||||
|
||||
You can also use a HTTP cookie, to target all users with a `canary` cookie set to `insider` the match condition should be:
|
||||
|
||||
```yaml
|
||||
match:
|
||||
- headers:
|
||||
cookie:
|
||||
regex: "^(.*?;)?(canary=insider)(;.*)?$"
|
||||
webhooks:
|
||||
- name: load-test
|
||||
url: http://flagger-loadtester.test/
|
||||
metadata:
|
||||
cmd: "hey -z 1m -q 10 -c 2 -H 'Cookie: canary=insider' http://podinfo.test:9898/"
|
||||
```
|
||||
|
||||
Trigger a canary deployment by updating the container image:
|
||||
|
||||
```bash
|
||||
kubectl -n test set image deployment/podinfo \
|
||||
podinfod=ghcr.io/stefanprodan/podinfo:6.0.3
|
||||
```
|
||||
|
||||
Flagger detects that the deployment revision changed and starts the A/B test:
|
||||
|
||||
```text
|
||||
kubectl -n appmesh-system logs deploy/flagger -f | jq .msg
|
||||
|
||||
New revision detected! progressing canary analysis for podinfo.test
|
||||
Advance podinfo.test canary iteration 1/10
|
||||
Advance podinfo.test canary iteration 2/10
|
||||
Advance podinfo.test canary iteration 3/10
|
||||
Advance podinfo.test canary iteration 4/10
|
||||
Advance podinfo.test canary iteration 5/10
|
||||
Advance podinfo.test canary iteration 6/10
|
||||
Advance podinfo.test canary iteration 7/10
|
||||
Advance podinfo.test canary iteration 8/10
|
||||
Advance podinfo.test canary iteration 9/10
|
||||
Advance podinfo.test canary iteration 10/10
|
||||
Copying podinfo.test template spec to podinfo-primary.test
|
||||
Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available
|
||||
Routing all traffic to primary
|
||||
Promotion completed! Scaling down podinfo.test
|
||||
```
|
||||
|
||||
The above procedure can be extended with
|
||||
[custom metrics](../usage/metrics.md) checks,
|
||||
[webhooks](../usage/webhooks.md),
|
||||
[manual promotion](../usage/webhooks.md#manual-gating) approval and
|
||||
[Slack or MS Teams](../usage/alerting.md) notifications.
|
||||
@@ -1,347 +0,0 @@
|
||||
# Canaries with Helm charts and GitOps
|
||||
|
||||
This guide shows you how to package a web app into a Helm chart, trigger canary deployments on Helm upgrade and automate the chart release process with Weave Flux.
|
||||
|
||||
## Packaging
|
||||
|
||||
You'll be using the [podinfo](https://github.com/stefanprodan/k8s-podinfo) chart. This chart packages a web app made with Go, it's configuration, a horizontal pod autoscaler \(HPA\) and the canary configuration file.
|
||||
|
||||
```text
|
||||
├── Chart.yaml
|
||||
├── README.md
|
||||
├── templates
|
||||
│ ├── NOTES.txt
|
||||
│ ├── _helpers.tpl
|
||||
│ ├── canary.yaml
|
||||
│ ├── configmap.yaml
|
||||
│ ├── deployment.yaml
|
||||
│ ├── hpa.yaml
|
||||
│ ├── service.yaml
|
||||
│ └── tests
|
||||
│ ├── test-config.yaml
|
||||
│ └── test-pod.yaml
|
||||
└── values.yaml
|
||||
```
|
||||
|
||||
You can find the chart source [here](https://github.com/stefanprodan/flagger/tree/master/charts/podinfo).
|
||||
|
||||
## Install
|
||||
|
||||
Create a test namespace with Istio sidecar injection enabled:
|
||||
|
||||
```bash
|
||||
export REPO=https://raw.githubusercontent.com/fluxcd/flagger/main
|
||||
|
||||
kubectl apply -f ${REPO}/artifacts/namespaces/test.yaml
|
||||
```
|
||||
|
||||
Add Flagger Helm repository:
|
||||
|
||||
```bash
|
||||
helm repo add flagger https://flagger.app
|
||||
```
|
||||
|
||||
Install podinfo with the release name `frontend` \(replace `example.com` with your own domain\):
|
||||
|
||||
```bash
|
||||
helm upgrade -i frontend flagger/podinfo \
|
||||
--namespace test \
|
||||
--set nameOverride=frontend \
|
||||
--set backend=http://backend.test:9898/echo \
|
||||
--set canary.enabled=true \
|
||||
--set canary.istioIngress.enabled=true \
|
||||
--set canary.istioIngress.gateway=istio-system/public-gateway \
|
||||
--set canary.istioIngress.host=frontend.istio.example.com
|
||||
```
|
||||
|
||||
Flagger takes a Kubernetes deployment and a horizontal pod autoscaler \(HPA\), then creates a series of objects \(Kubernetes deployments, ClusterIP services and Istio virtual services\). These objects expose the application on the mesh and drive the canary analysis and promotion.
|
||||
|
||||
```bash
|
||||
# generated by Helm
|
||||
configmap/frontend
|
||||
deployment.apps/frontend
|
||||
horizontalpodautoscaler.autoscaling/frontend
|
||||
canary.flagger.app/frontend
|
||||
|
||||
# generated by Flagger
|
||||
configmap/frontend-primary
|
||||
deployment.apps/frontend-primary
|
||||
horizontalpodautoscaler.autoscaling/frontend-primary
|
||||
service/frontend
|
||||
service/frontend-canary
|
||||
service/frontend-primary
|
||||
virtualservice.networking.istio.io/frontend
|
||||
```
|
||||
|
||||
When the `frontend-primary` deployment comes online, Flagger will route all traffic to the primary pods and scale to zero the `frontend` deployment.
|
||||
|
||||
Open your browser and navigate to the frontend URL:
|
||||
|
||||

|
||||
|
||||
Now let's install the `backend` release without exposing it outside the mesh:
|
||||
|
||||
```bash
|
||||
helm upgrade -i backend flagger/podinfo \
|
||||
--namespace test \
|
||||
--set nameOverride=backend \
|
||||
--set canary.enabled=true \
|
||||
--set canary.istioIngress.enabled=false
|
||||
```
|
||||
|
||||
Check if Flagger has successfully deployed the canaries:
|
||||
|
||||
```text
|
||||
kubectl -n test get canaries
|
||||
|
||||
NAME STATUS WEIGHT LASTTRANSITIONTIME
|
||||
backend Initialized 0 2019-02-12T18:53:18Z
|
||||
frontend Initialized 0 2019-02-12T17:50:50Z
|
||||
```
|
||||
|
||||
Click on the ping button in the `frontend` UI to trigger a HTTP POST request that will reach the `backend` app:
|
||||
|
||||

|
||||
|
||||
We'll use the `/echo` endpoint \(same as the one the ping button calls\) to generate load on both apps during a canary deployment.
|
||||
|
||||
## Upgrade
|
||||
|
||||
First let's install a load testing service that will generate traffic during analysis:
|
||||
|
||||
```bash
|
||||
helm upgrade -i flagger-loadtester flagger/loadtester \
|
||||
--namespace=test
|
||||
```
|
||||
|
||||
Install Flagger's helm test runner in the `kube-system` using `tiller` service account:
|
||||
|
||||
```bash
|
||||
helm upgrade -i flagger-helmtester flagger/loadtester \
|
||||
--namespace=kube-system \
|
||||
--set serviceAccountName=tiller
|
||||
```
|
||||
|
||||
Enable the load and helm tester and deploy a new `frontend` version:
|
||||
|
||||
```bash
|
||||
helm upgrade -i frontend flagger/podinfo/ \
|
||||
--namespace test \
|
||||
--reuse-values \
|
||||
--set canary.loadtest.enabled=true \
|
||||
--set canary.helmtest.enabled=true \
|
||||
--set image.tag=3.1.1
|
||||
```
|
||||
|
||||
Flagger detects that the deployment revision changed and starts the canary analysis:
|
||||
|
||||
```text
|
||||
kubectl -n istio-system logs deployment/flagger -f | jq .msg
|
||||
|
||||
New revision detected! Scaling up frontend.test
|
||||
Halt advancement frontend.test waiting for rollout to finish: 0 of 2 updated replicas are available
|
||||
Starting canary analysis for frontend.test
|
||||
Pre-rollout check helm test passed
|
||||
Advance frontend.test canary weight 5
|
||||
Advance frontend.test canary weight 10
|
||||
Advance frontend.test canary weight 15
|
||||
Advance frontend.test canary weight 20
|
||||
Advance frontend.test canary weight 25
|
||||
Advance frontend.test canary weight 30
|
||||
Advance frontend.test canary weight 35
|
||||
Advance frontend.test canary weight 40
|
||||
Advance frontend.test canary weight 45
|
||||
Advance frontend.test canary weight 50
|
||||
Copying frontend.test template spec to frontend-primary.test
|
||||
Halt advancement frontend-primary.test waiting for rollout to finish: 1 old replicas are pending termination
|
||||
Promotion completed! Scaling down frontend.test
|
||||
```
|
||||
|
||||
You can monitor the canary deployment with Grafana. Open the Flagger dashboard, select `test` from the namespace dropdown, `frontend-primary` from the primary dropdown and `frontend` from the canary dropdown.
|
||||
|
||||

|
||||
|
||||
Now trigger a canary deployment for the `backend` app, but this time you'll change a value in the configmap:
|
||||
|
||||
```bash
|
||||
helm upgrade -i backend flagger/podinfo/ \
|
||||
--namespace test \
|
||||
--reuse-values \
|
||||
--set canary.loadtest.enabled=true \
|
||||
--set canary.helmtest.enabled=true \
|
||||
--set httpServer.timeout=25s
|
||||
```
|
||||
|
||||
Generate HTTP 500 errors:
|
||||
|
||||
```bash
|
||||
kubectl -n test exec -it flagger-loadtester-xxx-yyy sh
|
||||
|
||||
watch curl http://backend-canary:9898/status/500
|
||||
```
|
||||
|
||||
Generate latency:
|
||||
|
||||
```bash
|
||||
kubectl -n test exec -it flagger-loadtester-xxx-yyy sh
|
||||
|
||||
watch curl http://backend-canary:9898/delay/1
|
||||
```
|
||||
|
||||
Flagger detects the config map change and starts a canary analysis. Flagger will pause the advancement when the HTTP success rate drops under 99% or when the average request duration in the last minute is over 500ms:
|
||||
|
||||
```text
|
||||
kubectl -n test describe canary backend
|
||||
|
||||
Events:
|
||||
|
||||
ConfigMap backend has changed
|
||||
New revision detected! Scaling up backend.test
|
||||
Starting canary analysis for backend.test
|
||||
Advance backend.test canary weight 5
|
||||
Advance backend.test canary weight 10
|
||||
Advance backend.test canary weight 15
|
||||
Advance backend.test canary weight 20
|
||||
Advance backend.test canary weight 25
|
||||
Advance backend.test canary weight 30
|
||||
Advance backend.test canary weight 35
|
||||
Halt backend.test advancement success rate 62.50% < 99%
|
||||
Halt backend.test advancement success rate 88.24% < 99%
|
||||
Advance backend.test canary weight 40
|
||||
Advance backend.test canary weight 45
|
||||
Halt backend.test advancement request duration 2.415s > 500ms
|
||||
Halt backend.test advancement request duration 2.42s > 500ms
|
||||
Advance backend.test canary weight 50
|
||||
ConfigMap backend-primary synced
|
||||
Copying backend.test template spec to backend-primary.test
|
||||
Promotion completed! Scaling down backend.test
|
||||
```
|
||||
|
||||

|
||||
|
||||
If the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary, the canary is scaled to zero and the rollout is marked as failed.
|
||||
|
||||
```bash
|
||||
kubectl -n test get canary
|
||||
|
||||
NAME STATUS WEIGHT LASTTRANSITIONTIME
|
||||
backend Succeeded 0 2019-02-12T19:33:11Z
|
||||
frontend Failed 0 2019-02-12T19:47:20Z
|
||||
```
|
||||
|
||||
If you've enabled the Slack notifications, you'll receive an alert with the reason why the `backend` promotion failed.
|
||||
|
||||
## GitOps automation
|
||||
|
||||
Instead of using Helm CLI from a CI tool to perform the install and upgrade, you could use a Git based approach. GitOps is a way to do Continuous Delivery, it works by using Git as a source of truth for declarative infrastructure and workloads. In the [GitOps model](https://www.weave.works/technologies/gitops/), any change to production must be committed in source control prior to being applied on the cluster. This way rollback and audit logs are provided by Git.
|
||||
|
||||

|
||||
|
||||
In order to apply the GitOps pipeline model to Flagger canary deployments you'll need a Git repository with your workloads definitions in YAML format, a container registry where your CI system pushes immutable images and an operator that synchronizes the Git repo with the cluster state.
|
||||
|
||||
Create a git repository with the following content:
|
||||
|
||||
```text
|
||||
├── namespaces
|
||||
│ └── test.yaml
|
||||
└── releases
|
||||
└── test
|
||||
├── backend.yaml
|
||||
├── frontend.yaml
|
||||
├── loadtester.yaml
|
||||
└── helmtester.yaml
|
||||
```
|
||||
|
||||
Define the `frontend` release using Flux `HelmRelease` custom resource:
|
||||
|
||||
```yaml
|
||||
apiVersion: flux.weave.works/v1beta1
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: frontend
|
||||
namespace: test
|
||||
annotations:
|
||||
fluxcd.io/automated: "true"
|
||||
filter.fluxcd.io/chart-image: semver:~3.1
|
||||
spec:
|
||||
releaseName: frontend
|
||||
chart:
|
||||
git: https://github.com/weaveowrks/flagger
|
||||
ref: master
|
||||
path: charts/podinfo
|
||||
values:
|
||||
image:
|
||||
repository: stefanprodan/podinfo
|
||||
tag: 3.1.0
|
||||
backend: http://backend-podinfo:9898/echo
|
||||
canary:
|
||||
enabled: true
|
||||
istioIngress:
|
||||
enabled: true
|
||||
gateway: istio-system/public-gateway
|
||||
host: frontend.istio.example.com
|
||||
loadtest:
|
||||
enabled: true
|
||||
helmtest:
|
||||
enabled: true
|
||||
```
|
||||
|
||||
In the `chart` section I've defined the release source by specifying the Helm repository \(hosted on GitHub Pages\), chart name and version. In the `values` section I've overwritten the defaults set in values.yaml.
|
||||
|
||||
With the `fluxcd.io` annotations I instruct Flux to automate this release. When an image tag in the sem ver range of `3.1.0 - 3.1.99` is pushed to Docker Hub, Flux will upgrade the Helm release and from there Flagger will pick up the change and start a canary deployment.
|
||||
|
||||
Install [Flux](https://github.com/fluxcd/flux) and its [Helm Operator](https://github.com/fluxcd/helm-operator) by specifying your Git repo URL:
|
||||
|
||||
```bash
|
||||
helm repo add fluxcd https://charts.fluxcd.io
|
||||
|
||||
helm install --name flux \
|
||||
--set git.url=git@github.com:<USERNAME>/<REPOSITORY> \
|
||||
--namespace fluxcd \
|
||||
fluxcd/flux
|
||||
|
||||
helm upgrade -i helm-operator fluxcd/helm-operator \
|
||||
--namespace fluxcd \
|
||||
--set git.ssh.secretName=flux-git-deploy
|
||||
```
|
||||
|
||||
At startup Flux generates a SSH key and logs the public key. Find the SSH public key with:
|
||||
|
||||
```bash
|
||||
kubectl -n fluxcd logs deployment/flux | grep identity.pub | cut -d '"' -f2
|
||||
```
|
||||
|
||||
In order to sync your cluster state with Git you need to copy the public key and create a deploy key with write access on your GitHub repository.
|
||||
|
||||
Open GitHub, navigate to your fork, go to _Setting > Deploy keys_ click on _Add deploy key_, check _Allow write access_, paste the Flux public key and click _Add key_.
|
||||
|
||||
After a couple of seconds Flux will apply the Kubernetes resources from Git and Flagger will launch the `frontend` and `backend` apps.
|
||||
|
||||
A CI/CD pipeline for the `frontend` release could look like this:
|
||||
|
||||
* cut a release from the master branch of the podinfo code repo with the git tag `3.1.1`
|
||||
* CI builds the image and pushes the `podinfo:6.0.1` image to the container registry
|
||||
* Flux scans the registry and updates the Helm release `image.tag` to `3.1.1`
|
||||
* Flux commits and push the change to the cluster repo
|
||||
* Flux applies the updated Helm release on the cluster
|
||||
* Flux Helm Operator picks up the change and calls Tiller to upgrade the release
|
||||
* Flagger detects a revision change and scales up the `frontend` deployment
|
||||
* Flagger runs the helm test before routing traffic to the canary service
|
||||
* Flagger starts the load test and runs the canary analysis
|
||||
* Based on the analysis result the canary deployment is promoted to production or rolled back
|
||||
* Flagger sends a Slack or MS Teams notification with the canary result
|
||||
|
||||
If the canary fails, fix the bug, do another patch release eg `3.1.2` and the whole process will run again.
|
||||
|
||||
A canary deployment can fail due to any of the following reasons:
|
||||
|
||||
* the container image can't be downloaded
|
||||
* the deployment replica set is stuck for more then ten minutes \(eg. due to a container crash loop\)
|
||||
* the webhooks \(acceptance tests, helm tests, load tests, etc\) are returning a non 2xx response
|
||||
* the HTTP success rate \(non 5xx responses\) metric drops under the threshold
|
||||
* the HTTP average duration metric goes over the threshold
|
||||
* the Istio telemetry service is unable to collect traffic metrics
|
||||
* the metrics server \(Prometheus\) can't be reached
|
||||
|
||||
If you want to find out more about managing Helm releases with Flux here are two in-depth guides: [flux2-kustomize-helm-example](https://github.com/fluxcd/flux2-kustomize-helm-example) and [gitops-istio](https://github.com/stefanprodan/gitops-istio).
|
||||
|
||||
@@ -2,17 +2,19 @@
|
||||
|
||||
This guide shows you how to use [Gateway API](https://gateway-api.sigs.k8s.io/) and Flagger to automate canary deployments and A/B testing.
|
||||
|
||||

|
||||

|
||||
|
||||
## Prerequisites
|
||||
|
||||
Flagger requires a Kubernetes cluster **v1.19** or newer and any mesh/ingress that implements the `v1beta1` or the `v1` version of Gateway API.
|
||||
Flagger requires an ingress controller or service mesh that implements the Gateway API **HTTPRoute** (`v1` or `v1beta1`).
|
||||
|
||||
We'll be using Istio for the sake of this tutorial, but you can use any other implementation.
|
||||
|
||||
Install the Gateway API CRDs
|
||||
Install the Gateway API CRDs:
|
||||
|
||||
```bash
|
||||
kubectl apply -k "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v1.0.0"
|
||||
# Suggestion: Change v1.4.0 in to the latest Gateway API version
|
||||
kubectl apply --server-side -k "github.com/kubernetes-sigs/gateway-api/config/crd?ref=v1.4.0"
|
||||
```
|
||||
|
||||
Install Istio:
|
||||
@@ -20,8 +22,8 @@ Install Istio:
|
||||
```bash
|
||||
istioctl install --set profile=minimal -y
|
||||
|
||||
# Suggestion: Please change release-1.20 in below command, to your real istio version.
|
||||
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.20/samples/addons/prometheus.yaml
|
||||
# Suggestion: Change release-1.27 in to the latest Istio version
|
||||
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.27/samples/addons/prometheus.yaml
|
||||
```
|
||||
|
||||
Install Flagger in the `flagger-system` namespace:
|
||||
@@ -37,9 +39,6 @@ helm upgrade -i flagger flagger/flagger \
|
||||
--set metricsServer=http://prometheus.istio-system:9090
|
||||
```
|
||||
|
||||
> Note: The above installation sets the mesh provider to be `gatewayapi:v1`. If your Gateway API implementation uses the `v1beta1` CRDs, then
|
||||
set the `--meshProvider` value to `gatewayapi:v1beta1`.
|
||||
|
||||
Create a namespace for the `Gateway`:
|
||||
|
||||
```bash
|
||||
@@ -49,7 +48,7 @@ kubectl create ns istio-ingress
|
||||
Create a `Gateway` that configures load balancing, traffic ACL, etc:
|
||||
|
||||
```yaml
|
||||
apiVersion: gateway.networking.k8s.io/v1beta1
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: gateway
|
||||
@@ -68,7 +67,9 @@ spec:
|
||||
|
||||
## Bootstrap
|
||||
|
||||
Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler \(HPA\), then creates a series of objects \(Kubernetes deployments, ClusterIP services, HTTPRoutes for the Gateway\). These objects expose the application inside the mesh and drive the canary analysis and promotion.
|
||||
Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler \(HPA\),
|
||||
then creates a series of objects \(Kubernetes deployments, ClusterIP services, HTTPRoutes for the Gateway\).
|
||||
These objects expose the application inside the mesh and drive the canary analysis and promotion.
|
||||
|
||||
Create a test namespace:
|
||||
|
||||
@@ -88,7 +89,9 @@ Deploy the load testing service to generate traffic during the canary analysis:
|
||||
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/tester?ref=main
|
||||
```
|
||||
|
||||
Create metric templates targeting the Prometheus server in the `flagger-system` namespace. The PromQL queries below are meant for `Envoy`, but you can [change it to your ingress/mesh provider](https://docs.flagger.app/faq#metrics) accordingly.
|
||||
Create metric templates targeting the Prometheus server in the `flagger-system` namespace.
|
||||
The PromQL queries below are meant for `Envoy`,
|
||||
but you can [change it to your ingress/mesh provider](https://docs.flagger.app/faq#metrics) accordingly.
|
||||
|
||||
```yaml
|
||||
apiVersion: flagger.app/v1beta1
|
||||
@@ -152,7 +155,7 @@ Save the above resource as metric-templates.yaml and then apply it:
|
||||
kubectl apply -f metric-templates.yaml
|
||||
```
|
||||
|
||||
Create a canary custom resource \(replace "www.example.com" with your own domain\):
|
||||
Create a Canary custom resource \(replace "www.example.com" with your own domain\):
|
||||
|
||||
```yaml
|
||||
apiVersion: flagger.app/v1beta1
|
||||
@@ -237,7 +240,8 @@ Save the above resource as podinfo-canary.yaml and then apply it:
|
||||
kubectl apply -f ./podinfo-canary.yaml
|
||||
```
|
||||
|
||||
When the canary analysis starts, Flagger will call the pre-rollout webhooks before routing traffic to the canary. The canary analysis will run for five minutes while validating the HTTP metrics and rollout hooks every minute.
|
||||
When the canary analysis starts, Flagger will call the pre-rollout webhooks before routing traffic to the canary.
|
||||
The canary analysis will run for five minutes while validating the HTTP metrics and rollout hooks every minute.
|
||||
|
||||
After a couple of seconds Flagger will create the canary objects:
|
||||
|
||||
@@ -266,22 +270,27 @@ export ADDRESS="$(kubectl -n istio-ingress get svc/gateway-istio -ojson \
|
||||
echo $ADDRESS
|
||||
```
|
||||
|
||||
Configure your DNS server with a CNAME record \(AWS\) or A record \(GKE/AKS/DOKS\) and point a domain e.g. `www.example.com` to the LB address.
|
||||
Configure your DNS server with a CNAME record \(AWS\) or A record \(GKE/AKS/DOKS\) and
|
||||
point a domain e.g. `www.example.com` to the LB address.
|
||||
|
||||
Now you can access the podinfo UI using your domain address.
|
||||
|
||||
Note that you should be using HTTPS when exposing production workloads on internet. You can obtain free TLS certs from Let's Encrypt, read this
|
||||
[guide](https://github.com/stefanprodan/istio-gke) on how to configure cert-manager to secure Istio with TLS certificates.
|
||||
Note that you should be using HTTPS when exposing production workloads on internet.
|
||||
If you're using a local cluster you can port forward to the Envoy LoadBalancer service:
|
||||
|
||||
If you're using a local cluster via kind/k3s you can port forward the Envoy LoadBalancer service:
|
||||
```bash
|
||||
kubectl port-forward -n istio-ingress svc/gateway-istio 8080:80
|
||||
```
|
||||
|
||||
Now you can access podinfo via `curl -H "Host: www.example.com" localhost:8080`
|
||||
Now you can access podinfo via `curl -H "Host: www.example.com" localhost:8080`.
|
||||
|
||||
## Automated canary promotion
|
||||
|
||||
With the application bootstrapped, Flagger will continuously monitor the deployment for changes.
|
||||
When a new revision is detected, Flagger will start a canary analysis and gradually shift traffic to the new version.
|
||||
|
||||

|
||||
|
||||
Trigger a canary deployment by updating the container image:
|
||||
|
||||
```bash
|
||||
@@ -319,7 +328,8 @@ Events:
|
||||
Normal Synced 5s flagger Promotion completed! Scaling down podinfo.test
|
||||
```
|
||||
|
||||
**Note** that if you apply new changes to the deployment during the canary analysis, Flagger will restart the analysis.
|
||||
**Note** that if you apply new changes to the deployment during the canary analysis,
|
||||
Flagger will restart the analysis.
|
||||
|
||||
A canary deployment is triggered by changes in any of the following objects:
|
||||
|
||||
@@ -327,7 +337,8 @@ A canary deployment is triggered by changes in any of the following objects:
|
||||
* ConfigMaps mounted as volumes or mapped to environment variables
|
||||
* Secrets mounted as volumes or mapped to environment variables
|
||||
|
||||
You can monitor how Flagger progressively changes the weights of the HTTPRoute object that is attahed to the Gateway with:
|
||||
You can monitor how Flagger progressively changes the weights of
|
||||
the HTTPRoute object that is attached to the Gateway with:
|
||||
|
||||
```bash
|
||||
watch kubectl get httproute -n test podinfo -o=jsonpath='{.spec.rules}'
|
||||
@@ -339,9 +350,9 @@ You can monitor all canaries with:
|
||||
watch kubectl get canaries --all-namespaces
|
||||
|
||||
NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME
|
||||
test podinfo Progressing 15 2022-01-16T14:05:07Z
|
||||
prod frontend Succeeded 0 2022-01-15T16:15:07Z
|
||||
prod backend Failed 0 2022-01-14T17:05:07Z
|
||||
test podinfo Progressing 15 2025-10-16T14:05:07Z
|
||||
prod frontend Succeeded 0 2025-10-15T16:15:07Z
|
||||
prod backend Failed 0 2025-10-14T17:05:07Z
|
||||
```
|
||||
|
||||
## Automated rollback
|
||||
@@ -373,7 +384,8 @@ Generate latency:
|
||||
watch curl http://podinfo-canary:9898/delay/1
|
||||
```
|
||||
|
||||
When the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary, the canary is scaled to zero and the rollout is marked as failed.
|
||||
When the number of failed checks reaches the canary analysis threshold,
|
||||
the traffic is routed back to the primary, the canary is scaled to zero and the rollout is marked as failed.
|
||||
|
||||
```text
|
||||
kubectl -n test describe canary/podinfo
|
||||
@@ -398,13 +410,142 @@ Events:
|
||||
Warning Synced 1m flagger Canary failed! Scaling down podinfo.test
|
||||
```
|
||||
|
||||
## A/B Testing
|
||||
|
||||
Besides weighted routing, Flagger can be configured to route traffic to the canary based on HTTP match conditions.
|
||||
In an A/B testing scenario, you'll be using HTTP headers and cookies to target a certain segment of your users.
|
||||
|
||||

|
||||
|
||||
Create a canary custom resource \(replace "www.example.com" with your own domain\):
|
||||
|
||||
```yaml
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: test
|
||||
spec:
|
||||
# deployment reference
|
||||
targetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: podinfo
|
||||
# the maximum time in seconds for the canary deployment
|
||||
# to make progress before it is rollback (default 600s)
|
||||
progressDeadlineSeconds: 60
|
||||
# HPA reference (optional)
|
||||
autoscalerRef:
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
name: podinfo
|
||||
service:
|
||||
# service port number
|
||||
port: 9898
|
||||
# container port number or name (optional)
|
||||
targetPort: 9898
|
||||
# Gateway API HTTPRoute host names
|
||||
hosts:
|
||||
- www.example.com
|
||||
# Reference to the Gateway that the generated HTTPRoute would attach to.
|
||||
gatewayRefs:
|
||||
- name: gateway
|
||||
namespace: istio-ingress
|
||||
analysis:
|
||||
# schedule interval (default 60s)
|
||||
interval: 1m
|
||||
# total number of iterations
|
||||
iterations: 10
|
||||
# max number of failed iterations before rollback
|
||||
threshold: 2
|
||||
# canary match condition
|
||||
match:
|
||||
- headers:
|
||||
user-agent:
|
||||
regex: ".*Firefox.*"
|
||||
- headers:
|
||||
cookie:
|
||||
regex: "^(.*?;)?(type=insider)(;.*)?$"
|
||||
metrics:
|
||||
- name: error-rate
|
||||
# max error rate (5xx responses)
|
||||
# percentage (0-100)
|
||||
templateRef:
|
||||
name: error-rate
|
||||
namespace: flagger-system
|
||||
thresholdRange:
|
||||
max: 1
|
||||
interval: 1m
|
||||
- name: latency
|
||||
templateRef:
|
||||
name: latency
|
||||
namespace: flagger-system
|
||||
# seconds
|
||||
thresholdRange:
|
||||
max: 0.5
|
||||
interval: 30s
|
||||
# testing (optional)
|
||||
webhooks:
|
||||
- name: load-test
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
metadata:
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host www.example.com -H 'Cookie: type=insider' http://gateway-istio.istio-ingress/"
|
||||
```
|
||||
|
||||
The above configuration will run an analysis for ten minutes targeting those users that
|
||||
have an insider cookie or are using Firefox as a browser.
|
||||
|
||||
Save the above resource as podinfo-ab-canary.yaml and then apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f ./podinfo-ab-canary.yaml
|
||||
```
|
||||
|
||||
Trigger a canary deployment by updating the container image:
|
||||
|
||||
```bash
|
||||
kubectl -n test set image deployment/podinfo \
|
||||
podinfod=stefanprodan/podinfo:6.0.3
|
||||
```
|
||||
|
||||
Flagger detects that the deployment revision changed and starts a new rollout:
|
||||
|
||||
```text
|
||||
kubectl -n test describe canary/podinfo
|
||||
|
||||
Status:
|
||||
Failed Checks: 0
|
||||
Phase: Succeeded
|
||||
Events:
|
||||
Type Reason Age From Message
|
||||
---- ------ ---- ---- -------
|
||||
Normal Synced 3m flagger New revision detected podinfo.test
|
||||
Normal Synced 3m flagger Scaling up podinfo.test
|
||||
Warning Synced 3m flagger Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available
|
||||
Normal Synced 3m flagger Advance podinfo.test canary iteration 1/10
|
||||
Normal Synced 3m flagger Advance podinfo.test canary iteration 2/10
|
||||
Normal Synced 3m flagger Advance podinfo.test canary iteration 3/10
|
||||
Normal Synced 2m flagger Advance podinfo.test canary iteration 4/10
|
||||
Normal Synced 2m flagger Advance podinfo.test canary iteration 5/10
|
||||
Normal Synced 1m flagger Advance podinfo.test canary iteration 6/10
|
||||
Normal Synced 1m flagger Advance podinfo.test canary iteration 7/10
|
||||
Normal Synced 55s flagger Advance podinfo.test canary iteration 8/10
|
||||
Normal Synced 45s flagger Advance podinfo.test canary iteration 9/10
|
||||
Normal Synced 35s flagger Advance podinfo.test canary iteration 10/10
|
||||
Normal Synced 25s flagger Copying podinfo.test template spec to podinfo-primary.test
|
||||
Warning Synced 15s flagger Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available
|
||||
Normal Synced 5s flagger Promotion completed! Scaling down podinfo.test
|
||||
```
|
||||
|
||||
## Session Affinity
|
||||
|
||||
While Flagger can perform weighted routing and A/B testing individually, with Gateway API it can combine the two leading to a Canary
|
||||
release with session affinity.
|
||||
While Flagger can perform weighted routing and A/B testing individually,
|
||||
with Gateway API it can combine the two leading to a Canary release with session affinity.
|
||||
For more information you can read the [deployment strategies docs](../usage/deployment-strategies.md#canary-release-with-session-affinity).
|
||||
|
||||
> **Note:** The implementation must have support for the [`ResponseHeaderModifier`](https://github.com/kubernetes-sigs/gateway-api/blob/3d22aa5a08413222cb79e6b2e245870360434614/apis/v1beta1/httproute_types.go#L651) API.
|
||||
> **Note:** Session Affinity requires a Gateway API implementation that supports
|
||||
> the [`ResponseHeaderModifier`](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/) API.
|
||||
|
||||
Create a canary custom resource \(replace www.example.com with your own domain\):
|
||||
|
||||
@@ -478,13 +619,6 @@ spec:
|
||||
interval: 30s
|
||||
# testing (optional)
|
||||
webhooks:
|
||||
- name: smoke-test
|
||||
type: pre-rollout
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 15s
|
||||
metadata:
|
||||
type: bash
|
||||
cmd: "curl -sd 'anon' http://podinfo-canary.test:9898/token | grep token"
|
||||
- name: load-test
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
@@ -509,145 +643,17 @@ You can load `www.example.com` in your browser and refresh it until you see the
|
||||
All subsequent requests after that will be served by `podinfo:6.0.1` and not `podinfo:6.0.0` because of the session affinity
|
||||
configured by Flagger in the HTTPRoute object.
|
||||
|
||||
# A/B Testing
|
||||
|
||||
Besides weighted routing, Flagger can be configured to route traffic to the canary based on HTTP match conditions. In an A/B testing scenario, you'll be using HTTP headers or cookies to target a certain segment of your users. This is particularly useful for frontend applications that require session affinity.
|
||||
|
||||

|
||||
|
||||
Create a canary custom resource \(replace "www.example.com" with your own domain\):
|
||||
|
||||
```yaml
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: test
|
||||
spec:
|
||||
# deployment reference
|
||||
targetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: podinfo
|
||||
# the maximum time in seconds for the canary deployment
|
||||
# to make progress before it is rollback (default 600s)
|
||||
progressDeadlineSeconds: 60
|
||||
# HPA reference (optional)
|
||||
autoscalerRef:
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
name: podinfo
|
||||
service:
|
||||
# service port number
|
||||
port: 9898
|
||||
# container port number or name (optional)
|
||||
targetPort: 9898
|
||||
# Gateway API HTTPRoute host names
|
||||
hosts:
|
||||
- www.example.com
|
||||
# Reference to the Gateway that the generated HTTPRoute would attach to.
|
||||
gatewayRefs:
|
||||
- name: gateway
|
||||
namespace: istio-ingress
|
||||
analysis:
|
||||
# schedule interval (default 60s)
|
||||
interval: 1m
|
||||
# max number of failed metric checks before rollback
|
||||
threshold: 5
|
||||
# max traffic percentage routed to canary
|
||||
# percentage (0-100)
|
||||
maxWeight: 50
|
||||
# canary increment step
|
||||
# percentage (0-100)
|
||||
stepWeight: 10
|
||||
metrics:
|
||||
- name: error-rate
|
||||
# max error rate (5xx responses)
|
||||
# percentage (0-100)
|
||||
templateRef:
|
||||
name: error-rate
|
||||
namespace: flagger-system
|
||||
thresholdRange:
|
||||
max: 1
|
||||
interval: 1m
|
||||
- name: latency
|
||||
templateRef:
|
||||
name: latency
|
||||
namespace: flagger-system
|
||||
# seconds
|
||||
thresholdRange:
|
||||
max: 0.5
|
||||
interval: 30s
|
||||
# testing (optional)
|
||||
webhooks:
|
||||
- name: smoke-test
|
||||
type: pre-rollout
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 15s
|
||||
metadata:
|
||||
type: bash
|
||||
cmd: "curl -sd 'anon' http://podinfo-canary.test:9898/token | grep token"
|
||||
- name: load-test
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
metadata:
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host www.example.com -H 'X-Canary: insider' http://gateway-istio.istio-ingress/"
|
||||
```
|
||||
|
||||
The above configuration will run an analysis for ten minutes targeting those users that have an insider cookie.
|
||||
|
||||
Save the above resource as podinfo-ab-canary.yaml and then apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f ./podinfo-ab-canary.yaml
|
||||
```
|
||||
|
||||
Trigger a canary deployment by updating the container image:
|
||||
|
||||
```bash
|
||||
kubectl -n test set image deployment/podinfo \
|
||||
podinfod=stefanprodan/podinfo:6.0.3
|
||||
```
|
||||
|
||||
Flagger detects that the deployment revision changed and starts a new rollout:
|
||||
|
||||
```text
|
||||
kubectl -n test describe canary/podinfo
|
||||
|
||||
Status:
|
||||
Failed Checks: 0
|
||||
Phase: Succeeded
|
||||
Events:
|
||||
Type Reason Age From Message
|
||||
---- ------ ---- ---- -------
|
||||
Normal Synced 3m flagger New revision detected podinfo.test
|
||||
Normal Synced 3m flagger Scaling up podinfo.test
|
||||
Warning Synced 3m flagger Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available
|
||||
Normal Synced 3m flagger Advance podinfo.test canary iteration 1/10
|
||||
Normal Synced 3m flagger Advance podinfo.test canary iteration 2/10
|
||||
Normal Synced 3m flagger Advance podinfo.test canary iteration 3/10
|
||||
Normal Synced 2m flagger Advance podinfo.test canary iteration 4/10
|
||||
Normal Synced 2m flagger Advance podinfo.test canary iteration 5/10
|
||||
Normal Synced 1m flagger Advance podinfo.test canary iteration 6/10
|
||||
Normal Synced 1m flagger Advance podinfo.test canary iteration 7/10
|
||||
Normal Synced 55s flagger Advance podinfo.test canary iteration 8/10
|
||||
Normal Synced 45s flagger Advance podinfo.test canary iteration 9/10
|
||||
Normal Synced 35s flagger Advance podinfo.test canary iteration 10/10
|
||||
Normal Synced 25s flagger Copying podinfo.test template spec to podinfo-primary.test
|
||||
Warning Synced 15s flagger Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available
|
||||
Normal Synced 5s flagger Promotion completed! Scaling down podinfo.test
|
||||
```
|
||||
To configure stickiness for the Primary deployment to ensure fair weighted traffic routing, please
|
||||
checkout the [deployment strategies docs](../usage/deployment-strategies.md#canary-release-with-session-affinity).
|
||||
|
||||
## Traffic mirroring
|
||||
|
||||

|
||||
|
||||
For applications that perform read operations, Flagger can be configured to do B/G tests with traffic mirroring.
|
||||
Gateway API traffic mirroring will copy each incoming request, sending one request to the primary and one to the canary service.
|
||||
The response from the primary is sent back to the user and the response from the canary is discarded.
|
||||
Metrics are collected on both requests so that the deployment will only proceed if the canary metrics are within the threshold values.
|
||||
|
||||
Note that mirroring should be used for requests that are **idempotent** or capable of being processed twice \(once by the primary and once by the canary\).
|
||||
> **Note:** Traffic mirroring requires a Gateway API implementation that supports
|
||||
> the [`RequestMirror`](https://gateway-api.sigs.k8s.io/guides/http-request-mirroring/) filter.
|
||||
|
||||
You can enable mirroring by replacing `stepWeight` with `iterations` and by setting `analysis.mirror` to `true`:
|
||||
|
||||
@@ -702,26 +708,139 @@ spec:
|
||||
cmd: "hey -z 2m -q 10 -c 2 -host www.example.com http://gateway-istio.istio-ingress/"
|
||||
```
|
||||
|
||||
With the above configuration, Flagger will run a canary release with the following steps:
|
||||
Gateway API traffic mirroring will copy each incoming request, sending one request to the primary and one to the canary service.
|
||||
The response from the primary is sent back to the user and the response from the canary is discarded.
|
||||
|
||||
* detect new revision \(deployment spec, secrets or configmaps changes\)
|
||||
* scale from zero the canary deployment
|
||||
* wait for the HPA to set the canary minimum replicas
|
||||
* check canary pods health
|
||||
* run the acceptance tests
|
||||
* abort the canary release if tests fail
|
||||
* start the load tests
|
||||
* mirror 100% of the traffic from primary to canary
|
||||
* check request success rate and request duration every minute
|
||||
* abort the canary release if the metrics check failure threshold is reached
|
||||
* stop traffic mirroring after the number of iterations is reached
|
||||
* route live traffic to the canary pods
|
||||
* promote the canary \(update the primary secrets, configmaps and deployment spec\)
|
||||
* wait for the primary deployment rollout to finish
|
||||
* wait for the HPA to set the primary minimum replicas
|
||||
* check primary pods health
|
||||
* switch live traffic back to primary
|
||||
* scale to zero the canary
|
||||
* send notification with the canary analysis result
|
||||
Metrics are collected on both requests so that the deployment will only proceed if the canary metrics are within the threshold values.
|
||||
|
||||
The above procedures can be extended with [custom metrics](../usage/metrics.md) checks, [webhooks](../usage/webhooks.md), [manual promotion](../usage/webhooks.md#manual-gating) approval and [Slack or MS Teams](../usage/alerting.md) notifications.
|
||||
|
||||
## Customising the HTTPRoute
|
||||
|
||||
Besides the `hosts` and `gatewayRefs` fields, you can customize the generated HTTPRoute with various options
|
||||
exposed under the `spec.service` field of the Canary.
|
||||
|
||||
### Header Manipulation
|
||||
|
||||
You can configure request and response header manipulation using the `spec.service.headers` field of the Canary.
|
||||
|
||||
> **Note:** Header manipulation requires a Gateway API implementation that supports
|
||||
> the [`RequestHeaderModifier`](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/) and [`ResponseHeaderModifier`](https://gateway-api.sigs.k8s.io/guides/http-header-modifier/) filters.
|
||||
|
||||
Example configuration:
|
||||
|
||||
```yaml
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: test
|
||||
spec:
|
||||
service:
|
||||
headers:
|
||||
request:
|
||||
add:
|
||||
x-custom-header: "custom-value"
|
||||
set:
|
||||
x-api-version: "v1"
|
||||
remove:
|
||||
- x-debug-header
|
||||
response:
|
||||
add:
|
||||
x-frame-options: "DENY"
|
||||
x-content-type-options: "nosniff"
|
||||
set:
|
||||
cache-control: "no-cache"
|
||||
remove:
|
||||
- x-powered-by
|
||||
```
|
||||
|
||||
### URL Rewriting
|
||||
|
||||
You can configure URL rewriting using the `spec.service.rewrite` field of the Canary to modify the path or hostname of requests.
|
||||
|
||||
> **Note:** URL rewriting requires a Gateway API implementation that supports
|
||||
> the [`URLRewrite`](https://gateway-api.sigs.k8s.io/guides/http-redirect-rewrite/?h=urlrewrite#rewrites) filter.
|
||||
|
||||
Example configuration:
|
||||
|
||||
```yaml
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: test
|
||||
spec:
|
||||
service:
|
||||
rewrite:
|
||||
# Rewrite the URI path
|
||||
uri: "/v2/api"
|
||||
# Optionally specify the rewrite type: "ReplaceFullPath" or "ReplacePrefixMatch"
|
||||
# Defaults to "ReplaceFullPath" if not specified
|
||||
type: "ReplaceFullPath"
|
||||
# Rewrite the hostname/authority header
|
||||
authority: "api.example.com"
|
||||
```
|
||||
|
||||
The `type` field determines how the URI rewriting is performed:
|
||||
|
||||
- **ReplaceFullPath**: Replaces the entire request path with the specified `uri` value
|
||||
- **ReplacePrefixMatch**: Replaces only the prefix portion of the path that was matched
|
||||
|
||||
Example with prefix replacement:
|
||||
|
||||
```yaml
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: test
|
||||
spec:
|
||||
service:
|
||||
rewrite:
|
||||
uri: "/api/v2"
|
||||
type: "ReplacePrefixMatch"
|
||||
```
|
||||
|
||||
When using `ReplacePrefixMatch`, if a request comes to `/old/path`, and the HTTPRoute matches the prefix `/old`,
|
||||
the request will be rewritten to `/api/v2/path`.
|
||||
|
||||
### CORS Policy
|
||||
|
||||
The cross-origin resource sharing policy can be configured the `spec.service.corsPolicy` field of the Canary.
|
||||
|
||||
> **Note:** Cross-origin resource sharing requires a Gateway API implementation that supports
|
||||
> the [`CORS`](https://gateway-api.sigs.k8s.io/geps/gep-1767/) filter.
|
||||
|
||||
Example configuration:
|
||||
|
||||
```yaml
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: test
|
||||
spec:
|
||||
service:
|
||||
corsPolicy:
|
||||
allowOrigin:
|
||||
- https://foo.example
|
||||
- http://foo.example
|
||||
allowMethods:
|
||||
- GET
|
||||
- PUT
|
||||
- POST
|
||||
- DELETE
|
||||
- PATCH
|
||||
- OPTIONS
|
||||
allowCredentials: true
|
||||
allowHeaders:
|
||||
- Keep-Alive
|
||||
- User-Agent
|
||||
- X-Requested-With
|
||||
- If-Modified-Since
|
||||
- Cache-Control
|
||||
- Content-Type
|
||||
- Authorization
|
||||
maxAge: 24h
|
||||
```
|
||||
|
||||
@@ -404,6 +404,9 @@ You can load `app.example.com` in your browser and refresh it until you see the
|
||||
All subsequent requests after that will be served by `podinfo:6.0.1` and not `podinfo:6.0.0` because of the session affinity
|
||||
configured by Flagger with Istio.
|
||||
|
||||
To configure stickiness for the Primary deployment to ensure fair weighted traffic routing, please
|
||||
checkout the [deployment strategies docs](../usage/deployment-strategies.md#canary-release-with-session-affinity).
|
||||
|
||||
## Traffic mirroring
|
||||
|
||||

|
||||
|
||||
@@ -20,6 +20,7 @@ Install Flagger:
|
||||
```bash
|
||||
helm repo add flagger https://flagger.app
|
||||
|
||||
kubectl create namespace flagger
|
||||
helm upgrade -i flagger flagger/flagger \
|
||||
--namespace flagger \
|
||||
--set prometheus.install=true \
|
||||
@@ -68,7 +69,7 @@ spec:
|
||||
- type: prometheus
|
||||
metadata:
|
||||
name: prom-trigger
|
||||
serverAddress: http://flagger-prometheus.flagger-system:9090
|
||||
serverAddress: http://flagger-prometheus.flagger:9090
|
||||
metricName: http_requests_total
|
||||
query: sum(rate(http_requests_total{ app="podinfo" }[30s]))
|
||||
threshold: '5'
|
||||
|
||||
249
docs/gitbook/tutorials/knative-progressive-delivery.md
Normal file
249
docs/gitbook/tutorials/knative-progressive-delivery.md
Normal file
@@ -0,0 +1,249 @@
|
||||
# Knative Canary Deployments
|
||||
|
||||
This guide shows you how to use [Knative](https://knative.dev/) and Flagger to automate canary deployments.
|
||||
|
||||

|
||||
|
||||
## Prerequisites
|
||||
|
||||
Flagger requires a Kubernetes cluster **v1.19** or newer and a Knative Serving installation that supports
|
||||
the resources with `serving.knative.dev/v1` as their API version.
|
||||
|
||||
Install Knative v1.17.0:
|
||||
|
||||
```bash
|
||||
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.17.0/serving-crds.yaml
|
||||
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.17.0/serving-core.yaml
|
||||
kubectl apply -f https://github.com/knative/net-kourier/releases/download/knative-v1.17.0/kourier.yaml
|
||||
kubectl patch configmap/config-network \
|
||||
--namespace knative-serving \
|
||||
--type merge \
|
||||
--patch '{"data":{"ingress-class":"kourier.ingress.networking.knative.dev"}}'
|
||||
```
|
||||
|
||||
|
||||
Install Flagger in the `flagger-system` namespace:
|
||||
|
||||
```bash
|
||||
kubectl apply -k github.com/fluxcd/flagger//kustomize/knative
|
||||
```
|
||||
|
||||
Create a namespace for your Kntive Service:
|
||||
|
||||
```bash
|
||||
kubectl create namespace test
|
||||
```
|
||||
|
||||
Create a Knative Service that deploys podinfo:
|
||||
|
||||
```yaml
|
||||
apiVersion: serving.knative.dev/v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: test
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- image: ghcr.io/stefanprodan/podinfo:6.0.0
|
||||
ports:
|
||||
- containerPort: 9898
|
||||
protocol: TCP
|
||||
command:
|
||||
- ./podinfo
|
||||
- --port=9898
|
||||
- --port-metrics=9797
|
||||
- --grpc-port=9999
|
||||
- --grpc-service-name=podinfo
|
||||
- --level=info
|
||||
- --random-delay=false
|
||||
- --random-error=false
|
||||
```
|
||||
|
||||
Deploy the load testing service to generate traffic during the canary analysis:
|
||||
|
||||
```bash
|
||||
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/tester?ref=main
|
||||
```
|
||||
|
||||
Create a Canary custom resource:
|
||||
|
||||
```yaml
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: test
|
||||
spec:
|
||||
provider: knative
|
||||
# knative service ref
|
||||
targetRef:
|
||||
apiVersion: serving.knative.dev/v1
|
||||
kind: Service
|
||||
name: podinfo
|
||||
# the maximum time in seconds for the canary deployment
|
||||
# to make progress before it is rollback (default 600s)
|
||||
progressDeadlineSeconds: 60
|
||||
analysis:
|
||||
# schedule interval (default 60s)
|
||||
interval: 15s
|
||||
# max number of failed metric checks before rollback
|
||||
threshold: 15
|
||||
# max traffic percentage routed to canary
|
||||
maxWeight: 50
|
||||
# canary increment step
|
||||
# percentage (0-100)
|
||||
stepWeight: 10
|
||||
metrics:
|
||||
- name: request-success-rate
|
||||
# min success rate (non-5xx responses)
|
||||
# percentage (0-100)
|
||||
thresholdRange:
|
||||
min: 99
|
||||
interval: 1m
|
||||
- name: request-duration
|
||||
# milliseconds
|
||||
thresholdRange:
|
||||
max: 500
|
||||
interval: 1m
|
||||
webhooks:
|
||||
- name: load-test
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
metadata:
|
||||
type: cmd
|
||||
cmd: "hey -z 1m -q 5 -c 2 http://podinfo.test"
|
||||
logCmdOutput: "true"
|
||||
```
|
||||
|
||||
> Note: Please note that for a Canary resource with `.spec.provider` set to `knative`, the resource is only valid if the
|
||||
`.spec.targetRef.kind` is `Service` and `.spec.targetRef.apiVersion` is `serving.knative.dev/v1`.
|
||||
|
||||
Save the above resource as podinfo-canary.yaml and then apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f ./podinfo-canary.yaml
|
||||
```
|
||||
|
||||
When the canary analysis starts, Flagger will call the pre-rollout webhooks before routing traffic to the canary.
|
||||
The canary analysis will run for five minutes while validating the HTTP metrics and rollout hooks every minute.
|
||||
|
||||
After a couple of seconds Flagger will make the following changes the Knative Service `podinfo`:
|
||||
|
||||
* Add an annotation to the object with the name `flagger.app/primary-revision`.
|
||||
* Modify the `.spec.traffic` section of the object such that it can manipulate the traffic spread between
|
||||
the primary and canary Knative Revision.
|
||||
|
||||
## Automated canary promotion
|
||||
|
||||
Trigger a canary deployment by updating the container image:
|
||||
|
||||
```bash
|
||||
kubectl -n test patch services.serving podinfo --type=json \
|
||||
-p '[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "ghcr.io/stefanprodan/podinfo:6.0.1"}]'
|
||||
```
|
||||
|
||||
Flagger detects that the deployment revision changed and starts a new rollout:
|
||||
|
||||
```text
|
||||
kubectl -n test describe canary/podinfo
|
||||
|
||||
Status:
|
||||
Canary Weight: 0
|
||||
Failed Checks: 0
|
||||
Phase: Succeeded
|
||||
Events:
|
||||
Type Reason Age From Message
|
||||
---- ------ ---- ---- -------
|
||||
Normal Synced 3m flagger New revision detected podinfo.test
|
||||
Normal Synced 3m flagger Scaling up podinfo.test
|
||||
Normal Synced 3m flagger Advance podinfo.test canary weight 5
|
||||
Normal Synced 3m flagger Advance podinfo.test canary weight 10
|
||||
Normal Synced 3m flagger Advance podinfo.test canary weight 15
|
||||
Normal Synced 2m flagger Advance podinfo.test canary weight 20
|
||||
Normal Synced 2m flagger Advance podinfo.test canary weight 25
|
||||
Normal Synced 1m flagger Advance podinfo.test canary weight 30
|
||||
Normal Synced 1m flagger Advance podinfo.test canary weight 35
|
||||
Normal Synced 55s flagger Advance podinfo.test canary weight 40
|
||||
Normal Synced 45s flagger Advance podinfo.test canary weight 45
|
||||
Normal Synced 35s flagger Advance podinfo.test canary weight 50
|
||||
Normal Synced 25s flagger Copying podinfo.test template spec to podinfo-primary.test
|
||||
Normal Synced 5s flagger Promotion completed! Scaling down podinfo.test
|
||||
```
|
||||
|
||||
A canary deployment is triggered everytime a new Knative Revision is created.
|
||||
|
||||
**Note** that if you apply new changes to the Knative Service during the canary analysis, Flagger will restart the analysis.
|
||||
|
||||
You can monitor how Flagger progressively changes the Knative Service object to spread traffic between Knative Revisions:
|
||||
|
||||
```bash
|
||||
watch kubectl get httproute -n test podinfo -o=jsonpath='{.spec.traffic}'
|
||||
```
|
||||
|
||||
You can monitor all canaries with:
|
||||
|
||||
```bash
|
||||
watch kubectl get canaries --all-namespaces
|
||||
|
||||
NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME
|
||||
test podinfo Progressing 15 2025-03-16T14:05:07Z
|
||||
prod frontend Succeeded 0 2025-03-16T16:15:07Z
|
||||
prod backend Failed 0 2025-03-16T17:05:07Z
|
||||
```
|
||||
|
||||
## Automated rollback
|
||||
|
||||
During the canary analysis you can generate HTTP 500 errors and high latency to test if Flagger pauses the rollout.
|
||||
|
||||
Trigger another canary deployment:
|
||||
|
||||
```bash
|
||||
kubectl -n test patch services.serving podinfo --type=json \
|
||||
-p '[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "ghcr.io/stefanprodan/podinfo:6.0.2"}]'
|
||||
```
|
||||
|
||||
Exec into the load tester pod with:
|
||||
|
||||
```bash
|
||||
kubectl -n test exec -it flagger-loadtester-xx-xx sh
|
||||
```
|
||||
|
||||
Generate HTTP 500 errors:
|
||||
|
||||
```bash
|
||||
watch curl http://podinfo-canary:9898/status/500
|
||||
```
|
||||
|
||||
Generate latency:
|
||||
|
||||
```bash
|
||||
watch curl http://podinfo-canary:9898/delay/1
|
||||
```
|
||||
|
||||
When the number of failed checks reaches the canary analysis threshold, the traffic is routed back to the primary
|
||||
Knative Revision and the rollout is marked as failed.
|
||||
|
||||
```text
|
||||
kubectl -n test describe canary/podinfo
|
||||
|
||||
Status:
|
||||
Canary Weight: 0
|
||||
Failed Checks: 10
|
||||
Phase: Failed
|
||||
Events:
|
||||
Type Reason Age From Message
|
||||
---- ------ ---- ---- -------
|
||||
Normal Synced 3m flagger Starting canary deployment for podinfo.test
|
||||
Normal Synced 3m flagger Advance podinfo.test canary weight 5
|
||||
Normal Synced 3m flagger Advance podinfo.test canary weight 10
|
||||
Normal Synced 3m flagger Advance podinfo.test canary weight 15
|
||||
Normal Synced 3m flagger Halt podinfo.test advancement error rate 69.17% > 1%
|
||||
Normal Synced 2m flagger Halt podinfo.test advancement error rate 61.39% > 1%
|
||||
Normal Synced 2m flagger Halt podinfo.test advancement error rate 55.06% > 1%
|
||||
Normal Synced 2m flagger Halt podinfo.test advancement error rate 47.00% > 1%
|
||||
Normal Synced 2m flagger (combined from similar events): Halt podinfo.test advancement error rate 38.08% > 1%
|
||||
Warning Synced 1m flagger Rolling back podinfo.test failed checks threshold reached 10
|
||||
Warning Synced 1m flagger Canary failed! Scaling down podinfo.test
|
||||
```
|
||||
@@ -72,7 +72,6 @@ metadata:
|
||||
name: podinfo
|
||||
namespace: test
|
||||
spec:
|
||||
# service mesh provider can be: kubernetes, istio, appmesh, nginx, gloo
|
||||
provider: kubernetes
|
||||
# deployment reference
|
||||
targetRef:
|
||||
|
||||
@@ -15,7 +15,7 @@ kumactl install control-plane | kubectl apply -f -
|
||||
kumactl install observability --components "grafana,prometheus" | kubectl apply -f -
|
||||
```
|
||||
|
||||
Install Flagger in the `kuma-system` namespace:
|
||||
Install Flagger in the `kong-mesh-system` namespace:
|
||||
|
||||
```bash
|
||||
kubectl apply -k github.com/fluxcd/flagger//kustomize/kuma
|
||||
|
||||
@@ -1,363 +0,0 @@
|
||||
# Open Service Mesh Canary Deployments
|
||||
|
||||
This guide shows you how to use Open Service Mesh (OSM) and Flagger to automate canary deployments.
|
||||
|
||||

|
||||
|
||||
## Prerequisites
|
||||
|
||||
Flagger requires a Kubernetes cluster **v1.16** or newer and Open Service Mesh **0.9.1** or newer.
|
||||
|
||||
OSM must have permissive traffic policy enabled and have an instance of Prometheus for metrics.
|
||||
|
||||
- If the OSM CLI is being used for installation, install OSM using the following command:
|
||||
```bash
|
||||
osm install \
|
||||
--set=OpenServiceMesh.deployPrometheus=true \
|
||||
--set=OpenServiceMesh.enablePermissiveTrafficPolicy=true
|
||||
```
|
||||
- If a managed instance of OSM is being used:
|
||||
- [Bring your own instance](docs.openservicemesh.io/docs/guides/observability/metrics/#byo-prometheus) of Prometheus,
|
||||
setting the namespace to match the managed OSM controller namespace
|
||||
- Enable permissive traffic policy after installation by updating the OSM MeshConfig resource:
|
||||
```bash
|
||||
# Replace <osm-namespace> with OSM controller's namespace
|
||||
kubectl patch meshconfig osm-mesh-config -n <osm-namespace> -p '{"spec":{"traffic":{"enablePermissiveTrafficPolicyMode":true}}}' --type=merge
|
||||
```
|
||||
|
||||
To install Flagger in the default `osm-system` namespace, use:
|
||||
```bash
|
||||
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/osm?ref=main
|
||||
```
|
||||
|
||||
Alternatively, if a non-default namespace or managed instance of OSM is in use, install Flagger with Helm, replacing the <osm-namespace>
|
||||
values as appropriate. If a custom instance of Prometheus is being used, replace `osm-prometheus` with the relevant Prometheus service name.
|
||||
```bash
|
||||
helm upgrade -i flagger flagger/flagger \
|
||||
--namespace=<osm-namespace> \
|
||||
--set meshProvider=osm \
|
||||
--set metricsServer=http://osm-prometheus.<osm-namespace>.svc:7070
|
||||
```
|
||||
|
||||
## Bootstrap
|
||||
|
||||
Flagger takes a Kubernetes deployment and optionally a horizontal pod autoscaler (HPA),
|
||||
then creates a series of objects (Kubernetes deployments, ClusterIP services and SMI traffic split).
|
||||
These objects expose the application inside the mesh and drive the canary analysis and promotion.
|
||||
|
||||
Create a `test` namespace and enable OSM namespace monitoring and metrics scraping for the namespace.
|
||||
|
||||
```bash
|
||||
kubectl create namespace test
|
||||
osm namespace add test
|
||||
osm metrics enable --namespace test
|
||||
```
|
||||
|
||||
Create a `podinfo` deployment and a horizontal pod autoscaler:
|
||||
|
||||
```bash
|
||||
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/podinfo?ref=main
|
||||
```
|
||||
|
||||
Install the load testing service to generate traffic during the canary analysis:
|
||||
|
||||
```bash
|
||||
kubectl apply -k https://github.com/fluxcd/flagger//kustomize/tester?ref=main
|
||||
```
|
||||
|
||||
Create a canary custom resource for the `podinfo` deployment.
|
||||
The following `podinfo` canary custom resource instructs Flagger to:
|
||||
1. monitor any changes to the `podinfo` deployment created earlier,
|
||||
2. detect `podinfo` deployment revision changes, and
|
||||
3. start a Flagger canary analysis, rollout, and promotion if there were deployment revision changes.
|
||||
|
||||
```yaml
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: Canary
|
||||
metadata:
|
||||
name: podinfo
|
||||
namespace: test
|
||||
spec:
|
||||
provider: osm
|
||||
# deployment reference
|
||||
targetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: podinfo
|
||||
# HPA reference (optional)
|
||||
autoscalerRef:
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
name: podinfo
|
||||
# the maximum time in seconds for the canary deployment
|
||||
# to make progress before it is rolled back (default 600s)
|
||||
progressDeadlineSeconds: 60
|
||||
service:
|
||||
# ClusterIP port number
|
||||
port: 9898
|
||||
# container port number or name (optional)
|
||||
targetPort: 9898
|
||||
analysis:
|
||||
# schedule interval (default 60s)
|
||||
interval: 30s
|
||||
# max number of failed metric checks before rollback
|
||||
threshold: 5
|
||||
# max traffic percentage routed to canary
|
||||
# percentage (0-100)
|
||||
maxWeight: 50
|
||||
# canary increment step
|
||||
# percentage (0-100)
|
||||
stepWeight: 5
|
||||
# OSM Prometheus checks
|
||||
metrics:
|
||||
- name: request-success-rate
|
||||
# minimum req success rate (non 5xx responses)
|
||||
# percentage (0-100)
|
||||
thresholdRange:
|
||||
min: 99
|
||||
interval: 1m
|
||||
- name: request-duration
|
||||
# maximum req duration P99
|
||||
# milliseconds
|
||||
thresholdRange:
|
||||
max: 500
|
||||
interval: 30s
|
||||
# testing (optional)
|
||||
webhooks:
|
||||
- name: acceptance-test
|
||||
type: pre-rollout
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 30s
|
||||
metadata:
|
||||
type: bash
|
||||
cmd: "curl -sd 'test' http://podinfo-canary.test:9898/token | grep token"
|
||||
- name: load-test
|
||||
type: rollout
|
||||
url: http://flagger-loadtester.test/
|
||||
timeout: 5s
|
||||
metadata:
|
||||
cmd: "hey -z 2m -q 10 -c 2 http://podinfo-canary.test:9898/"
|
||||
```
|
||||
|
||||
Save the above resource as podinfo-canary.yaml and then apply it:
|
||||
|
||||
```bash
|
||||
kubectl apply -f ./podinfo-canary.yaml
|
||||
```
|
||||
|
||||
When the canary analysis starts, Flagger will call the pre-rollout webhooks before routing traffic to the canary.
|
||||
The canary analysis will run for five minutes while validating the HTTP metrics and rollout hooks every half a minute.
|
||||
|
||||
After a couple of seconds Flagger will create the canary objects.
|
||||
|
||||
```bash
|
||||
# applied
|
||||
deployment.apps/podinfo
|
||||
horizontalpodautoscaler.autoscaling/podinfo
|
||||
ingresses.extensions/podinfo
|
||||
canary.flagger.app/podinfo
|
||||
|
||||
# generated
|
||||
deployment.apps/podinfo-primary
|
||||
horizontalpodautoscaler.autoscaling/podinfo-primary
|
||||
service/podinfo
|
||||
service/podinfo-canary
|
||||
service/podinfo-primary
|
||||
trafficsplits.split.smi-spec.io/podinfo
|
||||
```
|
||||
|
||||
After the bootstrap, the `podinfo` deployment will be scaled to zero and the traffic to `podinfo.test` will be routed to the primary pods.
|
||||
During the canary analysis, the `podinfo-canary.test` address can be used to target directly the canary pods.
|
||||
|
||||
## Automated Canary Promotion
|
||||
|
||||
Flagger implements a control loop that gradually shifts traffic to the canary while measuring key performance indicators like HTTP requests success rate, requests average duration and pod health.
|
||||
Based on analysis of the KPIs a canary is promoted or aborted.
|
||||
|
||||

|
||||
|
||||
Trigger a canary deployment by updating the container image:
|
||||
|
||||
```bash
|
||||
kubectl -n test set image deployment/podinfo \
|
||||
podinfod=ghcr.io/stefanprodan/podinfo:6.0.1
|
||||
```
|
||||
|
||||
Flagger detects that the deployment revision changed and starts a new rollout.
|
||||
|
||||
|
||||
```text
|
||||
kubectl -n test describe canary/podinfo
|
||||
|
||||
Status:
|
||||
Canary Weight: 0
|
||||
Failed Checks: 0
|
||||
Phase: Succeeded
|
||||
Events:
|
||||
New revision detected! Scaling up podinfo.test
|
||||
Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available
|
||||
Pre-rollout check acceptance-test passed
|
||||
Advance podinfo.test canary weight 5
|
||||
Advance podinfo.test canary weight 10
|
||||
Advance podinfo.test canary weight 15
|
||||
Advance podinfo.test canary weight 20
|
||||
Advance podinfo.test canary weight 25
|
||||
Waiting for podinfo.test rollout to finish: 1 of 2 updated replicas are available
|
||||
Advance podinfo.test canary weight 30
|
||||
Advance podinfo.test canary weight 35
|
||||
Advance podinfo.test canary weight 40
|
||||
Advance podinfo.test canary weight 45
|
||||
Advance podinfo.test canary weight 50
|
||||
Copying podinfo.test template spec to podinfo-primary.test
|
||||
Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available
|
||||
Promotion completed! Scaling down podinfo.test
|
||||
```
|
||||
|
||||
**Note** that if you apply any new changes to the `podinfo` deployment during the canary analysis, Flagger will restart the analysis.
|
||||
|
||||
A canary deployment is triggered by changes in any of the following objects:
|
||||
|
||||
* Deployment PodSpec \(container image, command, ports, env, resources, etc\)
|
||||
* ConfigMaps mounted as volumes or mapped to environment variables
|
||||
* Secrets mounted as volumes or mapped to environment variables
|
||||
|
||||
You can monitor all canaries with:
|
||||
|
||||
```bash
|
||||
watch kubectl get canaries --all-namespaces
|
||||
|
||||
NAMESPACE NAME STATUS WEIGHT LASTTRANSITIONTIME
|
||||
test podinfo Progressing 15 2019-06-30T14:05:07Z
|
||||
prod frontend Succeeded 0 2019-06-30T16:15:07Z
|
||||
prod backend Failed 0 2019-06-30T17:05:07Z
|
||||
```
|
||||
|
||||
## Automated Rollback
|
||||
|
||||
During the canary analysis you can generate HTTP 500 errors and high latency to test if Flagger pauses and rolls back the faulted version.
|
||||
|
||||
Trigger another canary deployment:
|
||||
|
||||
```bash
|
||||
kubectl -n test set image deployment/podinfo \
|
||||
podinfod=ghcr.io/stefanprodan/podinfo:6.0.2
|
||||
```
|
||||
|
||||
Exec into the load tester pod with:
|
||||
|
||||
```bash
|
||||
kubectl -n test exec -it flagger-loadtester-xx-xx sh
|
||||
```
|
||||
|
||||
Repeatedly generate HTTP 500 errors until the `kubectl describe` output below shows canary rollout failure:
|
||||
|
||||
```bash
|
||||
watch -n 0.1 curl http://podinfo-canary.test:9898/status/500
|
||||
```
|
||||
|
||||
Repeatedly generate latency until canary rollout fails:
|
||||
|
||||
```bash
|
||||
watch -n 0.1 curl http://podinfo-canary.test:9898/delay/1
|
||||
```
|
||||
|
||||
When the number of failed checks reaches the canary analysis thresholds defined in the `podinfo` canary custom resource earlier, the traffic is routed back to the primary, the canary is scaled to zero and the rollout is marked as failed.
|
||||
|
||||
```text
|
||||
kubectl -n test describe canary/podinfo
|
||||
|
||||
Status:
|
||||
Canary Weight: 0
|
||||
Failed Checks: 10
|
||||
Phase: Failed
|
||||
Events:
|
||||
Starting canary analysis for podinfo.test
|
||||
Pre-rollout check acceptance-test passed
|
||||
Advance podinfo.test canary weight 5
|
||||
Advance podinfo.test canary weight 10
|
||||
Advance podinfo.test canary weight 15
|
||||
Halt podinfo.test advancement success rate 69.17% < 99%
|
||||
Halt podinfo.test advancement success rate 61.39% < 99%
|
||||
Halt podinfo.test advancement success rate 55.06% < 99%
|
||||
Halt podinfo.test advancement request duration 1.20s > 0.5s
|
||||
Halt podinfo.test advancement request duration 1.45s > 0.5s
|
||||
Rolling back podinfo.test failed checks threshold reached 5
|
||||
Canary failed! Scaling down podinfo.test
|
||||
```
|
||||
|
||||
## Custom Metrics
|
||||
|
||||
The canary analysis can be extended with Prometheus queries.
|
||||
|
||||
Let's define a check for 404 not found errors.
|
||||
Edit the canary analysis (`podinfo-canary.yaml` file) and add the following metric.
|
||||
For more information on creating additional custom metrics using OSM metrics, please check the [metrics available in OSM](https://docs.openservicemesh.io/docs/guides/observability/metrics/#available-metrics).
|
||||
|
||||
```yaml
|
||||
analysis:
|
||||
metrics:
|
||||
- name: "404s percentage"
|
||||
threshold: 3
|
||||
query: |
|
||||
100 - (
|
||||
sum(
|
||||
rate(
|
||||
osm_request_total{
|
||||
destination_namespace="test",
|
||||
destination_kind="Deployment",
|
||||
destination_name="podinfo",
|
||||
response_code!="404"
|
||||
}[1m]
|
||||
)
|
||||
)
|
||||
/
|
||||
sum(
|
||||
rate(
|
||||
osm_request_total{
|
||||
destination_namespace="test",
|
||||
destination_kind="Deployment",
|
||||
destination_name="podinfo"
|
||||
}[1m]
|
||||
)
|
||||
) * 100
|
||||
)
|
||||
```
|
||||
|
||||
The above configuration validates the canary version by checking if the HTTP 404 req/sec percentage is below three percent of the total traffic.
|
||||
If the 404s rate reaches the 3% threshold, then the analysis is aborted and the canary is marked as failed.
|
||||
|
||||
Trigger a canary deployment by updating the container image:
|
||||
|
||||
```bash
|
||||
kubectl -n test set image deployment/podinfo \
|
||||
podinfod=ghcr.io/stefanprodan/podinfo:6.0.3
|
||||
```
|
||||
|
||||
Exec into the load tester pod with:
|
||||
|
||||
```bash
|
||||
kubectl -n test exec -it flagger-loadtester-xx-xx sh
|
||||
```
|
||||
|
||||
Repeatedly generate 404s until canary rollout fails:
|
||||
|
||||
```bash
|
||||
watch -n 0.1 curl http://podinfo-canary.test:9898/status/404
|
||||
```
|
||||
|
||||
Watch Flagger logs to confirm successful canary rollback.
|
||||
|
||||
```text
|
||||
kubectl -n osm-system logs deployment/flagger -f | jq .msg
|
||||
|
||||
Starting canary deployment for podinfo.test
|
||||
Pre-rollout check acceptance-test passed
|
||||
Advance podinfo.test canary weight 5
|
||||
Halt podinfo.test advancement 404s percentage 6.20 > 3
|
||||
Halt podinfo.test advancement 404s percentage 6.45 > 3
|
||||
Halt podinfo.test advancement 404s percentage 7.22 > 3
|
||||
Halt podinfo.test advancement 404s percentage 6.50 > 3
|
||||
Halt podinfo.test advancement 404s percentage 6.34 > 3
|
||||
Rolling back podinfo.test failed checks threshold reached 5
|
||||
Canary failed! Scaling down podinfo.test
|
||||
```
|
||||
@@ -63,7 +63,7 @@ helm upgrade -i flagger-loadtester flagger/loadtester \
|
||||
Create Traefik IngressRoute that references TraefikService generated by Flagger \(replace `app.example.com` with your own domain\):
|
||||
|
||||
```yaml
|
||||
apiVersion: traefik.containo.us/v1alpha1
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: IngressRoute
|
||||
metadata:
|
||||
name: podinfo
|
||||
@@ -177,7 +177,7 @@ horizontalpodautoscaler.autoscaling/podinfo-primary
|
||||
service/podinfo
|
||||
service/podinfo-canary
|
||||
service/podinfo-primary
|
||||
traefikservice.traefik.containo.us/podinfo
|
||||
traefikservice.traefik.io/podinfo
|
||||
```
|
||||
|
||||
## Automated canary promotion
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
Flagger can run automated application analysis, promotion and rollback for the following deployment strategies:
|
||||
|
||||
* **Canary Release** \(progressive traffic shifting\)
|
||||
* Istio, Linkerd, App Mesh, NGINX, Skipper, Contour, Gloo Edge, Traefik, Open Service Mesh, Kuma, Gateway API, Apache APISIX
|
||||
* Istio, Linkerd, App Mesh, NGINX, Skipper, Contour, Gloo Edge, Traefik, Kuma, Gateway API, Apache APISIX, Knative
|
||||
* **A/B Testing** \(HTTP headers and cookies traffic routing\)
|
||||
* Istio, App Mesh, NGINX, Contour, Gloo Edge, Gateway API
|
||||
* **Blue/Green** \(traffic switching\)
|
||||
* Kubernetes CNI, Istio, Linkerd, App Mesh, NGINX, Contour, Gloo Edge, Open Service Mesh, Gateway API
|
||||
* Kubernetes CNI, Istio, Linkerd, App Mesh, NGINX, Contour, Gloo Edge, Gateway API
|
||||
* **Blue/Green Mirroring** \(traffic shadowing\)
|
||||
* Istio, Gateway API
|
||||
* **Canary Release with Session Affinity** \(progressive traffic shifting combined with cookie based routing\)
|
||||
@@ -407,7 +407,7 @@ cookie based routing with regular weight based routing. This means once a user i
|
||||
version of our application (based on the traffic weights), they're always routed to that version, i.e.
|
||||
they're never routed back to the old version of our application.
|
||||
|
||||
You can enable this, by specifying `.spec.analsyis.sessionAffinity` in the Canary:
|
||||
You can enable this, by specifying `.spec.analysis.sessionAffinity` in the Canary:
|
||||
|
||||
```yaml
|
||||
analysis:
|
||||
@@ -450,3 +450,82 @@ the Canary deployment:
|
||||
```
|
||||
Set-Cookie: flagger-cookie=McxKdLQoIN; Max-Age=21600
|
||||
```
|
||||
|
||||
### Configuring stickiness for Primary deployment
|
||||
|
||||
The above strategy is helpful because it makes sure that any user that's routed to the Canary deployment
|
||||
once is always routed to that deployment. But, this can results in an imbalance in the traffic shifting,
|
||||
as over time, most of the traffic flows to the Canary deployment. To ensure fair traffic distribution, we
|
||||
can also configure stickiness for the Primary deployment. You can configure this by specifying a
|
||||
`primaryCookieName` field:
|
||||
|
||||
```yaml
|
||||
analysis:
|
||||
# schedule interval (default 60s)
|
||||
interval: 1m
|
||||
sessionAffinity:
|
||||
# name of the cookie used
|
||||
cookieName: flagger-cookie
|
||||
# max age of the cookie (in seconds)
|
||||
# optional; defaults to 86400
|
||||
maxAge: 21600
|
||||
# name of the cookie to use for the primary backend
|
||||
# optional; unset means no primary stickiness
|
||||
primaryCookieName: primary-flagger-cookie
|
||||
```
|
||||
|
||||
> Note: This is only supported for the Gateway API and Istio providers for now.
|
||||
|
||||
Let's understand what the above configuration does. All the session affinity stuff in the above section
|
||||
still occurs, but now the response header for requests routed to the primary deployment also include a
|
||||
`Set-Cookie` header:
|
||||
```
|
||||
Set-Cookie: primary-flagger-cookie=ApvLdqCoMF; Max-Age=60
|
||||
```
|
||||
|
||||
Note that the age of the cookie is the same as the Canary analysis's interval. This means that the cookie
|
||||
expires when a new steps of the analysis begins and a new cookie is generated like so:
|
||||
```
|
||||
Set-Cookie: primary-flagger-cookie=BRtlVaQoPC; Max-Age=60
|
||||
```
|
||||
|
||||
This ensures that, if the first request of a user during a particular step is routed to the primary deployment,
|
||||
then all subsequent requests will be routed to the same until the next step starts. During a new step, a new cookie
|
||||
value is generated which is then included in the headers of responses from the primary workload. This allows for
|
||||
weighted traffic routing to happen while ensuring that users don't ever switch back to the primary deployment from
|
||||
the canary deployment during a Canary analysis.
|
||||
|
||||
### Configuring additional cookie attributes
|
||||
|
||||
Depending on your use case, you may neet to set additional [cookie attributes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes) in order for your application to route requests correctly.
|
||||
You may set the following attributes:
|
||||
|
||||
```yaml
|
||||
analysis:
|
||||
# schedule interval (default 60s)
|
||||
interval: 1m
|
||||
sessionAffinity:
|
||||
# name of the cookie used
|
||||
cookieName: flagger-cookie
|
||||
# max age of the cookie (in seconds)
|
||||
# optional; defaults to 86400
|
||||
maxAge: 21600
|
||||
# defines the host to which the cookie will be sent.
|
||||
# optional
|
||||
domain: fluxcd.io
|
||||
# forbids JavaScript from accessing the cookie, for example, through the Document.cookie property.
|
||||
# optional
|
||||
httpOnly: true
|
||||
# indicates that the cookie should be stored using partitioned storage.
|
||||
# optional
|
||||
partitioned: true
|
||||
# indicates the path that must exist in the requested URL for the browser to send the Cookie header.
|
||||
# optional
|
||||
path: /flagger
|
||||
# controls whether or not a cookie is sent with cross-site requests.
|
||||
# optional; valid values are Strict, Lax or None
|
||||
sameSite: Strict
|
||||
# indicates that the cookie is sent to the server only when a request is made with the https: scheme (except on localhost)
|
||||
# optional
|
||||
secure: true
|
||||
```
|
||||
|
||||
@@ -147,6 +147,8 @@ spec:
|
||||
appProtocol: http
|
||||
targetPort: 9898
|
||||
portDiscovery: true
|
||||
headless: false
|
||||
trafficDistribution: PreferClose
|
||||
```
|
||||
|
||||
The container port from the target workload should match the `service.port` or `service.targetPort`.
|
||||
@@ -154,6 +156,7 @@ The `service.name` is optional, defaults to `spec.targetRef.name`.
|
||||
The `service.targetPort` can be a container port number or name.
|
||||
The `service.portName` is optional (defaults to `http`), if your workload uses gRPC then set the port name to `grpc`.
|
||||
The `service.appProtocol` is optional, more details can be found [here](https://kubernetes.io/docs/concepts/services-networking/service/#application-protocol).
|
||||
The `service.trafficDistribution` is optional, more details can be found [here](https://kubernetes.io/docs/concepts/services-networking/service/#traffic-distribution).
|
||||
|
||||
If port discovery is enabled, Flagger scans the target workload and extracts the containers ports
|
||||
excluding the port specified in the canary service and service mesh sidecar ports.
|
||||
@@ -204,6 +207,13 @@ Note that the `apex` annotations are added to both the generated Kubernetes Serv
|
||||
generated service mesh/ingress object. This allows using external-dns with Istio `VirtualServices`
|
||||
and `TraefikServices`. Beware of configuration conflicts [here](../faq.md#ExternalDNS).
|
||||
|
||||
Note that if any annotations or labels are added that are not specified here,
|
||||
Flagger will remove them during reconciliation. To specify metadata
|
||||
that should be ignored by Flagger, configure `unmanagedMetadata`.
|
||||
|
||||
If you want for the generated Kubernetes ClusterIP services to be [headless](https://kubernetes.io/docs/concepts/services-networking/service/#headless-services),
|
||||
then set `service.headless` to true.
|
||||
|
||||
Besides port mapping and metadata, the service specification can
|
||||
contain URI match and rewrite rules, timeout and retry polices:
|
||||
|
||||
|
||||
@@ -668,3 +668,116 @@ Reference the template in the canary analysis:
|
||||
max: 1000
|
||||
interval: 1m
|
||||
```
|
||||
|
||||
## Keptn
|
||||
|
||||
You can create custom metric checks using the Keptn provider.
|
||||
This Provider allows to verify either the value of a single [KeptnMetric](https://keptn.sh/stable/docs/reference/crd-reference/metric/),
|
||||
representing the value of a single metric,
|
||||
or of a [Keptn Analysis](https://keptn.sh/stable/docs/reference/crd-reference/analysis/),
|
||||
which provides a flexible grading logic for analysing and prioritising a number of different
|
||||
metric values coming from different data sources.
|
||||
|
||||
This provider requires [Keptn](https://keptn.sh/stable/docs/installation/) to be installed in the cluster.
|
||||
|
||||
Example for a Keptn metric template:
|
||||
|
||||
```yaml
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: MetricTemplate
|
||||
metadata:
|
||||
name: response-time
|
||||
namespace: istio-system
|
||||
spec:
|
||||
provider:
|
||||
type: keptn
|
||||
query: keptnmetric/my-namespace/response-time/2m/reporter=destination
|
||||
```
|
||||
|
||||
This will reference the `KeptnMetric` with the name `response-time` in
|
||||
the namespace `my-namespace`, which could look like the following:
|
||||
|
||||
```yaml
|
||||
apiVersion: metrics.keptn.sh/v1beta1
|
||||
kind: KeptnMetric
|
||||
metadata:
|
||||
name: response-time
|
||||
namespace: my-namespace
|
||||
spec:
|
||||
fetchIntervalSeconds: 10
|
||||
provider:
|
||||
name: my-prometheus-keptn-provider
|
||||
query: histogram_quantile(0.8, sum by(le) (rate(http_server_request_latency_seconds_bucket{status_code='200',
|
||||
job='simple-go-backend'}[5m[])))
|
||||
```
|
||||
|
||||
The `query` contains the following components, which are divided by `/` characters:
|
||||
|
||||
```
|
||||
<type>/<namespace>/<resource-name>/<timeframe>/<arguments>
|
||||
```
|
||||
|
||||
* **type (required)**: Must be either `keptnmetric` or `analysis`.
|
||||
* **namespace (required)**: The namespace of the referenced `KeptnMetric`/`AnalysisDefinition`.
|
||||
* **resource-name (required):** The name of the referenced `KeptnMetric`/`AnalysisDefinition`.
|
||||
* **timeframe (optional)**: The timeframe used for the Analysis.
|
||||
This will usually be set to the same value as the analysis interval of a `Canary`.
|
||||
Only relevant if the `type` is set to `analysis`.
|
||||
* **arguments (optional)**: Arguments to be passed to an `Analysis`.
|
||||
Arguments are passed as a list of key value pairs, separated by `;` characters,
|
||||
e.g. `foo=bar;bar=foo`.
|
||||
Only relevant if the `type` is set to `analysis`.
|
||||
|
||||
For the type `analysis`, the value returned by the provider is either `0`
|
||||
(if the analysis failed), or `1` (analysis passed).
|
||||
|
||||
## Splunk
|
||||
|
||||
You can create custom metric checks using the Splunk provider.
|
||||
|
||||
Create a secret that contains your authentication token that can be found in the Splunk o11y UI.
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: splunk
|
||||
namespace: istio-system
|
||||
data:
|
||||
sf_token_key: your-access-token
|
||||
```
|
||||
|
||||
Splunk template example:
|
||||
|
||||
```yaml
|
||||
apiVersion: flagger.app/v1beta1
|
||||
kind: MetricTemplate
|
||||
metadata:
|
||||
name: success-rate
|
||||
namespace: istio-system
|
||||
spec:
|
||||
provider:
|
||||
type: splunk
|
||||
address: https://api.<REALM>.signalfx.com
|
||||
secretRef:
|
||||
name: splunk
|
||||
query: |
|
||||
total = data('traces.count', filter=filter('sf_service', '{{target}}')).sum().publish(enable=False)
|
||||
success = data('traces.count', filter=filter('sf_service', '{{target}}') and filter('sf_error', 'false')).sum().publish(enable=False)
|
||||
((success/total) * 100).publish()
|
||||
```
|
||||
The query format documentation can be found [here](https://dev.splunk.com/observability/docs/signalflow).
|
||||
|
||||
Reference the template in the canary analysis:
|
||||
|
||||
```yaml
|
||||
analysis:
|
||||
metrics:
|
||||
- name: "success rate"
|
||||
templateRef:
|
||||
name: success-rate
|
||||
namespace: istio-system
|
||||
thresholdRange:
|
||||
max: 99
|
||||
interval: 1m
|
||||
```
|
||||
|
||||
@@ -121,4 +121,10 @@ flagger_canary_duration_seconds_count{name="podinfo",namespace="test"} 6
|
||||
# Last canary metric analysis result per different metrics
|
||||
flagger_canary_metric_analysis{metric="podinfo-http-successful-rate",name="podinfo",namespace="test"} 1
|
||||
flagger_canary_metric_analysis{metric="podinfo-custom-metric",name="podinfo",namespace="test"} 0.918223108974359
|
||||
|
||||
# Canary successes total counter
|
||||
flagger_canary_successes_total{name="podinfo",namespace="test",deployment_strategy="canary",analysis_status="completed"} 5
|
||||
|
||||
# Canary failures total counter
|
||||
flagger_canary_failures_total{name="podinfo",namespace="test",deployment_strategy="canary",analysis_status="completed"} 1
|
||||
```
|
||||
|
||||
@@ -124,7 +124,10 @@ Event payload (HTTP POST):
|
||||
The event receiver can create alerts based on the received phase
|
||||
(possible values: `Initialized`, `Waiting`, `Progressing`, `Promoting`, `Finalising`, `Succeeded` or `Failed`).
|
||||
|
||||
The webhook request can be retried by specifying a positive integer in the `retries` field.
|
||||
Options:
|
||||
* retries: The webhook request can be retried by specifying a positive integer in the `retries` field. This helps ensure reliability if the webhook fails due to transient network issues.
|
||||
|
||||
* disable TLS: Set `disableTLS` to `true` in the webhook spec to bypass TLS verification. This is useful in cases where the target service uses self-signed certificates, or you need to connect to an insecure service for testing purposes.
|
||||
|
||||
## Load Testing
|
||||
|
||||
|
||||
152
go.mod
152
go.mod
@@ -1,97 +1,109 @@
|
||||
module github.com/fluxcd/flagger
|
||||
|
||||
go 1.22
|
||||
go 1.25.0
|
||||
|
||||
require (
|
||||
cloud.google.com/go/monitoring v1.18.1
|
||||
github.com/Masterminds/semver/v3 v3.2.1
|
||||
github.com/aws/aws-sdk-go v1.51.7
|
||||
github.com/davecgh/go-spew v1.1.1
|
||||
cloud.google.com/go/monitoring v1.24.2
|
||||
github.com/Masterminds/semver/v3 v3.4.0
|
||||
github.com/aws/aws-sdk-go v1.55.8
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
|
||||
github.com/go-logr/zapr v1.3.0
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/googleapis/gax-go/v2 v2.12.3
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.13.0
|
||||
github.com/prometheus/client_golang v1.19.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/googleapis/gax-go/v2 v2.15.0
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.14.0
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
github.com/signalfx/signalflow-client-go v0.1.0
|
||||
github.com/signalfx/signalfx-go v1.53.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
go.uber.org/zap v1.27.0
|
||||
google.golang.org/api v0.171.0
|
||||
google.golang.org/genproto v0.0.0-20240325203815-454cdb8f5daa
|
||||
google.golang.org/grpc v1.62.1
|
||||
google.golang.org/protobuf v1.33.0
|
||||
golang.org/x/sync v0.18.0
|
||||
google.golang.org/api v0.252.0
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822
|
||||
google.golang.org/grpc v1.76.0
|
||||
google.golang.org/protobuf v1.36.10
|
||||
gopkg.in/h2non/gock.v1 v1.1.2
|
||||
k8s.io/api v0.27.11
|
||||
k8s.io/apimachinery v0.27.11
|
||||
k8s.io/client-go v0.27.11
|
||||
k8s.io/code-generator v0.27.11
|
||||
k8s.io/klog/v2 v2.120.1
|
||||
k8s.io/api v0.34.1
|
||||
k8s.io/apimachinery v0.34.1
|
||||
k8s.io/client-go v0.34.1
|
||||
k8s.io/code-generator v0.34.1
|
||||
k8s.io/klog/v2 v2.130.1
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397
|
||||
knative.dev/serving v0.46.6
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute v1.25.1 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/auth v0.17.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.10.2 // indirect
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/blendle/zapdriver v1.3.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.6 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic v0.6.9 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
github.com/google/go-containerregistry v0.20.3 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/imdario/mergo v0.3.15 // indirect
|
||||
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/oapi-codegen/runtime v1.0.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.66.1 // indirect
|
||||
github.com/prometheus/procfs v0.17.0 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc 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
|
||||
golang.org/x/crypto v0.21.0 // indirect
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
golang.org/x/net v0.22.0 // indirect
|
||||
golang.org/x/oauth2 v0.18.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/term v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.16.1 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/mod v0.29.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/oauth2 v0.31.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/term v0.37.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/time v0.13.0 // indirect
|
||||
golang.org/x/tools v0.38.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/gengo v0.0.0-20230306165830-ab3349d207d4 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect
|
||||
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
|
||||
knative.dev/networking v0.0.0-20250902160145-7dad473f6351 // indirect
|
||||
knative.dev/pkg v0.0.0-20250909011231-077dcf0d00e8 // 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/v6 v6.3.0 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
)
|
||||
|
||||
497
go.sum
497
go.sum
@@ -1,131 +1,100 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU=
|
||||
cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/monitoring v1.18.1 h1:0yvFXK+xQd95VKo6thndjwnJMno7c7Xw1CwMByg0B+8=
|
||||
cloud.google.com/go/monitoring v1.18.1/go.mod h1:52hTzJ5XOUMRm7jYi7928aEdVxBEmGwA0EjNJXIBvt8=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4=
|
||||
cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM=
|
||||
cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
|
||||
github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
|
||||
github.com/aws/aws-sdk-go v1.51.7 h1:RRjxHhx9RCjw5AhgpmmShq3F4JDlleSkyhYMQ2xUAe8=
|
||||
github.com/aws/aws-sdk-go v1.51.7/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
|
||||
github.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ=
|
||||
github.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk=
|
||||
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/blendle/zapdriver v1.3.1 h1:C3dydBOWYRiOk+B8X9IVZ5IOe+7cl+tGOexN4QqHfpE=
|
||||
github.com/blendle/zapdriver v1.3.1/go.mod h1:mdXfREi6u5MArG4j9fewC+FGnXaBR+T4Ox4J2u4eHCc=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv1aFbZMiM9vblcSArJRf2Irls=
|
||||
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||
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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE=
|
||||
github.com/emicklei/go-restful/v3 v3.10.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=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
|
||||
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
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/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.13.4 h1:zEqyPVyku6IvWCFwux4x9RxkLOMUL+1vC9xUFv5l2/M=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4 h1:jb83lalDRZSpPWW2Z7Mck/8kXZ5CQAFYVjQcdVIr83A=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
|
||||
github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
|
||||
github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=
|
||||
github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=
|
||||
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/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/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.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-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/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-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/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
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/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=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0=
|
||||
github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E=
|
||||
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=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
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/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.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI=
|
||||
github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.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-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
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.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
||||
github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA=
|
||||
github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
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.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
|
||||
github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
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 v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||
github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM=
|
||||
github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.13.0 h1:ioBbLmR5NMbAjP4UVA5r9b5xGjpABD7j65pI8kFphDM=
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.13.0/go.mod h1:k+spCbt9hcvqvUiz0sr5D8LolXHqAAOfPw9v/RIRHl4=
|
||||
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.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.14.0 h1:AjbBfJuq+QoaXNcrova8smSjwJdUHnwvfjMF71M1iI4=
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.14.0/go.mod h1:Ahpm3QXKMJslpXl3IftVLVezreAUtBOTZssDrjZEFHI=
|
||||
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf h1:7JTmneyiNEwVBOHSjoMxiWAqB992atOeepeFYegn5RU=
|
||||
github.com/influxdata/line-protocol v0.0.0-20210922203350-b1ad95c89adf/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 h1:liMMTbpW34dhU4az1GN0pTPADwNmvoRSeoZ6PItiqnY=
|
||||
github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
@@ -135,256 +104,204 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
|
||||
github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
|
||||
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/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
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.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/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
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/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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/oapi-codegen/runtime v1.0.0 h1:P4rqFX5fMFWqRzY9M/3YF9+aPSPPB06IzP2P7oOxrWo=
|
||||
github.com/oapi-codegen/runtime v1.0.0/go.mod h1:LmCUMQuPB4M/nLXilQXhHw+BLZdDb18B34OO356yJ/A=
|
||||
github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk=
|
||||
github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo=
|
||||
github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E=
|
||||
github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ=
|
||||
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/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
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/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
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/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.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
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.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||
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/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/signalfx/signalflow-client-go v0.1.0 h1:aqyt+st3/y8x8JtuwYRL9pOkOTJb+KeCoRWi0SuY5vw=
|
||||
github.com/signalfx/signalflow-client-go v0.1.0/go.mod h1:mY4DTAZuLHyMNGBjSrNdCg5kUU0hSkYjukAnjsVbsQs=
|
||||
github.com/signalfx/signalfx-go v1.53.0 h1:TMQDuj/Kyom8Xtb7NFuvV7URfCuTRjTrLyD0BroUDmM=
|
||||
github.com/signalfx/signalfx-go v1.53.0/go.mod h1:CHt+/W1qd62tXxNqG7ZUB9pEsEAOD6tuvdlyDNIOO1s=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
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=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
|
||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
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.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.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY=
|
||||
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.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
|
||||
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/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
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.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
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.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
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.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
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-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
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-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
|
||||
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
|
||||
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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo=
|
||||
golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
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-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
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.5/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.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
|
||||
golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
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=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
|
||||
golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
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=
|
||||
google.golang.org/api v0.171.0 h1:w174hnBPqut76FzW5Qaupt7zY8Kql6fiVjgys4f58sU=
|
||||
google.golang.org/api v0.171.0/go.mod h1:Hnq5AHm4OTMt2BUVjael2CWZFD6vksJdWCWiUAmjC9o=
|
||||
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/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
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-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20240325203815-454cdb8f5daa h1:ePqxpG3LVx+feAUOx8YmR5T7rc0rdzK8DyxM8cQ9zq0=
|
||||
google.golang.org/genproto v0.0.0-20240325203815-454cdb8f5daa/go.mod h1:CnZenrTdRJb7jc+jOm0Rkywq+9wh0QC4U8tyiRbEPPM=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
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.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
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=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
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.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/api v0.252.0 h1:xfKJeAJaMwb8OC9fesr369rjciQ704AjU/psjkKURSI=
|
||||
google.golang.org/api v0.252.0/go.mod h1:dnHOv81x5RAmumZ7BWLShB/u7JZNeyalImxHmtTHxqw=
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
|
||||
google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b h1:ULiyYQ0FdsJhwwZUwbaXpZF5yUE3h+RA+gxvBu37ucc=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b/go.mod h1:oDOGiMSXHL4sDTJvFvIB9nRQCGdLP1o/iVaqQK8zB+M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 h1:CirRxTOwnRWVLKzDNrs0CXAaVozJoR4G9xvdRecrdpk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
|
||||
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
|
||||
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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/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/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
|
||||
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
|
||||
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.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
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.27.11 h1:IsGrWbXt7RkE+arc9GLQPYI5AtZkT+feBMorY+Nzx4I=
|
||||
k8s.io/api v0.27.11/go.mod h1:SGqTcyqa0e+Db3pgyH6v+por5dO2OdTkKDCdD3Op3Ng=
|
||||
k8s.io/apimachinery v0.27.11 h1:ivrKMN7JgdtKhay14S5UQlvilV3z6W+wjiSQTzyr5zc=
|
||||
k8s.io/apimachinery v0.27.11/go.mod h1:IHu2ovJ60RqxyPSLmTel7KDLdOCRbpOxwtUBmwBnT/E=
|
||||
k8s.io/client-go v0.27.11 h1:SZChXsDaN6lB5IYywCpvQs/ZUa5vK2NHkpEwUhoK3fQ=
|
||||
k8s.io/client-go v0.27.11/go.mod h1:Rg3Yeuk9sX87gpVunVn3AsvMkGZfXuutTDC/jigBNUo=
|
||||
k8s.io/code-generator v0.27.11 h1:J3SExe92VqSKMUSIbItJrTtPITOSWS58AqIbLwycNY0=
|
||||
k8s.io/code-generator v0.27.11/go.mod h1:iyFD2q65bX/xrlrGzXi2kZXiBTbTDiAzEty3jq6a0NA=
|
||||
k8s.io/gengo v0.0.0-20230306165830-ab3349d207d4 h1:aClvVG6GbX10ISHcc24J+tqbr0S7fEe1MWkFJ7cWWCI=
|
||||
k8s.io/gengo v0.0.0-20230306165830-ab3349d207d4/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
|
||||
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
|
||||
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
|
||||
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg=
|
||||
k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg=
|
||||
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk=
|
||||
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM=
|
||||
k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk=
|
||||
k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4=
|
||||
k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw=
|
||||
k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY=
|
||||
k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8=
|
||||
k8s.io/code-generator v0.34.1 h1:WpphT26E+j7tEgIUfFr5WfbJrktCGzB3JoJH9149xYc=
|
||||
k8s.io/code-generator v0.34.1/go.mod h1:DeWjekbDnJWRwpw3s0Jat87c+e0TgkxoR4ar608yqvg=
|
||||
k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f h1:SLb+kxmzfA87x4E4brQzB33VBbT2+x7Zq9ROIHmGn9Q=
|
||||
k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU=
|
||||
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-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA=
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y=
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
knative.dev/networking v0.0.0-20250902160145-7dad473f6351 h1:Gv/UqbN0AK+ORoT5e2Kg+3+uMW/y9CCdhpXKxYaVV6k=
|
||||
knative.dev/networking v0.0.0-20250902160145-7dad473f6351/go.mod h1:P/fAhhVDgmLt1ugFX8vBvdSDiUOw2P4SGcjbzoZ02Xw=
|
||||
knative.dev/pkg v0.0.0-20250909011231-077dcf0d00e8 h1:n0BMHXIem9MyDkK4vfA4Vzdxaf1e+EeLJ6k+8exCjjI=
|
||||
knative.dev/pkg v0.0.0-20250909011231-077dcf0d00e8/go.mod h1:a1amDzo4YIUNuGeDgEz/uDHs5MQVYI1DXnRnEpWCAts=
|
||||
knative.dev/serving v0.46.6 h1:jmVF560qnepNBG69VEbNRtknGFwZtGEyR1QSlNuoKmk=
|
||||
knative.dev/serving v0.46.6/go.mod h1:mY7uXQo49PnxxM6UJWfnSAi6OqFEPM03dpWc8xsH3+I=
|
||||
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 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/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
|
||||
@@ -25,15 +25,29 @@ trap "cleanup" EXIT SIGINT
|
||||
|
||||
echo ">> Temporary output directory ${TEMP_DIR}"
|
||||
|
||||
# Ensure we can execute.
|
||||
chmod +x ${CODEGEN_PKG}/generate-groups.sh
|
||||
chmod +x ${CODEGEN_PKG}/generate-internal-groups.sh
|
||||
PACKAGE_PATH_BASE="github.com/fluxcd/flagger"
|
||||
|
||||
${CODEGEN_PKG}/generate-groups.sh client,deepcopy,informer,lister \
|
||||
github.com/fluxcd/flagger/pkg/client github.com/fluxcd/flagger/pkg/apis \
|
||||
"flagger:v1beta1 appmesh:v1beta2 appmesh:v1beta1 istio:v1beta1 smi:v1alpha1 smi:v1alpha2 smi:v1alpha3 gloo/gloo:v1 gloo/gateway:v1 projectcontour:v1 traefik:v1alpha1 kuma:v1alpha1 gatewayapi:v1beta1 gatewayapi:v1 keda:v1alpha1 apisix:v2" \
|
||||
--output-base "${TEMP_DIR}" \
|
||||
--go-header-file ${SCRIPT_ROOT}/hack/boilerplate.go.txt
|
||||
mkdir -p "${TEMP_DIR}/${PACKAGE_PATH_BASE}/pkg/client/informers" \
|
||||
"${TEMP_DIR}/${PACKAGE_PATH_BASE}/pkg/client/listers" \
|
||||
"${TEMP_DIR}/${PACKAGE_PATH_BASE}/pkg/client/clientset"
|
||||
|
||||
# Ensure we can execute.
|
||||
chmod +x ${CODEGEN_PKG}/kube_codegen.sh
|
||||
|
||||
source ${CODEGEN_PKG}/kube_codegen.sh
|
||||
|
||||
kube::codegen::gen_helpers \
|
||||
--boilerplate "${SCRIPT_ROOT}/hack/boilerplate.go.txt" \
|
||||
./pkg/apis
|
||||
|
||||
kube::codegen::gen_client \
|
||||
--output-dir "${TEMP_DIR}/${PACKAGE_PATH_BASE}/pkg/client" \
|
||||
--output-pkg "${PACKAGE_PATH_BASE}/pkg/client" \
|
||||
--with-watch \
|
||||
--boilerplate "${SCRIPT_ROOT}/hack/boilerplate.go.txt" \
|
||||
./pkg/apis
|
||||
|
||||
tree $TEMP_DIR/${PACKAGE_PATH_BASE/pkg/client}/
|
||||
|
||||
# Copy everything back.
|
||||
cp -r "${TEMP_DIR}/github.com/fluxcd/flagger/." "${SCRIPT_ROOT}/"
|
||||
cp -r "${TEMP_DIR}/${PACKAGE_PATH_BASE}/." "${SCRIPT_ROOT}/"
|
||||
|
||||
@@ -34,14 +34,6 @@ kustomize build https://github.com/fluxcd/flagger/kustomize/linkerd?ref=main | k
|
||||
This deploys Flagger in the `linkerd` namespace and sets the metrics server URL to linkerd-viz extension's Prometheus instance
|
||||
which lives under `linkerd-viz` namespace by default.
|
||||
|
||||
Install Flagger for Open Service Mesh:
|
||||
|
||||
```bash
|
||||
kustomize build https://github.com/fluxcd/flagger/kustomize/osm?ref=main | kubectl apply -f -
|
||||
```
|
||||
|
||||
This deploys Flagger in the `osm-system` namespace and sets the metrics server URL to OSM's Prometheus instance.
|
||||
|
||||
If you want to install a specific Flagger release, add the version number to the URL:
|
||||
|
||||
```bash
|
||||
@@ -76,7 +68,7 @@ metadata:
|
||||
name: app
|
||||
namespace: test
|
||||
spec:
|
||||
# can be: kubernetes, istio, linkerd, appmesh, nginx, skipper, gloo, osm
|
||||
# can be: kubernetes, istio, linkerd, appmesh, nginx, skipper, gloo
|
||||
# use the kubernetes provider for Blue/Green style deployments
|
||||
provider: nginx
|
||||
```
|
||||
|
||||
@@ -80,7 +80,6 @@ spec:
|
||||
type: object
|
||||
required:
|
||||
- targetRef
|
||||
- service
|
||||
- analysis
|
||||
properties:
|
||||
provider:
|
||||
@@ -129,9 +128,11 @@ spec:
|
||||
type: object
|
||||
properties:
|
||||
minReplicas:
|
||||
type: number
|
||||
type: integer
|
||||
minimum: 1
|
||||
maxReplicas:
|
||||
type: number
|
||||
type: integer
|
||||
minimum: 1
|
||||
ingressRef:
|
||||
description: Ingress selector
|
||||
type: object
|
||||
@@ -190,11 +191,21 @@ spec:
|
||||
appProtocol:
|
||||
description: Application protocol of the port
|
||||
type: string
|
||||
trafficDistribution:
|
||||
description: Traffic distribution of the service
|
||||
type: string
|
||||
enum:
|
||||
- PreferClose
|
||||
- PreferSameZone
|
||||
- PreferSameNode
|
||||
targetPort:
|
||||
description: Container target port name
|
||||
x-kubernetes-int-or-string: true
|
||||
portDiscovery:
|
||||
description: Enable port dicovery
|
||||
description: Enable port discovery
|
||||
type: boolean
|
||||
headless:
|
||||
description: Headless if set to true, generates headless Kubernetes services.
|
||||
type: boolean
|
||||
timeout:
|
||||
description: HTTP or gRPC request timeout
|
||||
@@ -905,6 +916,18 @@ spec:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: string
|
||||
unmanagedMetadata:
|
||||
description: UnmanagedMetadata is a list of metadata keys that should be ignored by Flagger.
|
||||
type: object
|
||||
properties:
|
||||
annotations:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
labels:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
skipAnalysis:
|
||||
description: Skip analysis and promote canary
|
||||
type: boolean
|
||||
@@ -1135,6 +1158,9 @@ spec:
|
||||
retries:
|
||||
description: Number of retries for this webhook
|
||||
type: number
|
||||
disableTLS:
|
||||
description: Disable TLS verification for this webhook
|
||||
type: boolean
|
||||
metadata:
|
||||
description: Metadata (key-value pairs) for this webhook
|
||||
type: object
|
||||
@@ -1148,10 +1174,35 @@ spec:
|
||||
cookieName:
|
||||
description: CookieName is the key that will be used for the session affinity cookie.
|
||||
type: string
|
||||
primaryCookieName:
|
||||
description: CookieName is the key that will be used for the session affinity cookie.
|
||||
type: string
|
||||
domain:
|
||||
description: Domain defines the host to which the cookie will be sent.
|
||||
type: string
|
||||
httpOnly:
|
||||
description: HttpOnly forbids JavaScript from accessing the cookie, for example, through the Document.cookie property.
|
||||
type: boolean
|
||||
maxAge:
|
||||
description: MaxAge indicates the number of seconds until the session affinity cookie will expire.
|
||||
default: 86400
|
||||
type: number
|
||||
partitioned:
|
||||
description: Partitioned indicates that the cookie should be stored using partitioned storage.
|
||||
type: boolean
|
||||
path:
|
||||
description: Path indicates the path that must exist in the requested URL for the browser to send the Cookie header.
|
||||
type: string
|
||||
sameSite:
|
||||
description: SameSite controls whether or not a cookie is sent with cross-site requests.
|
||||
type: string
|
||||
enum:
|
||||
- Strict
|
||||
- Lax
|
||||
- None
|
||||
secure:
|
||||
description: "Secure indicates that the cookie is sent to the server only when a request is made with the https: scheme (except on localhost)"
|
||||
type: boolean
|
||||
status:
|
||||
description: CanaryStatus defines the observed state of a canary.
|
||||
type: object
|
||||
@@ -1199,6 +1250,9 @@ spec:
|
||||
sessionAffinityCookie:
|
||||
description: Session affinity cookie of the current canary run
|
||||
type: string
|
||||
primarySessionAffinityCookie:
|
||||
description: Primary session affinity cookie of the current canary run
|
||||
type: string
|
||||
previousSessionAffinityCookie:
|
||||
description: Session affinity cookie of the previous canary run
|
||||
type: string
|
||||
@@ -1298,9 +1352,18 @@ spec:
|
||||
- newrelic
|
||||
- graphite
|
||||
- dynatrace
|
||||
- keptn
|
||||
- splunk
|
||||
address:
|
||||
description: API address of this provider
|
||||
type: string
|
||||
headers:
|
||||
description: Headers to add to HTTP(S) requests
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
secretRef:
|
||||
description: Kubernetes secret reference containing the provider credentials
|
||||
type: object
|
||||
|
||||
@@ -9,4 +9,4 @@ resources:
|
||||
images:
|
||||
- name: ghcr.io/fluxcd/flagger
|
||||
newName: ghcr.io/fluxcd/flagger
|
||||
newTag: 1.37.0
|
||||
newTag: 1.42.0
|
||||
|
||||
@@ -241,10 +241,37 @@ rules:
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- metrics.keptn.sh
|
||||
resources:
|
||||
- keptnmetrics
|
||||
- analyses
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- serving.knative.dev
|
||||
resources:
|
||||
- services
|
||||
verbs:
|
||||
- get
|
||||
- update
|
||||
- apiGroups:
|
||||
- serving.knative.dev
|
||||
resources:
|
||||
- revisions
|
||||
verbs:
|
||||
- get
|
||||
- nonResourceURLs:
|
||||
- /version
|
||||
verbs:
|
||||
- get
|
||||
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
|
||||
8
kustomize/knative/kustomization.yaml
Normal file
8
kustomize/knative/kustomization.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace: flagger-system
|
||||
resources:
|
||||
- namespace.yaml
|
||||
bases:
|
||||
- ../base/flagger/
|
||||
- ../base/prometheus/
|
||||
patchesStrategicMerge:
|
||||
- patch.yaml
|
||||
9
kustomize/knative/namespace.yaml
Normal file
9
kustomize/knative/namespace.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: flagger-system
|
||||
annotations:
|
||||
linkerd.io/inject: disabled
|
||||
labels:
|
||||
istio-injection: disabled
|
||||
appmesh.k8s.aws/sidecarInjectorWebhook: disabled
|
||||
14
kustomize/knative/patch.yaml
Normal file
14
kustomize/knative/patch.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: flagger
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: flagger
|
||||
args:
|
||||
- -log-level=info
|
||||
- -include-label-prefix=app.kubernetes.io
|
||||
- -mesh-provider=knative
|
||||
- -metrics-server=http://flagger-prometheus:9090
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace: kuma-system
|
||||
namespace: kong-mesh-system
|
||||
bases:
|
||||
- ../base/flagger/
|
||||
patchesStrategicMerge:
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
namespace: osm-system
|
||||
bases:
|
||||
- ../base/flagger/
|
||||
patchesStrategicMerge:
|
||||
- patch.yaml
|
||||
@@ -1,27 +0,0 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: flagger
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: flagger
|
||||
args:
|
||||
- -log-level=info
|
||||
- -include-label-prefix=app.kubernetes.io
|
||||
- -mesh-provider=osm
|
||||
- -metrics-server=http://osm-prometheus.osm-system.svc:7070
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: flagger
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: flagger
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: flagger
|
||||
namespace: osm-system
|
||||
@@ -19,7 +19,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: loadtester
|
||||
image: ghcr.io/fluxcd/flagger-loadtester:0.32.0
|
||||
image: ghcr.io/fluxcd/flagger-loadtester:0.36.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- name: http
|
||||
|
||||
@@ -20,10 +20,11 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1"
|
||||
istiov1beta1 "github.com/fluxcd/flagger/pkg/apis/istio/v1beta1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
|
||||
"github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1"
|
||||
istiov1beta1 "github.com/fluxcd/flagger/pkg/apis/istio/v1beta1"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -35,6 +36,13 @@ const (
|
||||
MetricInterval = "1m"
|
||||
)
|
||||
|
||||
// Deployment strategies
|
||||
const (
|
||||
DeploymentStrategyCanary = "canary"
|
||||
DeploymentStrategyBlueGreen = "blue-green"
|
||||
DeploymentStrategyABTesting = "ab-testing"
|
||||
)
|
||||
|
||||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
@@ -74,7 +82,7 @@ type CanarySpec struct {
|
||||
|
||||
// AutoscalerRef references an autoscaling resource
|
||||
// +optional
|
||||
AutoscalerRef *AutoscalerRefernce `json:"autoscalerRef,omitempty"`
|
||||
AutoscalerRef *AutoscalerReference `json:"autoscalerRef,omitempty"`
|
||||
|
||||
// Reference to NGINX ingress resource
|
||||
// +optional
|
||||
@@ -143,9 +151,19 @@ type CanaryService struct {
|
||||
// +optional
|
||||
AppProtocol string `json:"appProtocol,omitempty"`
|
||||
|
||||
// TrafficDistribution of the service
|
||||
// https://kubernetes.io/docs/concepts/services-networking/service/#traffic-distribution
|
||||
// +optional
|
||||
TrafficDistribution string `json:"trafficDistribution,omitempty"`
|
||||
|
||||
// PortDiscovery adds all container ports to the generated Kubernetes service
|
||||
PortDiscovery bool `json:"portDiscovery"`
|
||||
|
||||
// Headless if set to true, generates headless Kubernetes services.
|
||||
// ref: https://kubernetes.io/docs/concepts/services-networking/service/#headless-services
|
||||
// +optional
|
||||
Headless bool `json:"headless,omitempty"`
|
||||
|
||||
// Timeout of the HTTP or gRPC request
|
||||
// +optional
|
||||
Timeout string `json:"timeout,omitempty"`
|
||||
@@ -218,6 +236,17 @@ type CanaryService struct {
|
||||
// Canary is the metadata to add to the canary service
|
||||
// +optional
|
||||
Canary *CustomMetadata `json:"canary,omitempty"`
|
||||
|
||||
// UnmanagedMetadata is a list of metadata keys that should be ignored by Flagger.
|
||||
// Flagger will not add, remove or change the value of these annotations.
|
||||
// +optional
|
||||
UnmanagedMetadata *UnmanagedMetadata `json:"unmanagedMetadata,omitempty"`
|
||||
}
|
||||
|
||||
// UnmanagedMetadata is a list of metadata keys that should be ignored by Flagger.
|
||||
type UnmanagedMetadata struct {
|
||||
Annotations []string `json:"annotations,omitempty"`
|
||||
Labels []string `json:"labels,omitempty"`
|
||||
}
|
||||
|
||||
// CanaryAnalysis is used to describe how the analysis should be done
|
||||
@@ -285,11 +314,33 @@ type CanaryAnalysis struct {
|
||||
type SessionAffinity struct {
|
||||
// CookieName is the key that will be used for the session affinity cookie.
|
||||
CookieName string `json:"cookieName,omitempty"`
|
||||
// MaxAge indicates the number of seconds until the session affinity cookie will expire.
|
||||
// ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes
|
||||
// Domain defines the host to which the cookie will be sent.
|
||||
// +optional
|
||||
Domain string `json:"domain,omitempty"`
|
||||
// HttpOnly forbids JavaScript from accessing the cookie, for example, through the Document.cookie property.
|
||||
// +optional
|
||||
HttpOnly bool `json:"httpOnly,omitempty"`
|
||||
// MaxAge indicates the number of seconds until the session affinity cookie will expire.
|
||||
// The default value is 86,400 seconds, i.e. a day.
|
||||
// +optional
|
||||
MaxAge int `json:"maxAge,omitempty"`
|
||||
// Partitioned indicates that the cookie should be stored using partitioned storage.
|
||||
// +optional
|
||||
Partitioned bool `json:"partitioned,omitempty"`
|
||||
// Path indicates the path that must exist in the requested URL for the browser to send the Cookie header.
|
||||
// +optional
|
||||
Path string `json:"path,omitempty"`
|
||||
// SameSite controls whether or not a cookie is sent with cross-site requests.
|
||||
// +optional
|
||||
// +kubebuilder:validation:Enum=Strict;Lax;None
|
||||
SameSite string `json:"sameSite,omitempty"`
|
||||
// Secure indicates that the cookie is sent to the server only when a request is made with the https: scheme (except on localhost)
|
||||
// +optional
|
||||
Secure bool `json:"secure,omitempty"`
|
||||
// PrimaryCookieName is the key that will be used for the primary session affinity cookie.
|
||||
// +optional
|
||||
PrimaryCookieName string `json:"primaryCookieName,omitempty"`
|
||||
}
|
||||
|
||||
// CanaryMetric holds the reference to metrics used for canary analysis
|
||||
@@ -398,6 +449,10 @@ type CanaryWebhook struct {
|
||||
// Number of retries for this webhook
|
||||
// +optional
|
||||
Retries int `json:"retries,omitempty"`
|
||||
|
||||
// Disable TLS verification for this webhook
|
||||
// +optional
|
||||
DisableTLS bool `json:"disableTLS,omitempty"`
|
||||
}
|
||||
|
||||
// CanaryWebhookPayload holds the deployment info and metadata sent to webhooks
|
||||
@@ -454,7 +509,15 @@ type LocalObjectReference struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type AutoscalerRefernce struct {
|
||||
func (l *LocalObjectReference) IsKnativeService() bool {
|
||||
if l.Kind == "Service" && l.APIVersion == "serving.knative.dev/v1" {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
type AutoscalerReference struct {
|
||||
// API version of the scaler
|
||||
// +required
|
||||
APIVersion string `json:"apiVersion,omitempty"`
|
||||
@@ -620,3 +683,57 @@ func (c *Canary) SkipAnalysis() bool {
|
||||
}
|
||||
return c.Spec.SkipAnalysis
|
||||
}
|
||||
|
||||
// DeploymentStrategy returns the deployment strategy based on canary analysis configuration
|
||||
func (c *Canary) DeploymentStrategy() string {
|
||||
analysis := c.GetAnalysis()
|
||||
if analysis == nil {
|
||||
return DeploymentStrategyCanary
|
||||
}
|
||||
|
||||
// A/B Testing: has match conditions and iterations
|
||||
if len(analysis.Match) > 0 && analysis.Iterations > 0 {
|
||||
return DeploymentStrategyABTesting
|
||||
}
|
||||
|
||||
// Blue/Green: has iterations but no match conditions
|
||||
if analysis.Iterations > 0 {
|
||||
return DeploymentStrategyBlueGreen
|
||||
}
|
||||
|
||||
// Canary Release: default (has maxWeight, stepWeight, or stepWeights)
|
||||
return DeploymentStrategyCanary
|
||||
}
|
||||
|
||||
// BuildCookie returns the cookie that should be used as the value of a Set-Cookie header
|
||||
func (s *SessionAffinity) BuildCookie(cookieName string, maxAge int) string {
|
||||
cookie := fmt.Sprintf("%s; %s=%d", cookieName, "Max-Age",
|
||||
maxAge,
|
||||
)
|
||||
|
||||
if s.Domain != "" {
|
||||
cookie += fmt.Sprintf("; %s=%s", "Domain", s.Domain)
|
||||
}
|
||||
|
||||
if s.HttpOnly {
|
||||
cookie += fmt.Sprintf("; %s", "HttpOnly")
|
||||
}
|
||||
|
||||
if s.Partitioned {
|
||||
cookie += fmt.Sprintf("; %s", "Partitioned")
|
||||
}
|
||||
|
||||
if s.Path != "" {
|
||||
cookie += fmt.Sprintf("; %s=%s", "Path", s.Path)
|
||||
}
|
||||
|
||||
if s.SameSite != "" {
|
||||
cookie += fmt.Sprintf("; %s=%s", "SameSite", s.SameSite)
|
||||
}
|
||||
|
||||
if s.Secure {
|
||||
cookie += fmt.Sprintf("; %s", "Secure")
|
||||
}
|
||||
|
||||
return cookie
|
||||
}
|
||||
|
||||
94
pkg/apis/flagger/v1beta1/canary_test.go
Normal file
94
pkg/apis/flagger/v1beta1/canary_test.go
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
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 v1beta1
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
istiov1alpha1 "github.com/fluxcd/flagger/pkg/apis/istio/common/v1alpha1"
|
||||
istiov1beta1 "github.com/fluxcd/flagger/pkg/apis/istio/v1beta1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCanary_GetDeploymentStrategy(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
analysis *CanaryAnalysis
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "canary strategy with maxWeight",
|
||||
analysis: &CanaryAnalysis{
|
||||
MaxWeight: 30,
|
||||
StepWeight: 10,
|
||||
},
|
||||
expected: DeploymentStrategyCanary,
|
||||
},
|
||||
{
|
||||
name: "canary strategy with stepWeights",
|
||||
analysis: &CanaryAnalysis{
|
||||
StepWeights: []int{10, 20, 30},
|
||||
},
|
||||
expected: DeploymentStrategyCanary,
|
||||
},
|
||||
{
|
||||
name: "blue-green strategy with iterations",
|
||||
analysis: &CanaryAnalysis{
|
||||
Iterations: 5,
|
||||
},
|
||||
expected: DeploymentStrategyBlueGreen,
|
||||
},
|
||||
{
|
||||
name: "ab-testing strategy with iterations and match",
|
||||
analysis: &CanaryAnalysis{
|
||||
Iterations: 10,
|
||||
Match: []istiov1beta1.HTTPMatchRequest{
|
||||
{
|
||||
Headers: map[string]istiov1alpha1.StringMatch{
|
||||
"x-canary": {
|
||||
Exact: "insider",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: DeploymentStrategyABTesting,
|
||||
},
|
||||
{
|
||||
name: "default to canary when analysis is nil",
|
||||
analysis: nil,
|
||||
expected: DeploymentStrategyCanary,
|
||||
},
|
||||
{
|
||||
name: "default to canary when analysis is empty",
|
||||
analysis: &CanaryAnalysis{},
|
||||
expected: DeploymentStrategyCanary,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
canary := &Canary{
|
||||
Spec: CanarySpec{
|
||||
Analysis: tt.analysis,
|
||||
},
|
||||
}
|
||||
result := canary.DeploymentStrategy()
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"text/template"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@@ -67,6 +68,10 @@ type MetricTemplateProvider struct {
|
||||
// +optional
|
||||
Address string `json:"address,omitempty"`
|
||||
|
||||
// Headers to be supplied to HTTP(S) request of this provider
|
||||
// +optional
|
||||
Headers http.Header `json:"headers,omitempty"`
|
||||
|
||||
// Secret reference containing the provider credentials
|
||||
// +optional
|
||||
SecretRef *corev1.LocalObjectReference `json:"secretRef,omitempty"`
|
||||
|
||||
@@ -7,6 +7,7 @@ const (
|
||||
IstioProvider string = "istio"
|
||||
SMIProvider string = "smi"
|
||||
ContourProvider string = "contour"
|
||||
KnativeProvider string = "knative"
|
||||
GlooProvider string = "gloo"
|
||||
NGINXProvider string = "nginx"
|
||||
KubernetesProvider string = "kubernetes"
|
||||
|
||||
@@ -78,6 +78,8 @@ type CanaryStatus struct {
|
||||
// +optional
|
||||
SessionAffinityCookie string `json:"sessionAffinityCookie,omitempty"`
|
||||
// +optional
|
||||
PrimarySessionAffinityCookie string `json:"primarySessionAffinityCookie,omitempty"`
|
||||
// +optional
|
||||
TrackedConfigs *map[string]string `json:"trackedConfigs,omitempty"`
|
||||
// +optional
|
||||
LastAppliedSpec string `json:"lastAppliedSpec,omitempty"`
|
||||
|
||||
@@ -22,6 +22,8 @@ limitations under the License.
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
http "net/http"
|
||||
|
||||
gatewayapiv1beta1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1"
|
||||
istiov1beta1 "github.com/fluxcd/flagger/pkg/apis/istio/v1beta1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
@@ -152,7 +154,7 @@ func (in *AlertProviderStatus) DeepCopy() *AlertProviderStatus {
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *AutoscalerRefernce) DeepCopyInto(out *AutoscalerRefernce) {
|
||||
func (in *AutoscalerReference) DeepCopyInto(out *AutoscalerReference) {
|
||||
*out = *in
|
||||
if in.PrimaryScalerQueries != nil {
|
||||
in, out := &in.PrimaryScalerQueries, &out.PrimaryScalerQueries
|
||||
@@ -169,12 +171,12 @@ func (in *AutoscalerRefernce) DeepCopyInto(out *AutoscalerRefernce) {
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoscalerRefernce.
|
||||
func (in *AutoscalerRefernce) DeepCopy() *AutoscalerRefernce {
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AutoscalerReference.
|
||||
func (in *AutoscalerReference) DeepCopy() *AutoscalerReference {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(AutoscalerRefernce)
|
||||
out := new(AutoscalerReference)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
@@ -450,6 +452,11 @@ func (in *CanaryService) DeepCopyInto(out *CanaryService) {
|
||||
*out = new(CustomMetadata)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.UnmanagedMetadata != nil {
|
||||
in, out := &in.UnmanagedMetadata, &out.UnmanagedMetadata
|
||||
*out = new(UnmanagedMetadata)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -469,7 +476,7 @@ func (in *CanarySpec) DeepCopyInto(out *CanarySpec) {
|
||||
out.TargetRef = in.TargetRef
|
||||
if in.AutoscalerRef != nil {
|
||||
in, out := &in.AutoscalerRef, &out.AutoscalerRef
|
||||
*out = new(AutoscalerRefernce)
|
||||
*out = new(AutoscalerReference)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.IngressRef != nil {
|
||||
@@ -810,6 +817,21 @@ func (in *MetricTemplateModel) DeepCopy() *MetricTemplateModel {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *MetricTemplateProvider) DeepCopyInto(out *MetricTemplateProvider) {
|
||||
*out = *in
|
||||
if in.Headers != nil {
|
||||
in, out := &in.Headers, &out.Headers
|
||||
*out = make(http.Header, len(*in))
|
||||
for key, val := range *in {
|
||||
var outVal []string
|
||||
if val == nil {
|
||||
(*out)[key] = nil
|
||||
} else {
|
||||
in, out := &val, &outVal
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
(*out)[key] = outVal
|
||||
}
|
||||
}
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(v1.LocalObjectReference)
|
||||
@@ -909,3 +931,29 @@ func (in *SessionAffinity) DeepCopy() *SessionAffinity {
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *UnmanagedMetadata) DeepCopyInto(out *UnmanagedMetadata) {
|
||||
*out = *in
|
||||
if in.Annotations != nil {
|
||||
in, out := &in.Annotations, &out.Annotations
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Labels != nil {
|
||||
in, out := &in.Labels, &out.Labels
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UnmanagedMetadata.
|
||||
func (in *UnmanagedMetadata) DeepCopy() *UnmanagedMetadata {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(UnmanagedMetadata)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -20,11 +20,12 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +genclient
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:categories=gateway-api
|
||||
// +kubebuilder:subresource:status
|
||||
// +kubebuilder:storageversion
|
||||
// +kubebuilder:printcolumn:name="Hostnames",type=string,JSONPath=`.spec.hostnames`
|
||||
// +kubebuilder:printcolumn:name="Age",type=date,JSONPath=`.metadata.creationTimestamp`
|
||||
|
||||
@@ -33,13 +34,16 @@ import (
|
||||
// used to specify additional processing steps. Backends specify where matching
|
||||
// requests should be routed.
|
||||
type HTTPRoute struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// +optional
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// Spec defines the desired state of HTTPRoute.
|
||||
// +required
|
||||
Spec HTTPRouteSpec `json:"spec"`
|
||||
|
||||
// Status defines the current state of HTTPRoute.
|
||||
// +optional
|
||||
Status HTTPRouteStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
@@ -112,14 +116,18 @@ type HTTPRouteSpec struct {
|
||||
// Support: Core
|
||||
//
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
// +kubebuilder:validation:MaxItems=16
|
||||
Hostnames []Hostname `json:"hostnames,omitempty"`
|
||||
|
||||
// Rules are a list of HTTP matchers, filters and actions.
|
||||
//
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
// <gateway:experimental:validation:XValidation:message="Rule name must be unique within the route",rule="self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) && l1.name == l2.name))">
|
||||
// +kubebuilder:validation:MaxItems=16
|
||||
// +kubebuilder:default={{matches: {{path: {type: "PathPrefix", value: "/"}}}}}
|
||||
// +kubebuilder:validation:XValidation:message="While 16 rules and 64 matches per rule are allowed, the total number of matches across all rules in a route must be less than 128",rule="(self.size() > 0 ? self[0].matches.size() : 0) + (self.size() > 1 ? self[1].matches.size() : 0) + (self.size() > 2 ? self[2].matches.size() : 0) + (self.size() > 3 ? self[3].matches.size() : 0) + (self.size() > 4 ? self[4].matches.size() : 0) + (self.size() > 5 ? self[5].matches.size() : 0) + (self.size() > 6 ? self[6].matches.size() : 0) + (self.size() > 7 ? self[7].matches.size() : 0) + (self.size() > 8 ? self[8].matches.size() : 0) + (self.size() > 9 ? self[9].matches.size() : 0) + (self.size() > 10 ? self[10].matches.size() : 0) + (self.size() > 11 ? self[11].matches.size() : 0) + (self.size() > 12 ? self[12].matches.size() : 0) + (self.size() > 13 ? self[13].matches.size() : 0) + (self.size() > 14 ? self[14].matches.size() : 0) + (self.size() > 15 ? self[15].matches.size() : 0) <= 128"
|
||||
Rules []HTTPRouteRule `json:"rules,omitempty"`
|
||||
}
|
||||
|
||||
@@ -133,6 +141,12 @@ type HTTPRouteSpec struct {
|
||||
// +kubebuilder:validation:XValidation:message="Within backendRefs, when using RequestRedirect filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified",rule="(has(self.backendRefs) && self.backendRefs.exists_one(b, (has(b.filters) && b.filters.exists_one(f, has(f.requestRedirect) && has(f.requestRedirect.path) && f.requestRedirect.path.type == 'ReplacePrefixMatch' && has(f.requestRedirect.path.replacePrefixMatch))) )) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != 'PathPrefix') ? false : true) : true"
|
||||
// +kubebuilder:validation:XValidation:message="Within backendRefs, When using URLRewrite filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified",rule="(has(self.backendRefs) && self.backendRefs.exists_one(b, (has(b.filters) && b.filters.exists_one(f, has(f.urlRewrite) && has(f.urlRewrite.path) && f.urlRewrite.path.type == 'ReplacePrefixMatch' && has(f.urlRewrite.path.replacePrefixMatch))) )) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != 'PathPrefix') ? false : true) : true"
|
||||
type HTTPRouteRule struct {
|
||||
// Name is the name of the route rule. This name MUST be unique within a Route if it is set.
|
||||
//
|
||||
// Support: Extended
|
||||
// +optional
|
||||
Name *SectionName `json:"name,omitempty"`
|
||||
|
||||
// Matches define conditions used for matching the rule against incoming
|
||||
// HTTP requests. Each match is independent, i.e. this rule will be matched
|
||||
// if **any** one of the matches is satisfied.
|
||||
@@ -191,15 +205,27 @@ type HTTPRouteRule struct {
|
||||
// parent a request is coming from, a HTTP 404 status code MUST be returned.
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:validation:MaxItems=8
|
||||
// +listType=atomic
|
||||
// +kubebuilder:validation:MaxItems=64
|
||||
// +kubebuilder:default={{path:{ type: "PathPrefix", value: "/"}}}
|
||||
Matches []HTTPRouteMatch `json:"matches,omitempty"`
|
||||
|
||||
// Filters define the filters that are applied to requests that match
|
||||
// this rule.
|
||||
//
|
||||
// The effects of ordering of multiple behaviors are currently unspecified.
|
||||
// This can change in the future based on feedback during the alpha stage.
|
||||
// Wherever possible, implementations SHOULD implement filters in the order
|
||||
// they are specified.
|
||||
//
|
||||
// Implementations MAY choose to implement this ordering strictly, rejecting
|
||||
// any combination or order of filters that cannot be supported. If implementations
|
||||
// choose a strict interpretation of filter ordering, they MUST clearly document
|
||||
// that behavior.
|
||||
//
|
||||
// To reject an invalid combination or order of filters, implementations SHOULD
|
||||
// consider the Route Rules with this configuration invalid. If all Route Rules
|
||||
// in a Route are invalid, the entire Route would be considered invalid. If only
|
||||
// a portion of Route Rules are invalid, implementations MUST set the
|
||||
// "PartiallyInvalid" condition for the Route.
|
||||
//
|
||||
// Conformance-levels at this level are defined based on the type of filter:
|
||||
//
|
||||
@@ -213,7 +239,7 @@ type HTTPRouteRule struct {
|
||||
//
|
||||
// All filters are expected to be compatible with each other except for the
|
||||
// URLRewrite and RequestRedirect filters, which may not be combined. If an
|
||||
// implementation can not support other combinations of filters, they must clearly
|
||||
// implementation cannot support other combinations of filters, they must clearly
|
||||
// document that limitation. In cases where incompatible or unsupported
|
||||
// filters are specified and cause the `Accepted` condition to be set to status
|
||||
// `False`, implementations may use the `IncompatibleFilters` reason to specify
|
||||
@@ -222,6 +248,7 @@ type HTTPRouteRule struct {
|
||||
// Support: Core
|
||||
//
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
// +kubebuilder:validation:MaxItems=16
|
||||
// +kubebuilder:validation:XValidation:message="May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both",rule="!(self.exists(f, f.type == 'RequestRedirect') && self.exists(f, f.type == 'URLRewrite'))"
|
||||
// +kubebuilder:validation:XValidation:message="RequestHeaderModifier filter cannot be repeated",rule="self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1"
|
||||
@@ -253,6 +280,11 @@ type HTTPRouteRule struct {
|
||||
// invalid, 50 percent of traffic must receive a 500. Implementations may
|
||||
// choose how that 50 percent is determined.
|
||||
//
|
||||
// When a HTTPBackendRef refers to a Service that has no ready endpoints,
|
||||
// implementations SHOULD return a 503 for requests to that backend instead.
|
||||
// If an implementation chooses to do this, all of the above rules for 500 responses
|
||||
// MUST also apply for responses that return a 503.
|
||||
//
|
||||
// Support: Core for Kubernetes Service
|
||||
//
|
||||
// Support: Extended for Kubernetes ServiceImport
|
||||
@@ -262,6 +294,7 @@ type HTTPRouteRule struct {
|
||||
// Support for weight: Core
|
||||
//
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
// +kubebuilder:validation:MaxItems=16
|
||||
BackendRefs []HTTPBackendRef `json:"backendRefs,omitempty"`
|
||||
|
||||
@@ -270,13 +303,28 @@ type HTTPRouteRule struct {
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
// <gateway:experimental>
|
||||
Timeouts *HTTPRouteTimeouts `json:"timeouts,omitempty"`
|
||||
|
||||
// Retry defines the configuration for when to retry an HTTP request.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
// <gateway:experimental>
|
||||
Retry *HTTPRouteRetry `json:"retry,omitempty"`
|
||||
|
||||
// SessionPersistence defines and configures session persistence
|
||||
// for the route rule.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
// <gateway:experimental>
|
||||
SessionPersistence *SessionPersistence `json:"sessionPersistence,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPRouteTimeouts defines timeouts that can be configured for an HTTPRoute.
|
||||
// Timeout values are represented with Gateway API Duration formatting.
|
||||
// Specifying a zero value such as "0s" is interpreted as no timeout.
|
||||
//
|
||||
// +kubebuilder:validation:XValidation:message="backendRequest timeout cannot be longer than request timeout",rule="!(has(self.request) && has(self.backendRequest) && duration(self.request) != duration('0s') && duration(self.backendRequest) > duration(self.request))"
|
||||
type HTTPRouteTimeouts struct {
|
||||
@@ -288,12 +336,18 @@ type HTTPRouteTimeouts struct {
|
||||
// `HTTPRoute` will cause a timeout if a client request is taking longer than 10 seconds
|
||||
// to complete.
|
||||
//
|
||||
// Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout
|
||||
// completely. Implementations that cannot completely disable the timeout MUST
|
||||
// instead interpret the zero duration as the longest possible value to which
|
||||
// the timeout can be set.
|
||||
//
|
||||
// This timeout is intended to cover as close to the whole request-response transaction
|
||||
// as possible although an implementation MAY choose to start the timeout after the entire
|
||||
// request stream has been received instead of immediately after the transaction is
|
||||
// initiated by the client.
|
||||
//
|
||||
// When this field is unspecified, request timeout behavior is implementation-specific.
|
||||
// The value of Request is a Gateway API Duration string as defined by GEP-2257. When this
|
||||
// field is unspecified, request timeout behavior is implementation-specific.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
@@ -304,12 +358,19 @@ type HTTPRouteTimeouts struct {
|
||||
// to a backend. This covers the time from when the request first starts being
|
||||
// sent from the gateway to when the full response has been received from the backend.
|
||||
//
|
||||
// Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout
|
||||
// completely. Implementations that cannot completely disable the timeout MUST
|
||||
// instead interpret the zero duration as the longest possible value to which
|
||||
// the timeout can be set.
|
||||
//
|
||||
// An entire client HTTP transaction with a gateway, covered by the Request timeout,
|
||||
// may result in more than one call from the gateway to the destination backend,
|
||||
// for example, if automatic retries are supported.
|
||||
//
|
||||
// Because the Request timeout encompasses the BackendRequest timeout, the value of
|
||||
// BackendRequest must be <= the value of Request timeout.
|
||||
// The value of BackendRequest must be a Gateway API Duration string as defined by
|
||||
// GEP-2257. When this field is unspecified, its behavior is implementation-specific;
|
||||
// when specified, the value of BackendRequest must be no more than the value of the
|
||||
// Request timeout (since the Request timeout encompasses the BackendRequest timeout).
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
@@ -317,6 +378,96 @@ type HTTPRouteTimeouts struct {
|
||||
BackendRequest *Duration `json:"backendRequest,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPRouteRetry defines retry configuration for an HTTPRoute.
|
||||
//
|
||||
// Implementations SHOULD retry on connection errors (disconnect, reset, timeout,
|
||||
// TCP failure) if a retry stanza is configured.
|
||||
type HTTPRouteRetry struct {
|
||||
// Codes defines the HTTP response status codes for which a backend request
|
||||
// should be retried.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
Codes []HTTPRouteRetryStatusCode `json:"codes,omitempty"`
|
||||
|
||||
// Attempts specifies the maximum number of times an individual request
|
||||
// from the gateway to a backend should be retried.
|
||||
//
|
||||
// If the maximum number of retries has been attempted without a successful
|
||||
// response from the backend, the Gateway MUST return an error.
|
||||
//
|
||||
// When this field is unspecified, the number of times to attempt to retry
|
||||
// a backend request is implementation-specific.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
Attempts *int `json:"attempts,omitempty"`
|
||||
|
||||
// Backoff specifies the minimum duration a Gateway should wait between
|
||||
// retry attempts and is represented in Gateway API Duration formatting.
|
||||
//
|
||||
// For example, setting the `rules[].retry.backoff` field to the value
|
||||
// `100ms` will cause a backend request to first be retried approximately
|
||||
// 100 milliseconds after timing out or receiving a response code configured
|
||||
// to be retryable.
|
||||
//
|
||||
// An implementation MAY use an exponential or alternative backoff strategy
|
||||
// for subsequent retry attempts, MAY cap the maximum backoff duration to
|
||||
// some amount greater than the specified minimum, and MAY add arbitrary
|
||||
// jitter to stagger requests, as long as unsuccessful backend requests are
|
||||
// not retried before the configured minimum duration.
|
||||
//
|
||||
// If a Request timeout (`rules[].timeouts.request`) is configured on the
|
||||
// route, the entire duration of the initial request and any retry attempts
|
||||
// MUST not exceed the Request timeout duration. If any retry attempts are
|
||||
// still in progress when the Request timeout duration has been reached,
|
||||
// these SHOULD be canceled if possible and the Gateway MUST immediately
|
||||
// return a timeout error.
|
||||
//
|
||||
// If a BackendRequest timeout (`rules[].timeouts.backendRequest`) is
|
||||
// configured on the route, any retry attempts which reach the configured
|
||||
// BackendRequest timeout duration without a response SHOULD be canceled if
|
||||
// possible and the Gateway should wait for at least the specified backoff
|
||||
// duration before attempting to retry the backend request again.
|
||||
//
|
||||
// If a BackendRequest timeout is _not_ configured on the route, retry
|
||||
// attempts MAY time out after an implementation default duration, or MAY
|
||||
// remain pending until a configured Request timeout or implementation
|
||||
// default duration for total request time is reached.
|
||||
//
|
||||
// When this field is unspecified, the time to wait between retry attempts
|
||||
// is implementation-specific.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
Backoff *Duration `json:"backoff,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPRouteRetryStatusCode defines an HTTP response status code for
|
||||
// which a backend request should be retried.
|
||||
//
|
||||
// Implementations MUST support the following status codes as retryable:
|
||||
//
|
||||
// * 500
|
||||
// * 502
|
||||
// * 503
|
||||
// * 504
|
||||
//
|
||||
// Implementations MAY support specifying additional discrete values in the
|
||||
// 500-599 range.
|
||||
//
|
||||
// Implementations MAY support specifying discrete values in the 400-499 range,
|
||||
// which are often inadvisable to retry.
|
||||
//
|
||||
// +kubebuilder:validation:Minimum:=400
|
||||
// +kubebuilder:validation:Maximum:=599
|
||||
// <gateway:experimental>
|
||||
type HTTPRouteRetryStatusCode int
|
||||
|
||||
// PathMatchType specifies the semantics of how HTTP paths should be compared.
|
||||
// Valid PathMatchType values, along with their support levels, are:
|
||||
//
|
||||
@@ -346,7 +497,7 @@ const (
|
||||
PathMatchExact PathMatchType = "Exact"
|
||||
|
||||
// Matches based on a URL path prefix split by `/`. Matching is
|
||||
// case sensitive and done on a path element by element basis. A
|
||||
// case-sensitive and done on a path element by element basis. A
|
||||
// path element refers to the list of labels in the path split by
|
||||
// the `/` separator. When specified, a trailing `/` is ignored.
|
||||
//
|
||||
@@ -455,7 +606,7 @@ type HTTPHeaderMatch struct {
|
||||
Type *HeaderMatchType `json:"type,omitempty"`
|
||||
|
||||
// Name is the name of the HTTP Header to be matched. Name matching MUST be
|
||||
// case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2).
|
||||
// case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2).
|
||||
//
|
||||
// If multiple entries specify equivalent header names, only the first
|
||||
// entry with an equivalent name MUST be considered for a match. Subsequent
|
||||
@@ -468,12 +619,14 @@ type HTTPHeaderMatch struct {
|
||||
// Generally, proxies should follow the guidance from the RFC:
|
||||
// https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding
|
||||
// processing a repeated header, with special handling for "Set-Cookie".
|
||||
// +required
|
||||
Name HTTPHeaderName `json:"name"`
|
||||
|
||||
// Value is the value of HTTP Header to be matched.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=4096
|
||||
// +required
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
@@ -535,12 +688,14 @@ type HTTPQueryParamMatch struct {
|
||||
//
|
||||
// Users SHOULD NOT route traffic based on repeated query params to guard
|
||||
// themselves against potential differences in the implementations.
|
||||
// +required
|
||||
Name HTTPHeaderName `json:"name"`
|
||||
|
||||
// Value is the value of HTTP query param to be matched.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=1024
|
||||
// +required
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
@@ -560,6 +715,9 @@ type HTTPQueryParamMatch struct {
|
||||
// +kubebuilder:validation:Enum=GET;HEAD;POST;PUT;DELETE;CONNECT;OPTIONS;TRACE;PATCH
|
||||
type HTTPMethod string
|
||||
|
||||
// +kubebuilder:validation:Enum=GET;HEAD;POST;PUT;DELETE;CONNECT;OPTIONS;TRACE;PATCH;*
|
||||
type HTTPMethodWithWildcard string
|
||||
|
||||
const (
|
||||
HTTPMethodGet HTTPMethod = "GET"
|
||||
HTTPMethodHead HTTPMethod = "HEAD"
|
||||
@@ -646,6 +804,10 @@ type HTTPRouteMatch struct {
|
||||
// +kubebuilder:validation:XValidation:message="filter.requestRedirect must be specified for RequestRedirect filter.type",rule="!(!has(self.requestRedirect) && self.type == 'RequestRedirect')"
|
||||
// +kubebuilder:validation:XValidation:message="filter.urlRewrite must be nil if the filter.type is not URLRewrite",rule="!(has(self.urlRewrite) && self.type != 'URLRewrite')"
|
||||
// +kubebuilder:validation:XValidation:message="filter.urlRewrite must be specified for URLRewrite filter.type",rule="!(!has(self.urlRewrite) && self.type == 'URLRewrite')"
|
||||
// <gateway:experimental:validation:XValidation:message="filter.cors must be nil if the filter.type is not CORS",rule="!(has(self.cors) && self.type != 'CORS')">
|
||||
// <gateway:experimental:validation:XValidation:message="filter.cors must be specified for CORS filter.type",rule="!(!has(self.cors) && self.type == 'CORS')">
|
||||
// <gateway:experimental:validation:XValidation:message="filter.externalAuth must be nil if the filter.type is not ExternalAuth",rule="!(has(self.externalAuth) && self.type != 'ExternalAuth')">
|
||||
// <gateway:experimental:validation:XValidation:message="filter.externalAuth must be specified for ExternalAuth filter.type",rule="!(!has(self.externalAuth) && self.type == 'ExternalAuth')">
|
||||
// +kubebuilder:validation:XValidation:message="filter.extensionRef must be nil if the filter.type is not ExtensionRef",rule="!(has(self.extensionRef) && self.type != 'ExtensionRef')"
|
||||
// +kubebuilder:validation:XValidation:message="filter.extensionRef must be specified for ExtensionRef filter.type",rule="!(!has(self.extensionRef) && self.type == 'ExtensionRef')"
|
||||
type HTTPRouteFilter struct {
|
||||
@@ -684,6 +846,8 @@ type HTTPRouteFilter struct {
|
||||
//
|
||||
// +unionDiscriminator
|
||||
// +kubebuilder:validation:Enum=RequestHeaderModifier;ResponseHeaderModifier;RequestMirror;RequestRedirect;URLRewrite;ExtensionRef
|
||||
// <gateway:experimental:validation:Enum=RequestHeaderModifier;ResponseHeaderModifier;RequestMirror;RequestRedirect;URLRewrite;ExtensionRef;CORS;ExternalAuth>
|
||||
// +required
|
||||
Type HTTPRouteFilterType `json:"type"`
|
||||
|
||||
// RequestHeaderModifier defines a schema for a filter that modifies request
|
||||
@@ -713,6 +877,8 @@ type HTTPRouteFilter struct {
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
//
|
||||
// +kubebuilder:validation:XValidation:message="Only one of percent or fraction may be specified in HTTPRequestMirrorFilter",rule="!(has(self.percent) && has(self.fraction))"
|
||||
RequestMirror *HTTPRequestMirrorFilter `json:"requestMirror,omitempty"`
|
||||
|
||||
// RequestRedirect defines a schema for a filter that responds to the
|
||||
@@ -730,6 +896,28 @@ type HTTPRouteFilter struct {
|
||||
// +optional
|
||||
URLRewrite *HTTPURLRewriteFilter `json:"urlRewrite,omitempty"`
|
||||
|
||||
// CORS defines a schema for a filter that responds to the
|
||||
// cross-origin request based on HTTP response header.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
// <gateway:experimental>
|
||||
CORS *HTTPCORSFilter `json:"cors,omitempty"`
|
||||
|
||||
// ExternalAuth configures settings related to sending request details
|
||||
// to an external auth service. The external service MUST authenticate
|
||||
// the request, and MAY authorize the request as well.
|
||||
//
|
||||
// If there is any problem communicating with the external service,
|
||||
// this filter MUST fail closed.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
// <gateway:experimental>
|
||||
ExternalAuth *HTTPExternalAuthFilter `json:"externalAuth,omitempty"`
|
||||
|
||||
// ExtensionRef is an optional, implementation-specific extension to the
|
||||
// "filter" behavior. For example, resource "myroutefilter" in group
|
||||
// "networking.example.net"). ExtensionRef MUST NOT be used for core and
|
||||
@@ -792,6 +980,27 @@ const (
|
||||
// Support in HTTPBackendRef: Extended
|
||||
HTTPRouteFilterRequestMirror HTTPRouteFilterType = "RequestMirror"
|
||||
|
||||
// HTTPRouteFilterCORS can be used to add CORS headers to an
|
||||
// HTTP response before it is sent to the client.
|
||||
//
|
||||
// Support in HTTPRouteRule: Extended
|
||||
//
|
||||
// Support in HTTPBackendRef: Extended
|
||||
// <gateway:experimental>
|
||||
HTTPRouteFilterCORS HTTPRouteFilterType = "CORS"
|
||||
|
||||
// HTTPRouteFilterExternalAuth can be used to configure a Gateway implementation
|
||||
// to call out to an external Auth server, which MUST perform Authentication
|
||||
// and MAY perform Authorization on the matched request before the request
|
||||
// is forwarded to the backend.
|
||||
//
|
||||
// Support in HTTPRouteRule: Extended
|
||||
//
|
||||
// Feature Name: HTTPRouteExternalAuth
|
||||
//
|
||||
// <gateway:experimental>
|
||||
HTTPRouteFilterExternalAuth HTTPRouteFilterType = "ExternalAuth"
|
||||
|
||||
// HTTPRouteFilterExtensionRef should be used for configuring custom
|
||||
// HTTP filters.
|
||||
//
|
||||
@@ -804,28 +1013,30 @@ const (
|
||||
// HTTPHeader represents an HTTP Header name and value as defined by RFC 7230.
|
||||
type HTTPHeader struct {
|
||||
// Name is the name of the HTTP Header to be matched. Name matching MUST be
|
||||
// case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2).
|
||||
// case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2).
|
||||
//
|
||||
// If multiple entries specify equivalent header names, the first entry with
|
||||
// an equivalent name MUST be considered for a match. Subsequent entries
|
||||
// with an equivalent header name MUST be ignored. Due to the
|
||||
// case-insensitivity of header names, "foo" and "Foo" are considered
|
||||
// equivalent.
|
||||
// +required
|
||||
Name HTTPHeaderName `json:"name"`
|
||||
|
||||
// Value is the value of HTTP Header to be matched.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=4096
|
||||
// +required
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// HTTPHeaderFilter defines a filter that modifies the headers of an HTTP
|
||||
// request or response. Only one action for a given header name is permitted.
|
||||
// Filters specifying multiple actions of the same or different type for any one
|
||||
// header name are invalid and will be rejected by the webhook if installed.
|
||||
// Configuration to set or add multiple values for a header must use RFC 7230
|
||||
// header value formatting, separating each value with a comma.
|
||||
// request or response. Only one action for a given header name is
|
||||
// permitted. Filters specifying multiple actions of the same or different
|
||||
// type for any one header name are invalid. Configuration to set or add
|
||||
// multiple values for a header must use RFC 7230 header value formatting,
|
||||
// separating each value with a comma.
|
||||
type HTTPHeaderFilter struct {
|
||||
// Set overwrites the request with the given header (name, value)
|
||||
// before the action.
|
||||
@@ -935,6 +1146,7 @@ type HTTPPathModifier struct {
|
||||
// Reason of `UnsupportedValue`.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=ReplaceFullPath;ReplacePrefixMatch
|
||||
// +required
|
||||
Type HTTPPathModifierType `json:"type"`
|
||||
|
||||
// ReplaceFullPath specifies the value with which to replace the full path
|
||||
@@ -1042,6 +1254,9 @@ type HTTPRequestRedirectFilter struct {
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
//
|
||||
// +kubebuilder:validation:Minimum=1
|
||||
// +kubebuilder:validation:Maximum=65535
|
||||
Port *PortNumber `json:"port,omitempty"`
|
||||
|
||||
// StatusCode is the HTTP status code to be used in response.
|
||||
@@ -1108,7 +1323,438 @@ type HTTPRequestMirrorFilter struct {
|
||||
// Support: Extended for Kubernetes Service
|
||||
//
|
||||
// Support: Implementation-specific for any other resource
|
||||
// +required
|
||||
BackendRef BackendObjectReference `json:"backendRef"`
|
||||
|
||||
// Percent represents the percentage of requests that should be
|
||||
// mirrored to BackendRef. Its minimum value is 0 (indicating 0% of
|
||||
// requests) and its maximum value is 100 (indicating 100% of requests).
|
||||
//
|
||||
// Only one of Fraction or Percent may be specified. If neither field
|
||||
// is specified, 100% of requests will be mirrored.
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:validation:Minimum=0
|
||||
// +kubebuilder:validation:Maximum=100
|
||||
Percent *int32 `json:"percent,omitempty"`
|
||||
|
||||
// Fraction represents the fraction of requests that should be
|
||||
// mirrored to BackendRef.
|
||||
//
|
||||
// Only one of Fraction or Percent may be specified. If neither field
|
||||
// is specified, 100% of requests will be mirrored.
|
||||
//
|
||||
// +optional
|
||||
Fraction *Fraction `json:"fraction,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPCORSFilter defines a filter that that configures Cross-Origin Request
|
||||
// Sharing (CORS).
|
||||
type HTTPCORSFilter struct {
|
||||
// AllowOrigins indicates whether the response can be shared with requested
|
||||
// resource from the given `Origin`.
|
||||
//
|
||||
// The `Origin` consists of a scheme and a host, with an optional port, and
|
||||
// takes the form `<scheme>://<host>(:<port>)`.
|
||||
//
|
||||
// Valid values for scheme are: `http` and `https`.
|
||||
//
|
||||
// Valid values for port are any integer between 1 and 65535 (the list of
|
||||
// available TCP/UDP ports). Note that, if not included, port `80` is
|
||||
// assumed for `http` scheme origins, and port `443` is assumed for `https`
|
||||
// origins. This may affect origin matching.
|
||||
//
|
||||
// The host part of the origin may contain the wildcard character `*`. These
|
||||
// wildcard characters behave as follows:
|
||||
//
|
||||
// * `*` is a greedy match to the _left_, including any number of
|
||||
// DNS labels to the left of its position. This also means that
|
||||
// `*` will include any number of period `.` characters to the
|
||||
// left of its position.
|
||||
// * A wildcard by itself matches all hosts.
|
||||
//
|
||||
// An origin value that includes _only_ the `*` character indicates requests
|
||||
// from all `Origin`s are allowed.
|
||||
//
|
||||
// When the `AllowOrigins` field is configured with multiple origins, it
|
||||
// means the server supports clients from multiple origins. If the request
|
||||
// `Origin` matches the configured allowed origins, the gateway must return
|
||||
// the given `Origin` and sets value of the header
|
||||
// `Access-Control-Allow-Origin` same as the `Origin` header provided by the
|
||||
// client.
|
||||
//
|
||||
// The status code of a successful response to a "preflight" request is
|
||||
// always an OK status (i.e., 204 or 200).
|
||||
//
|
||||
// If the request `Origin` does not match the configured allowed origins,
|
||||
// the gateway returns 204/200 response but doesn't set the relevant
|
||||
// cross-origin response headers. Alternatively, the gateway responds with
|
||||
// 403 status to the "preflight" request is denied, coupled with omitting
|
||||
// the CORS headers. The cross-origin request fails on the client side.
|
||||
// Therefore, the client doesn't attempt the actual cross-origin request.
|
||||
//
|
||||
// The `Access-Control-Allow-Origin` response header can only use `*`
|
||||
// wildcard as value when the `AllowCredentials` field is false or omitted.
|
||||
//
|
||||
// When the `AllowCredentials` field is true and `AllowOrigins` field
|
||||
// specified with the `*` wildcard, the gateway must return a single origin
|
||||
// in the value of the `Access-Control-Allow-Origin` response header,
|
||||
// instead of specifying the `*` wildcard. The value of the header
|
||||
// `Access-Control-Allow-Origin` is same as the `Origin` header provided by
|
||||
// the client.
|
||||
//
|
||||
// Support: Extended
|
||||
// +listType=set
|
||||
// +kubebuilder:validation:MaxItems=64
|
||||
// +kubebuilder:validation:XValidation:message="AllowOrigins cannot contain '*' alongside other origins",rule="!('*' in self && self.size() > 1)"
|
||||
// +optional
|
||||
AllowOrigins []CORSOrigin `json:"allowOrigins,omitempty"`
|
||||
|
||||
// AllowCredentials indicates whether the actual cross-origin request allows
|
||||
// to include credentials.
|
||||
//
|
||||
// When set to true, the gateway will include the `Access-Control-Allow-Credentials`
|
||||
// response header with value true (case-sensitive).
|
||||
//
|
||||
// When set to false or omitted the gateway will omit the header
|
||||
// `Access-Control-Allow-Credentials` entirely (this is the standard CORS
|
||||
// behavior).
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
AllowCredentials *bool `json:"allowCredentials,omitempty"`
|
||||
|
||||
// AllowMethods indicates which HTTP methods are supported for accessing the
|
||||
// requested resource.
|
||||
//
|
||||
// Valid values are any method defined by RFC9110, along with the special
|
||||
// value `*`, which represents all HTTP methods are allowed.
|
||||
//
|
||||
// Method names are case sensitive, so these values are also case-sensitive.
|
||||
// (See https://www.rfc-editor.org/rfc/rfc2616#section-5.1.1)
|
||||
//
|
||||
// Multiple method names in the value of the `Access-Control-Allow-Methods`
|
||||
// response header are separated by a comma (",").
|
||||
//
|
||||
// A CORS-safelisted method is a method that is `GET`, `HEAD`, or `POST`.
|
||||
// (See https://fetch.spec.whatwg.org/#cors-safelisted-method) The
|
||||
// CORS-safelisted methods are always allowed, regardless of whether they
|
||||
// are specified in the `AllowMethods` field.
|
||||
//
|
||||
// When the `AllowMethods` field is configured with one or more methods, the
|
||||
// gateway must return the `Access-Control-Allow-Methods` response header
|
||||
// which value is present in the `AllowMethods` field.
|
||||
//
|
||||
// If the HTTP method of the `Access-Control-Request-Method` request header
|
||||
// is not included in the list of methods specified by the response header
|
||||
// `Access-Control-Allow-Methods`, it will present an error on the client
|
||||
// side.
|
||||
//
|
||||
// The `Access-Control-Allow-Methods` response header can only use `*`
|
||||
// wildcard as value when the `AllowCredentials` field is false or omitted.
|
||||
//
|
||||
// When the `AllowCredentials` field is true and `AllowMethods` field
|
||||
// specified with the `*` wildcard, the gateway must specify one HTTP method
|
||||
// in the value of the Access-Control-Allow-Methods response header. The
|
||||
// value of the header `Access-Control-Allow-Methods` is same as the
|
||||
// `Access-Control-Request-Method` header provided by the client. If the
|
||||
// header `Access-Control-Request-Method` is not included in the request,
|
||||
// the gateway will omit the `Access-Control-Allow-Methods` response header,
|
||||
// instead of specifying the `*` wildcard. A Gateway implementation may
|
||||
// choose to add implementation-specific default methods.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +listType=set
|
||||
// +kubebuilder:validation:MaxItems=9
|
||||
// +kubebuilder:validation:XValidation:message="AllowMethods cannot contain '*' alongside other methods",rule="!('*' in self && self.size() > 1)"
|
||||
// +optional
|
||||
AllowMethods []HTTPMethodWithWildcard `json:"allowMethods,omitempty"`
|
||||
|
||||
// AllowHeaders indicates which HTTP request headers are supported for
|
||||
// accessing the requested resource.
|
||||
//
|
||||
// Header names are not case sensitive.
|
||||
//
|
||||
// Multiple header names in the value of the `Access-Control-Allow-Headers`
|
||||
// response header are separated by a comma (",").
|
||||
//
|
||||
// When the `AllowHeaders` field is configured with one or more headers, the
|
||||
// gateway must return the `Access-Control-Allow-Headers` response header
|
||||
// which value is present in the `AllowHeaders` field.
|
||||
//
|
||||
// If any header name in the `Access-Control-Request-Headers` request header
|
||||
// is not included in the list of header names specified by the response
|
||||
// header `Access-Control-Allow-Headers`, it will present an error on the
|
||||
// client side.
|
||||
//
|
||||
// If any header name in the `Access-Control-Allow-Headers` response header
|
||||
// does not recognize by the client, it will also occur an error on the
|
||||
// client side.
|
||||
//
|
||||
// A wildcard indicates that the requests with all HTTP headers are allowed.
|
||||
// The `Access-Control-Allow-Headers` response header can only use `*`
|
||||
// wildcard as value when the `AllowCredentials` field is false or omitted.
|
||||
//
|
||||
// When the `AllowCredentials` field is true and `AllowHeaders` field
|
||||
// specified with the `*` wildcard, the gateway must specify one or more
|
||||
// HTTP headers in the value of the `Access-Control-Allow-Headers` response
|
||||
// header. The value of the header `Access-Control-Allow-Headers` is same as
|
||||
// the `Access-Control-Request-Headers` header provided by the client. If
|
||||
// the header `Access-Control-Request-Headers` is not included in the
|
||||
// request, the gateway will omit the `Access-Control-Allow-Headers`
|
||||
// response header, instead of specifying the `*` wildcard. A Gateway
|
||||
// implementation may choose to add implementation-specific default headers.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +listType=set
|
||||
// +kubebuilder:validation:MaxItems=64
|
||||
// +optional
|
||||
AllowHeaders []HTTPHeaderName `json:"allowHeaders,omitempty"`
|
||||
|
||||
// ExposeHeaders indicates which HTTP response headers can be exposed
|
||||
// to client-side scripts in response to a cross-origin request.
|
||||
//
|
||||
// A CORS-safelisted response header is an HTTP header in a CORS response
|
||||
// that it is considered safe to expose to the client scripts.
|
||||
// The CORS-safelisted response headers include the following headers:
|
||||
// `Cache-Control`
|
||||
// `Content-Language`
|
||||
// `Content-Length`
|
||||
// `Content-Type`
|
||||
// `Expires`
|
||||
// `Last-Modified`
|
||||
// `Pragma`
|
||||
// (See https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name)
|
||||
// The CORS-safelisted response headers are exposed to client by default.
|
||||
//
|
||||
// When an HTTP header name is specified using the `ExposeHeaders` field,
|
||||
// this additional header will be exposed as part of the response to the
|
||||
// client.
|
||||
//
|
||||
// Header names are not case sensitive.
|
||||
//
|
||||
// Multiple header names in the value of the `Access-Control-Expose-Headers`
|
||||
// response header are separated by a comma (",").
|
||||
//
|
||||
// A wildcard indicates that the responses with all HTTP headers are exposed
|
||||
// to clients. The `Access-Control-Expose-Headers` response header can only
|
||||
// use `*` wildcard as value when the `AllowCredentials` field is false or omitted.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
// +listType=set
|
||||
// +kubebuilder:validation:MaxItems=64
|
||||
ExposeHeaders []HTTPHeaderName `json:"exposeHeaders,omitempty"`
|
||||
|
||||
// MaxAge indicates the duration (in seconds) for the client to cache the
|
||||
// results of a "preflight" request.
|
||||
//
|
||||
// The information provided by the `Access-Control-Allow-Methods` and
|
||||
// `Access-Control-Allow-Headers` response headers can be cached by the
|
||||
// client until the time specified by `Access-Control-Max-Age` elapses.
|
||||
//
|
||||
// The default value of `Access-Control-Max-Age` response header is 5
|
||||
// (seconds).
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:default=5
|
||||
// +kubebuilder:validation:Minimum=1
|
||||
MaxAge int32 `json:"maxAge,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPRouteExternalAuthProtcol specifies what protocol should be used
|
||||
// for communicating with an external authorization server.
|
||||
//
|
||||
// Valid values are supplied as constants below.
|
||||
type HTTPRouteExternalAuthProtocol string
|
||||
|
||||
const (
|
||||
HTTPRouteExternalAuthGRPCProtocol HTTPRouteExternalAuthProtocol = "GRPC"
|
||||
HTTPRouteExternalAuthHTTPProtocol HTTPRouteExternalAuthProtocol = "HTTP"
|
||||
)
|
||||
|
||||
// HTTPExternalAuthFilter defines a filter that modifies requests by sending
|
||||
// request details to an external authorization server.
|
||||
//
|
||||
// Support: Extended
|
||||
// Feature Name: HTTPRouteExternalAuth
|
||||
// +kubebuilder:validation:XValidation:message="grpc must be specified when protocol is set to 'GRPC'",rule="self.protocol == 'GRPC' ? has(self.grpc) : true"
|
||||
// +kubebuilder:validation:XValidation:message="protocol must be 'GRPC' when grpc is set",rule="has(self.grpc) ? self.protocol == 'GRPC' : true"
|
||||
// +kubebuilder:validation:XValidation:message="http must be specified when protocol is set to 'HTTP'",rule="self.protocol == 'HTTP' ? has(self.http) : true"
|
||||
// +kubebuilder:validation:XValidation:message="protocol must be 'HTTP' when http is set",rule="has(self.http) ? self.protocol == 'HTTP' : true"
|
||||
type HTTPExternalAuthFilter struct {
|
||||
// ExternalAuthProtocol describes which protocol to use when communicating with an
|
||||
// ext_authz authorization server.
|
||||
//
|
||||
// When this is set to GRPC, each backend must use the Envoy ext_authz protocol
|
||||
// on the port specified in `backendRefs`. Requests and responses are defined
|
||||
// in the protobufs explained at:
|
||||
// https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto
|
||||
//
|
||||
// When this is set to HTTP, each backend must respond with a `200` status
|
||||
// code in on a successful authorization. Any other code is considered
|
||||
// an authorization failure.
|
||||
//
|
||||
// Feature Names:
|
||||
// GRPC Support - HTTPRouteExternalAuthGRPC
|
||||
// HTTP Support - HTTPRouteExternalAuthHTTP
|
||||
//
|
||||
// +unionDiscriminator
|
||||
// +required
|
||||
// +kubebuilder:validation:Enum=HTTP;GRPC
|
||||
ExternalAuthProtocol HTTPRouteExternalAuthProtocol `json:"protocol,omitempty"`
|
||||
|
||||
// BackendRef is a reference to a backend to send authorization
|
||||
// requests to.
|
||||
//
|
||||
// The backend must speak the selected protocol (GRPC or HTTP) on the
|
||||
// referenced port.
|
||||
//
|
||||
// If the backend service requires TLS, use BackendTLSPolicy to tell the
|
||||
// implementation to supply the TLS details to be used to connect to that
|
||||
// backend.
|
||||
//
|
||||
// +required
|
||||
BackendRef BackendObjectReference `json:"backendRef,omitempty"`
|
||||
|
||||
// GRPCAuthConfig contains configuration for communication with ext_authz
|
||||
// protocol-speaking backends.
|
||||
//
|
||||
// If unset, implementations must assume the default behavior for each
|
||||
// included field is intended.
|
||||
//
|
||||
// +optional
|
||||
GRPCAuthConfig *GRPCAuthConfig `json:"grpc,omitempty"`
|
||||
|
||||
// HTTPAuthConfig contains configuration for communication with HTTP-speaking
|
||||
// backends.
|
||||
//
|
||||
// If unset, implementations must assume the default behavior for each
|
||||
// included field is intended.
|
||||
//
|
||||
// +optional
|
||||
HTTPAuthConfig *HTTPAuthConfig `json:"http,omitempty"`
|
||||
|
||||
// ForwardBody controls if requests to the authorization server should include
|
||||
// the body of the client request; and if so, how big that body is allowed
|
||||
// to be.
|
||||
//
|
||||
// It is expected that implementations will buffer the request body up to
|
||||
// `forwardBody.maxSize` bytes. Bodies over that size must be rejected with a
|
||||
// 4xx series error (413 or 403 are common examples), and fail processing
|
||||
// of the filter.
|
||||
//
|
||||
// If unset, or `forwardBody.maxSize` is set to `0`, then the body will not
|
||||
// be forwarded.
|
||||
//
|
||||
// Feature Name: HTTPRouteExternalAuthForwardBody
|
||||
//
|
||||
//
|
||||
// +optional
|
||||
ForwardBody *ForwardBodyConfig `json:"forwardBody,omitempty"`
|
||||
}
|
||||
|
||||
// GRPCAuthConfig contains configuration for communication with Auth server
|
||||
// backends that speak Envoy's ext_authz gRPC protocol.
|
||||
//
|
||||
// Requests and responses are defined in the protobufs explained at:
|
||||
// https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto
|
||||
type GRPCAuthConfig struct {
|
||||
// AllowedRequestHeaders specifies what headers from the client request
|
||||
// will be sent to the authorization server.
|
||||
//
|
||||
// If this list is empty, then all headers must be sent.
|
||||
//
|
||||
// If the list has entries, only those entries must be sent.
|
||||
//
|
||||
// +optional
|
||||
// +listType=set
|
||||
// +kubebuilder:validation:MaxLength=64
|
||||
AllowedRequestHeaders []string `json:"allowedHeaders,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPAuthConfig contains configuration for communication with HTTP-speaking
|
||||
// backends.
|
||||
type HTTPAuthConfig struct {
|
||||
// Path sets the prefix that paths from the client request will have added
|
||||
// when forwarded to the authorization server.
|
||||
//
|
||||
// When empty or unspecified, no prefix is added.
|
||||
//
|
||||
// Valid values are the same as the "value" regex for path values in the `match`
|
||||
// stanza, and the validation regex will screen out invalid paths in the same way.
|
||||
// Even with the validation, implementations MUST sanitize this input before using it
|
||||
// directly.
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:validation:MaxLength=1024
|
||||
// +kubebuilder:validation:Pattern="^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$"
|
||||
Path string `json:"path,omitempty"`
|
||||
|
||||
// AllowedRequestHeaders specifies what additional headers from the client request
|
||||
// will be sent to the authorization server.
|
||||
//
|
||||
// The following headers must always be sent to the authorization server,
|
||||
// regardless of this setting:
|
||||
//
|
||||
// * `Host`
|
||||
// * `Method`
|
||||
// * `Path`
|
||||
// * `Content-Length`
|
||||
// * `Authorization`
|
||||
//
|
||||
// If this list is empty, then only those headers must be sent.
|
||||
//
|
||||
// Note that `Content-Length` has a special behavior, in that the length
|
||||
// sent must be correct for the actual request to the external authorization
|
||||
// server - that is, it must reflect the actual number of bytes sent in the
|
||||
// body of the request to the authorization server.
|
||||
//
|
||||
// So if the `forwardBody` stanza is unset, or `forwardBody.maxSize` is set
|
||||
// to `0`, then `Content-Length` must be `0`. If `forwardBody.maxSize` is set
|
||||
// to anything other than `0`, then the `Content-Length` of the authorization
|
||||
// request must be set to the actual number of bytes forwarded.
|
||||
//
|
||||
// +optional
|
||||
// +listType=set
|
||||
// +kubebuilder:validation:MaxLength=64
|
||||
AllowedRequestHeaders []string `json:"allowedHeaders,omitempty"`
|
||||
|
||||
// AllowedResponseHeaders specifies what headers from the authorization response
|
||||
// will be copied into the request to the backend.
|
||||
//
|
||||
// If this list is empty, then all headers from the authorization server
|
||||
// except Authority or Host must be copied.
|
||||
//
|
||||
// +optional
|
||||
// +listType=set
|
||||
// +kubebuilder:validation:MaxLength=64
|
||||
AllowedResponseHeaders []string `json:"allowedResponseHeaders,omitempty"`
|
||||
}
|
||||
|
||||
// ForwardBody configures if requests to the authorization server should include
|
||||
// the body of the client request; and if so, how big that body is allowed
|
||||
// to be.
|
||||
//
|
||||
// If empty or unset, do not forward the body.
|
||||
type ForwardBodyConfig struct {
|
||||
// MaxSize specifies how large in bytes the largest body that will be buffered
|
||||
// and sent to the authorization server. If the body size is larger than
|
||||
// `maxSize`, then the body sent to the authorization server must be
|
||||
// truncated to `maxSize` bytes.
|
||||
//
|
||||
// Experimental note: This behavior needs to be checked against
|
||||
// various dataplanes; it may need to be changed.
|
||||
// See https://github.com/kubernetes-sigs/gateway-api/pull/4001#discussion_r2291405746
|
||||
// for more.
|
||||
//
|
||||
// If 0, the body will not be sent to the authorization server.
|
||||
// +optional
|
||||
MaxSize uint16 `json:"maxSize,omitempty"`
|
||||
}
|
||||
|
||||
// HTTPBackendRef defines how a HTTPRoute forwards a HTTP request.
|
||||
@@ -1187,9 +1833,9 @@ type HTTPBackendRef struct {
|
||||
// Filters field in HTTPRouteRule.)
|
||||
//
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
// +kubebuilder:validation:MaxItems=16
|
||||
// +kubebuilder:validation:XValidation:message="May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both",rule="!(self.exists(f, f.type == 'RequestRedirect') && self.exists(f, f.type == 'URLRewrite'))"
|
||||
// +kubebuilder:validation:XValidation:message="May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both",rule="!(self.exists(f, f.type == 'RequestRedirect') && self.exists(f, f.type == 'URLRewrite'))"
|
||||
// +kubebuilder:validation:XValidation:message="RequestHeaderModifier filter cannot be repeated",rule="self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1"
|
||||
// +kubebuilder:validation:XValidation:message="ResponseHeaderModifier filter cannot be repeated",rule="self.filter(f, f.type == 'ResponseHeaderModifier').size() <= 1"
|
||||
// +kubebuilder:validation:XValidation:message="RequestRedirect filter cannot be repeated",rule="self.filter(f, f.type == 'RequestRedirect').size() <= 1"
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes 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.
|
||||
@@ -24,12 +27,15 @@ package v1
|
||||
type LocalObjectReference struct {
|
||||
// Group is the group of the referent. For example, "gateway.networking.k8s.io".
|
||||
// When unspecified or empty string, core API group is inferred.
|
||||
// +required
|
||||
Group Group `json:"group"`
|
||||
|
||||
// Kind is kind of the referent. For example "HTTPRoute" or "Service".
|
||||
// +required
|
||||
Kind Kind `json:"kind"`
|
||||
|
||||
// Name is the name of the referent.
|
||||
// +required
|
||||
Name ObjectName `json:"name"`
|
||||
}
|
||||
|
||||
@@ -50,22 +56,23 @@ type SecretObjectReference struct {
|
||||
// +kubebuilder:default=""
|
||||
Group *Group `json:"group"`
|
||||
|
||||
// Kind is kind of the referent. For example "HTTPRoute" or "Service".
|
||||
// Kind is kind of the referent. For example "Secret".
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:default=Secret
|
||||
Kind *Kind `json:"kind"`
|
||||
|
||||
// Name is the name of the referent.
|
||||
// +required
|
||||
Name ObjectName `json:"name"`
|
||||
|
||||
// Namespace is the namespace of the backend. When unspecified, the local
|
||||
// Namespace is the namespace of the referenced object. When unspecified, the local
|
||||
// namespace is inferred.
|
||||
//
|
||||
// Note that when a namespace is specified, a ReferenceGrant object
|
||||
// is required in the referent namespace to allow that namespace's
|
||||
// owner to accept the reference. See the ReferenceGrant documentation
|
||||
// for details.
|
||||
// Note that when a namespace different than the local namespace is specified,
|
||||
// a ReferenceGrant object is required in the referent namespace to allow that
|
||||
// namespace's owner to accept the reference. See the ReferenceGrant
|
||||
// documentation for details.
|
||||
//
|
||||
// Support: Core
|
||||
//
|
||||
@@ -77,10 +84,10 @@ type SecretObjectReference struct {
|
||||
// specific to BackendRef. It includes a few additional fields and features
|
||||
// than a regular ObjectReference.
|
||||
//
|
||||
// Note that when a namespace is specified, a ReferenceGrant object
|
||||
// is required in the referent namespace to allow that namespace's
|
||||
// owner to accept the reference. See the ReferenceGrant documentation
|
||||
// for details.
|
||||
// Note that when a namespace different than the local namespace is specified, a
|
||||
// ReferenceGrant object is required in the referent namespace to allow that
|
||||
// namespace's owner to accept the reference. See the ReferenceGrant
|
||||
// documentation for details.
|
||||
//
|
||||
// The API object must be valid in the cluster; the Group and Kind must
|
||||
// be registered in the cluster for this reference to be valid.
|
||||
@@ -88,6 +95,8 @@ type SecretObjectReference struct {
|
||||
// References to objects with invalid Group and Kind are not valid, and must
|
||||
// be rejected by the implementation, with appropriate Conditions set
|
||||
// on the containing object.
|
||||
//
|
||||
// +kubebuilder:validation:XValidation:message="Must have port for Service reference",rule="(size(self.group) == 0 && self.kind == 'Service') ? has(self.port) : true"
|
||||
type BackendObjectReference struct {
|
||||
// Group is the group of the referent. For example, "gateway.networking.k8s.io".
|
||||
// When unspecified or empty string, core API group is inferred.
|
||||
@@ -96,23 +105,36 @@ type BackendObjectReference struct {
|
||||
// +kubebuilder:default=""
|
||||
Group *Group `json:"group,omitempty"`
|
||||
|
||||
// Kind is kind of the referent. For example "HTTPRoute" or "Service".
|
||||
// Kind is the Kubernetes resource kind of the referent. For example
|
||||
// "Service".
|
||||
//
|
||||
// Defaults to "Service" when not specified.
|
||||
//
|
||||
// ExternalName services can refer to CNAME DNS records that may live
|
||||
// outside of the cluster and as such are difficult to reason about in
|
||||
// terms of conformance. They also may not be safe to forward to (see
|
||||
// CVE-2021-25740 for more information). Implementations SHOULD NOT
|
||||
// support ExternalName Services.
|
||||
//
|
||||
// Support: Core (Services with a type other than ExternalName)
|
||||
//
|
||||
// Support: Implementation-specific (Services with type ExternalName)
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:default=Service
|
||||
Kind *Kind `json:"kind,omitempty"`
|
||||
|
||||
// Name is the name of the referent.
|
||||
// +required
|
||||
Name ObjectName `json:"name"`
|
||||
|
||||
// Namespace is the namespace of the backend. When unspecified, the local
|
||||
// namespace is inferred.
|
||||
//
|
||||
// Note that when a namespace is specified, a ReferenceGrant object
|
||||
// is required in the referent namespace to allow that namespace's
|
||||
// owner to accept the reference. See the ReferenceGrant documentation
|
||||
// for details.
|
||||
// Note that when a namespace different than the local namespace is specified,
|
||||
// a ReferenceGrant object is required in the referent namespace to allow that
|
||||
// namespace's owner to accept the reference. See the ReferenceGrant
|
||||
// documentation for details.
|
||||
//
|
||||
// Support: Core
|
||||
//
|
||||
@@ -126,5 +148,43 @@ type BackendObjectReference struct {
|
||||
// resource or this field.
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:validation:Minimum=1
|
||||
// +kubebuilder:validation:Maximum=65535
|
||||
Port *PortNumber `json:"port,omitempty"`
|
||||
}
|
||||
|
||||
// ObjectReference identifies an API object including its namespace.
|
||||
//
|
||||
// The API object must be valid in the cluster; the Group and Kind must
|
||||
// be registered in the cluster for this reference to be valid.
|
||||
//
|
||||
// References to objects with invalid Group and Kind are not valid, and must
|
||||
// be rejected by the implementation, with appropriate Conditions set
|
||||
// on the containing object.
|
||||
type ObjectReference struct {
|
||||
// Group is the group of the referent. For example, "gateway.networking.k8s.io".
|
||||
// When set to the empty string, core API group is inferred.
|
||||
// +required
|
||||
Group Group `json:"group"`
|
||||
|
||||
// Kind is kind of the referent. For example "ConfigMap" or "Service".
|
||||
// +required
|
||||
Kind Kind `json:"kind"`
|
||||
|
||||
// Name is the name of the referent.
|
||||
// +required
|
||||
Name ObjectName `json:"name"`
|
||||
|
||||
// Namespace is the namespace of the referenced object. When unspecified, the local
|
||||
// namespace is inferred.
|
||||
//
|
||||
// Note that when a namespace different than the local namespace is specified,
|
||||
// a ReferenceGrant object is required in the referent namespace to allow that
|
||||
// namespace's owner to accept the reference. See the ReferenceGrant
|
||||
// documentation for details.
|
||||
//
|
||||
// Support: Core
|
||||
//
|
||||
// +optional
|
||||
Namespace *Namespace `json:"namespace,omitempty"`
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
// with "Core" support:
|
||||
//
|
||||
// * Gateway (Gateway conformance profile)
|
||||
// * Service (Mesh conformance profile, experimental, ClusterIP Services only)
|
||||
// * Service (Mesh conformance profile, ClusterIP Services only)
|
||||
//
|
||||
// This API may be extended in the future to support additional kinds of parent
|
||||
// resources.
|
||||
@@ -49,7 +49,7 @@ type ParentReference struct {
|
||||
// There are two kinds of parent resources with "Core" support:
|
||||
//
|
||||
// * Gateway (Gateway conformance profile)
|
||||
// * Service (Mesh conformance profile, experimental, ClusterIP Services only)
|
||||
// * Service (Mesh conformance profile, ClusterIP Services only)
|
||||
//
|
||||
// Support for other resources is Implementation-Specific.
|
||||
//
|
||||
@@ -86,19 +86,18 @@ type ParentReference struct {
|
||||
// Name is the name of the referent.
|
||||
//
|
||||
// Support: Core
|
||||
// +required
|
||||
Name ObjectName `json:"name"`
|
||||
|
||||
// SectionName is the name of a section within the target resource. In the
|
||||
// following resources, SectionName is interpreted as the following:
|
||||
//
|
||||
// * Gateway: Listener Name. When both Port (experimental) and SectionName
|
||||
// * Gateway: Listener name. When both Port (experimental) and SectionName
|
||||
// are specified, the name and port of the selected listener must match
|
||||
// both specified values.
|
||||
// * Service: Port Name. When both Port (experimental) and SectionName
|
||||
// * Service: Port name. When both Port (experimental) and SectionName
|
||||
// are specified, the name and port of the selected listener must match
|
||||
// both specified values. Note that attaching Routes to Services as Parents
|
||||
// is part of experimental Mesh support and is not supported for any other
|
||||
// purpose.
|
||||
// both specified values.
|
||||
//
|
||||
// Implementations MAY choose to support attaching Routes to other resources.
|
||||
// If that is the case, they MUST clearly document how SectionName is
|
||||
@@ -150,10 +149,31 @@ type ParentReference struct {
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
// <gateway:experimental>
|
||||
//
|
||||
// +kubebuilder:validation:Minimum=1
|
||||
// +kubebuilder:validation:Maximum=65535
|
||||
Port *PortNumber `json:"port,omitempty"`
|
||||
}
|
||||
|
||||
// GatewayDefaultScope defines the set of default scopes that a Gateway
|
||||
// can claim, for use in any Route type. At present the only supported
|
||||
// scopes are "All" and "None". "None" is a special scope which
|
||||
// explicitly means that the Route MUST NOT attached to any default
|
||||
// Gateway.
|
||||
//
|
||||
// +kubebuilder:validation:Enum=All;None
|
||||
type GatewayDefaultScope string
|
||||
|
||||
const (
|
||||
// GatewayDefaultScopeAll indicates that a Gateway can claim absolutely
|
||||
// any Route asking for a default Gateway.
|
||||
GatewayDefaultScopeAll GatewayDefaultScope = "All"
|
||||
|
||||
// GatewayDefaultScopeNone indicates that a Gateway MUST NOT claim
|
||||
// any Route asking for a default Gateway.
|
||||
GatewayDefaultScopeNone GatewayDefaultScope = "None"
|
||||
)
|
||||
|
||||
// CommonRouteSpec defines the common attributes that all Routes MUST include
|
||||
// within their spec.
|
||||
type CommonRouteSpec struct {
|
||||
@@ -171,9 +191,8 @@ type CommonRouteSpec struct {
|
||||
// There are two kinds of parent resources with "Core" support:
|
||||
//
|
||||
// * Gateway (Gateway conformance profile)
|
||||
// <gateway:experimental:description>
|
||||
// * Service (Mesh conformance profile, experimental, ClusterIP Services only)
|
||||
// </gateway:experimental:description>
|
||||
// * Service (Mesh conformance profile, ClusterIP Services only)
|
||||
//
|
||||
// This API may be extended in the future to support additional kinds of parent
|
||||
// resources.
|
||||
//
|
||||
@@ -222,19 +241,34 @@ type CommonRouteSpec struct {
|
||||
// </gateway:experimental:description>
|
||||
//
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
// +kubebuilder:validation:MaxItems=32
|
||||
// <gateway:standard:validation:XValidation:message="sectionName must be specified when parentRefs includes 2 or more references to the same parent",rule="self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) || p1.sectionName == '') == (!has(p2.sectionName) || p2.sectionName == '')) : true))">
|
||||
// <gateway:standard:validation:XValidation:message="sectionName must be unique when parentRefs includes 2 or more references to the same parent",rule="self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName))))">
|
||||
// <gateway:experimental:validation:XValidation:message="sectionName or port must be specified when parentRefs includes 2 or more references to the same parent",rule="self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__)) ? ((!has(p1.sectionName) || p1.sectionName == '') == (!has(p2.sectionName) || p2.sectionName == '') && (!has(p1.port) || p1.port == 0) == (!has(p2.port) || p2.port == 0)): true))">
|
||||
// <gateway:experimental:validation:XValidation:message="sectionName or port must be unique when parentRefs includes 2 or more references to the same parent",rule="self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || ( has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)) && (((!has(p1.port) || p1.port == 0) && (!has(p2.port) || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port))))">
|
||||
ParentRefs []ParentReference `json:"parentRefs,omitempty"`
|
||||
|
||||
// UseDefaultGateways indicates the default Gateway scope to use for this
|
||||
// Route. If unset (the default) or set to None, the Route will not be
|
||||
// attached to any default Gateway; if set, it will be attached to any
|
||||
// default Gateway supporting the named scope, subject to the usual rules
|
||||
// about which Routes a Gateway is allowed to claim.
|
||||
//
|
||||
// Think carefully before using this functionality! The set of default
|
||||
// Gateways supporting the requested scope can change over time without
|
||||
// any notice to the Route author, and in many situations it will not be
|
||||
// appropriate to request a default Gateway for a given Route -- for
|
||||
// example, a Route with specific security requirements should almost
|
||||
// certainly not use a default Gateway.
|
||||
//
|
||||
// +optional
|
||||
// <gateway:experimental>
|
||||
UseDefaultGateways GatewayDefaultScope `json:"useDefaultGateways,omitempty"`
|
||||
}
|
||||
|
||||
// PortNumber defines a network port.
|
||||
//
|
||||
// +kubebuilder:validation:Minimum=1
|
||||
// +kubebuilder:validation:Maximum=65535
|
||||
type PortNumber int32
|
||||
type PortNumber = int32
|
||||
|
||||
// BackendRef defines how a Route should forward a request to a Kubernetes
|
||||
// resource.
|
||||
@@ -440,6 +474,7 @@ const (
|
||||
type RouteParentStatus struct {
|
||||
// ParentRef corresponds with a ParentRef in the spec that this
|
||||
// RouteParentStatus struct describes the status of.
|
||||
// +required
|
||||
ParentRef ParentReference `json:"parentRef"`
|
||||
|
||||
// ControllerName is a domain/path string that indicates the name of the
|
||||
@@ -455,6 +490,7 @@ type RouteParentStatus struct {
|
||||
// Controllers MUST populate this field when writing status. Controllers should ensure that
|
||||
// entries to status populated with their ControllerName are cleaned up when they are no
|
||||
// longer necessary.
|
||||
// +required
|
||||
ControllerName GatewayController `json:"controllerName"`
|
||||
|
||||
// Conditions describes the status of the route with respect to the Gateway.
|
||||
@@ -473,14 +509,45 @@ type RouteParentStatus struct {
|
||||
// There are a number of cases where the "Accepted" condition may not be set
|
||||
// due to lack of controller visibility, that includes when:
|
||||
//
|
||||
// * The Route refers to a non-existent parent.
|
||||
// * The Route refers to a nonexistent parent.
|
||||
// * The Route is of a type that the controller does not support.
|
||||
// * The Route is in a namespace the controller does not have access to.
|
||||
//
|
||||
// <gateway:util:excludeFromCRD>
|
||||
//
|
||||
// Notes for implementors:
|
||||
//
|
||||
// Conditions are a listType `map`, which means that they function like a
|
||||
// map with a key of the `type` field _in the k8s apiserver_.
|
||||
//
|
||||
// This means that implementations must obey some rules when updating this
|
||||
// section.
|
||||
//
|
||||
// * Implementations MUST perform a read-modify-write cycle on this field
|
||||
// before modifying it. That is, when modifying this field, implementations
|
||||
// must be confident they have fetched the most recent version of this field,
|
||||
// and ensure that changes they make are on that recent version.
|
||||
// * Implementations MUST NOT remove or reorder Conditions that they are not
|
||||
// directly responsible for. For example, if an implementation sees a Condition
|
||||
// with type `special.io/SomeField`, it MUST NOT remove, change or update that
|
||||
// Condition.
|
||||
// * Implementations MUST always _merge_ changes into Conditions of the same Type,
|
||||
// rather than creating more than one Condition of the same Type.
|
||||
// * Implementations MUST always update the `observedGeneration` field of the
|
||||
// Condition to the `metadata.generation` of the Gateway at the time of update creation.
|
||||
// * If the `observedGeneration` of a Condition is _greater than_ the value the
|
||||
// implementation knows about, then it MUST NOT perform the update on that Condition,
|
||||
// but must wait for a future reconciliation and status update. (The assumption is that
|
||||
// the implementation's copy of the object is stale and an update will be re-triggered
|
||||
// if relevant.)
|
||||
//
|
||||
// </gateway:util:excludeFromCRD>
|
||||
//
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
// +kubebuilder:validation:MinItems=1
|
||||
// +kubebuilder:validation:MaxItems=8
|
||||
// +required
|
||||
Conditions []metav1.Condition `json:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
@@ -502,6 +569,31 @@ type RouteStatus struct {
|
||||
// A maximum of 32 Gateways will be represented in this list. An empty list
|
||||
// means the route has not been attached to any Gateway.
|
||||
//
|
||||
// <gateway:util:excludeFromCRD>
|
||||
// Notes for implementors:
|
||||
//
|
||||
// While parents is not a listType `map`, this is due to the fact that the
|
||||
// list key is not scalar, and Kubernetes is unable to represent this.
|
||||
//
|
||||
// Parent status MUST be considered to be namespaced by the combination of
|
||||
// the parentRef and controllerName fields, and implementations should keep
|
||||
// the following rules in mind when updating this status:
|
||||
//
|
||||
// * Implementations MUST update only entries that have a matching value of
|
||||
// `controllerName` for that implementation.
|
||||
// * Implementations MUST NOT update entries with non-matching `controllerName`
|
||||
// fields.
|
||||
// * Implementations MUST treat each `parentRef`` in the Route separately and
|
||||
// update its status based on the relationship with that parent.
|
||||
// * Implementations MUST perform a read-modify-write cycle on this field
|
||||
// before modifying it. That is, when modifying this field, implementations
|
||||
// must be confident they have fetched the most recent version of this field,
|
||||
// and ensure that changes they make are on that recent version.
|
||||
//
|
||||
// </gateway:util:excludeFromCRD>
|
||||
//
|
||||
// +required
|
||||
// +listType=atomic
|
||||
// +kubebuilder:validation:MaxItems=32
|
||||
Parents []RouteParentStatus `json:"parents"`
|
||||
}
|
||||
@@ -539,6 +631,30 @@ type Hostname string
|
||||
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
|
||||
type PreciseHostname string
|
||||
|
||||
// AbsoluteURI represents a Uniform Resource Identifier (URI) as defined by RFC3986.
|
||||
|
||||
// The AbsoluteURI MUST NOT be a relative URI, and it MUST follow the URI syntax and
|
||||
// encoding rules specified in RFC3986. The AbsoluteURI MUST include both a
|
||||
// scheme (e.g., "http" or "spiffe") and a scheme-specific-part. URIs that
|
||||
// include an authority MUST include a fully qualified domain name or
|
||||
// IP address as the host.
|
||||
// <gateway:util:excludeFromCRD> The below regex is taken from the regex section in RFC 3986 with a slight modification to enforce a full URI and not relative. </gateway:util:excludeFromCRD>
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +kubebuilder:validation:Pattern=`^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))?`
|
||||
type AbsoluteURI string
|
||||
|
||||
// The CORSOrigin MUST NOT be a relative URI, and it MUST follow the URI syntax and
|
||||
// encoding rules specified in RFC3986. The CORSOrigin MUST include both a
|
||||
// scheme (e.g., "http" or "spiffe") and a scheme-specific-part, or it should be a single '*' character.
|
||||
// URIs that include an authority MUST include a fully qualified domain name or
|
||||
// IP address as the host.
|
||||
// <gateway:util:excludeFromCRD> The below regex was generated to simplify the assertion of scheme://host:<port> being port optional </gateway:util:excludeFromCRD>
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +kubebuilder:validation:Pattern=`(^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$)`
|
||||
type CORSOrigin string
|
||||
|
||||
// Group refers to a Kubernetes Group. It must either be an empty string or a
|
||||
// RFC 1123 subdomain.
|
||||
//
|
||||
@@ -576,7 +692,7 @@ type Group string
|
||||
type Kind string
|
||||
|
||||
// ObjectName refers to the name of a Kubernetes object.
|
||||
// Object names can have a variety of forms, including RFC1123 subdomains,
|
||||
// Object names can have a variety of forms, including RFC 1123 subdomains,
|
||||
// RFC 1123 labels, or RFC 1035 labels.
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
@@ -606,11 +722,22 @@ type Namespace string
|
||||
|
||||
// SectionName is the name of a section in a Kubernetes resource.
|
||||
//
|
||||
// In the following resources, SectionName is interpreted as the following:
|
||||
//
|
||||
// * Gateway: Listener name
|
||||
// * HTTPRoute: HTTPRouteRule name
|
||||
// * Service: Port name
|
||||
//
|
||||
// Section names can have a variety of forms, including RFC 1123 subdomains,
|
||||
// RFC 1123 labels, or RFC 1035 labels.
|
||||
//
|
||||
// This validation is based off of the corresponding Kubernetes validation:
|
||||
// https://github.com/kubernetes/apimachinery/blob/02cfb53916346d085a6c6c7c66f882e3c6b0eca6/pkg/util/validation/validation.go#L208
|
||||
//
|
||||
// Valid values include:
|
||||
//
|
||||
// * "example"
|
||||
// * "foo-example"
|
||||
// * "example.com"
|
||||
// * "foo.example.com"
|
||||
//
|
||||
@@ -655,11 +782,11 @@ type GatewayController string
|
||||
// Invalid values include:
|
||||
//
|
||||
// * example~ - "~" is an invalid character
|
||||
// * example.com. - can not start or end with "."
|
||||
// * example.com. - cannot start or end with "."
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +kubebuilder:validation:Pattern=`^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]/?)*$`
|
||||
// +kubebuilder:validation:Pattern=`^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9]$`
|
||||
type AnnotationKey string
|
||||
|
||||
// AnnotationValue is the value of an annotation in Gateway API. This is used
|
||||
@@ -671,6 +798,45 @@ type AnnotationKey string
|
||||
// +kubebuilder:validation:MaxLength=4096
|
||||
type AnnotationValue string
|
||||
|
||||
// LabelKey is the key of a label in the Gateway API. This is used for validation
|
||||
// of maps such as Gateway infrastructure labels. This matches the Kubernetes
|
||||
// "qualified name" validation that is used for labels.
|
||||
//
|
||||
// Valid values include:
|
||||
//
|
||||
// * example
|
||||
// * example.com
|
||||
// * example.com/path
|
||||
// * example.com/path.html
|
||||
//
|
||||
// Invalid values include:
|
||||
//
|
||||
// * example~ - "~" is an invalid character
|
||||
// * example.com. - cannot start or end with "."
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=253
|
||||
// +kubebuilder:validation:Pattern=`^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9]$`
|
||||
type LabelKey string
|
||||
|
||||
// LabelValue is the value of a label in the Gateway API. This is used for validation
|
||||
// of maps such as Gateway infrastructure labels. This matches the Kubernetes
|
||||
// label validation rules:
|
||||
// * must be 63 characters or less (can be empty),
|
||||
// * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]),
|
||||
// * could contain dashes (-), underscores (_), dots (.), and alphanumerics between.
|
||||
//
|
||||
// Valid values include:
|
||||
//
|
||||
// * MyValue
|
||||
// * my.name
|
||||
// * 123-my-value
|
||||
//
|
||||
// +kubebuilder:validation:MinLength=0
|
||||
// +kubebuilder:validation:MaxLength=63
|
||||
// +kubebuilder:validation:Pattern=`^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$`
|
||||
type LabelValue string
|
||||
|
||||
// AddressType defines how a network address is represented as a text string.
|
||||
// This may take two possible forms:
|
||||
//
|
||||
@@ -712,7 +878,7 @@ const (
|
||||
// (see [RFC 5952](https://tools.ietf.org/html/rfc5952)).
|
||||
//
|
||||
// This type is intended for specific addresses. Address ranges are not
|
||||
// supported (e.g. you can not use a CIDR range like 127.0.0.0/24 as an
|
||||
// supported (e.g. you cannot use a CIDR range like 127.0.0.0/24 as an
|
||||
// IPAddress).
|
||||
//
|
||||
// Support: Extended
|
||||
@@ -736,3 +902,129 @@ const (
|
||||
// Support: Implementation-specific
|
||||
NamedAddressType AddressType = "NamedAddress"
|
||||
)
|
||||
|
||||
// SessionPersistence defines the desired state of SessionPersistence.
|
||||
// +kubebuilder:validation:XValidation:message="AbsoluteTimeout must be specified when cookie lifetimeType is Permanent",rule="!has(self.cookieConfig) || !has(self.cookieConfig.lifetimeType) || self.cookieConfig.lifetimeType != 'Permanent' || has(self.absoluteTimeout)"
|
||||
type SessionPersistence struct {
|
||||
// SessionName defines the name of the persistent session token
|
||||
// which may be reflected in the cookie or the header. Users
|
||||
// should avoid reusing session names to prevent unintended
|
||||
// consequences, such as rejection or unpredictable behavior.
|
||||
//
|
||||
// Support: Implementation-specific
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:validation:MaxLength=128
|
||||
SessionName *string `json:"sessionName,omitempty"`
|
||||
|
||||
// AbsoluteTimeout defines the absolute timeout of the persistent
|
||||
// session. Once the AbsoluteTimeout duration has elapsed, the
|
||||
// session becomes invalid.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
AbsoluteTimeout *Duration `json:"absoluteTimeout,omitempty"`
|
||||
|
||||
// IdleTimeout defines the idle timeout of the persistent session.
|
||||
// Once the session has been idle for more than the specified
|
||||
// IdleTimeout duration, the session becomes invalid.
|
||||
//
|
||||
// Support: Extended
|
||||
//
|
||||
// +optional
|
||||
IdleTimeout *Duration `json:"idleTimeout,omitempty"`
|
||||
|
||||
// Type defines the type of session persistence such as through
|
||||
// the use a header or cookie. Defaults to cookie based session
|
||||
// persistence.
|
||||
//
|
||||
// Support: Core for "Cookie" type
|
||||
//
|
||||
// Support: Extended for "Header" type
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:default=Cookie
|
||||
Type *SessionPersistenceType `json:"type,omitempty"`
|
||||
|
||||
// CookieConfig provides configuration settings that are specific
|
||||
// to cookie-based session persistence.
|
||||
//
|
||||
// Support: Core
|
||||
//
|
||||
// +optional
|
||||
CookieConfig *CookieConfig `json:"cookieConfig,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:validation:Enum=Cookie;Header
|
||||
type SessionPersistenceType string
|
||||
|
||||
const (
|
||||
// CookieBasedSessionPersistence specifies cookie-based session
|
||||
// persistence.
|
||||
//
|
||||
// Support: Core
|
||||
CookieBasedSessionPersistence SessionPersistenceType = "Cookie"
|
||||
|
||||
// HeaderBasedSessionPersistence specifies header-based session
|
||||
// persistence.
|
||||
//
|
||||
// Support: Extended
|
||||
HeaderBasedSessionPersistence SessionPersistenceType = "Header"
|
||||
)
|
||||
|
||||
// CookieConfig defines the configuration for cookie-based session persistence.
|
||||
type CookieConfig struct {
|
||||
// LifetimeType specifies whether the cookie has a permanent or
|
||||
// session-based lifetime. A permanent cookie persists until its
|
||||
// specified expiry time, defined by the Expires or Max-Age cookie
|
||||
// attributes, while a session cookie is deleted when the current
|
||||
// session ends.
|
||||
//
|
||||
// When set to "Permanent", AbsoluteTimeout indicates the
|
||||
// cookie's lifetime via the Expires or Max-Age cookie attributes
|
||||
// and is required.
|
||||
//
|
||||
// When set to "Session", AbsoluteTimeout indicates the
|
||||
// absolute lifetime of the cookie tracked by the gateway and
|
||||
// is optional.
|
||||
//
|
||||
// Defaults to "Session".
|
||||
//
|
||||
// Support: Core for "Session" type
|
||||
//
|
||||
// Support: Extended for "Permanent" type
|
||||
//
|
||||
// +optional
|
||||
// +kubebuilder:default=Session
|
||||
LifetimeType *CookieLifetimeType `json:"lifetimeType,omitempty"`
|
||||
}
|
||||
|
||||
// +kubebuilder:validation:Enum=Permanent;Session
|
||||
type CookieLifetimeType string
|
||||
|
||||
const (
|
||||
// SessionCookieLifetimeType specifies the type for a session
|
||||
// cookie.
|
||||
//
|
||||
// Support: Core
|
||||
SessionCookieLifetimeType CookieLifetimeType = "Session"
|
||||
|
||||
// PermanentCookieLifetimeType specifies the type for a permanent
|
||||
// cookie.
|
||||
//
|
||||
// Support: Extended
|
||||
PermanentCookieLifetimeType CookieLifetimeType = "Permanent"
|
||||
)
|
||||
|
||||
// +kubebuilder:validation:XValidation:message="numerator must be less than or equal to denominator",rule="self.numerator <= self.denominator"
|
||||
type Fraction struct {
|
||||
// +kubebuilder:validation:Minimum=0
|
||||
// +required
|
||||
Numerator int32 `json:"numerator"`
|
||||
|
||||
// +optional
|
||||
// +kubebuilder:default=100
|
||||
// +kubebuilder:validation:Minimum=1
|
||||
Denominator *int32 `json:"denominator,omitempty"`
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ func (in *BackendObjectReference) DeepCopyInto(out *BackendObjectReference) {
|
||||
}
|
||||
if in.Port != nil {
|
||||
in, out := &in.Port, &out.Port
|
||||
*out = new(PortNumber)
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
@@ -107,6 +107,111 @@ func (in *CommonRouteSpec) DeepCopy() *CommonRouteSpec {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CookieConfig) DeepCopyInto(out *CookieConfig) {
|
||||
*out = *in
|
||||
if in.LifetimeType != nil {
|
||||
in, out := &in.LifetimeType, &out.LifetimeType
|
||||
*out = new(CookieLifetimeType)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CookieConfig.
|
||||
func (in *CookieConfig) DeepCopy() *CookieConfig {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(CookieConfig)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ForwardBodyConfig) DeepCopyInto(out *ForwardBodyConfig) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ForwardBodyConfig.
|
||||
func (in *ForwardBodyConfig) DeepCopy() *ForwardBodyConfig {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ForwardBodyConfig)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Fraction) DeepCopyInto(out *Fraction) {
|
||||
*out = *in
|
||||
if in.Denominator != nil {
|
||||
in, out := &in.Denominator, &out.Denominator
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Fraction.
|
||||
func (in *Fraction) DeepCopy() *Fraction {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Fraction)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *GRPCAuthConfig) DeepCopyInto(out *GRPCAuthConfig) {
|
||||
*out = *in
|
||||
if in.AllowedRequestHeaders != nil {
|
||||
in, out := &in.AllowedRequestHeaders, &out.AllowedRequestHeaders
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GRPCAuthConfig.
|
||||
func (in *GRPCAuthConfig) DeepCopy() *GRPCAuthConfig {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(GRPCAuthConfig)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPAuthConfig) DeepCopyInto(out *HTTPAuthConfig) {
|
||||
*out = *in
|
||||
if in.AllowedRequestHeaders != nil {
|
||||
in, out := &in.AllowedRequestHeaders, &out.AllowedRequestHeaders
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AllowedResponseHeaders != nil {
|
||||
in, out := &in.AllowedResponseHeaders, &out.AllowedResponseHeaders
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPAuthConfig.
|
||||
func (in *HTTPAuthConfig) DeepCopy() *HTTPAuthConfig {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HTTPAuthConfig)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPBackendRef) DeepCopyInto(out *HTTPBackendRef) {
|
||||
*out = *in
|
||||
@@ -131,6 +236,79 @@ func (in *HTTPBackendRef) DeepCopy() *HTTPBackendRef {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPCORSFilter) DeepCopyInto(out *HTTPCORSFilter) {
|
||||
*out = *in
|
||||
if in.AllowOrigins != nil {
|
||||
in, out := &in.AllowOrigins, &out.AllowOrigins
|
||||
*out = make([]CORSOrigin, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AllowCredentials != nil {
|
||||
in, out := &in.AllowCredentials, &out.AllowCredentials
|
||||
*out = new(bool)
|
||||
**out = **in
|
||||
}
|
||||
if in.AllowMethods != nil {
|
||||
in, out := &in.AllowMethods, &out.AllowMethods
|
||||
*out = make([]HTTPMethodWithWildcard, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.AllowHeaders != nil {
|
||||
in, out := &in.AllowHeaders, &out.AllowHeaders
|
||||
*out = make([]HTTPHeaderName, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.ExposeHeaders != nil {
|
||||
in, out := &in.ExposeHeaders, &out.ExposeHeaders
|
||||
*out = make([]HTTPHeaderName, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPCORSFilter.
|
||||
func (in *HTTPCORSFilter) DeepCopy() *HTTPCORSFilter {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HTTPCORSFilter)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPExternalAuthFilter) DeepCopyInto(out *HTTPExternalAuthFilter) {
|
||||
*out = *in
|
||||
in.BackendRef.DeepCopyInto(&out.BackendRef)
|
||||
if in.GRPCAuthConfig != nil {
|
||||
in, out := &in.GRPCAuthConfig, &out.GRPCAuthConfig
|
||||
*out = new(GRPCAuthConfig)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.HTTPAuthConfig != nil {
|
||||
in, out := &in.HTTPAuthConfig, &out.HTTPAuthConfig
|
||||
*out = new(HTTPAuthConfig)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ForwardBody != nil {
|
||||
in, out := &in.ForwardBody, &out.ForwardBody
|
||||
*out = new(ForwardBodyConfig)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPExternalAuthFilter.
|
||||
func (in *HTTPExternalAuthFilter) DeepCopy() *HTTPExternalAuthFilter {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HTTPExternalAuthFilter)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPHeader) DeepCopyInto(out *HTTPHeader) {
|
||||
*out = *in
|
||||
@@ -276,6 +454,16 @@ func (in *HTTPQueryParamMatch) DeepCopy() *HTTPQueryParamMatch {
|
||||
func (in *HTTPRequestMirrorFilter) DeepCopyInto(out *HTTPRequestMirrorFilter) {
|
||||
*out = *in
|
||||
in.BackendRef.DeepCopyInto(&out.BackendRef)
|
||||
if in.Percent != nil {
|
||||
in, out := &in.Percent, &out.Percent
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
}
|
||||
if in.Fraction != nil {
|
||||
in, out := &in.Fraction, &out.Fraction
|
||||
*out = new(Fraction)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -309,7 +497,7 @@ func (in *HTTPRequestRedirectFilter) DeepCopyInto(out *HTTPRequestRedirectFilter
|
||||
}
|
||||
if in.Port != nil {
|
||||
in, out := &in.Port, &out.Port
|
||||
*out = new(PortNumber)
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
}
|
||||
if in.StatusCode != nil {
|
||||
@@ -386,6 +574,16 @@ func (in *HTTPRouteFilter) DeepCopyInto(out *HTTPRouteFilter) {
|
||||
*out = new(HTTPURLRewriteFilter)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.CORS != nil {
|
||||
in, out := &in.CORS, &out.CORS
|
||||
*out = new(HTTPCORSFilter)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ExternalAuth != nil {
|
||||
in, out := &in.ExternalAuth, &out.ExternalAuth
|
||||
*out = new(HTTPExternalAuthFilter)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ExtensionRef != nil {
|
||||
in, out := &in.ExtensionRef, &out.ExtensionRef
|
||||
*out = new(LocalObjectReference)
|
||||
@@ -477,9 +675,45 @@ func (in *HTTPRouteMatch) DeepCopy() *HTTPRouteMatch {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPRouteRetry) DeepCopyInto(out *HTTPRouteRetry) {
|
||||
*out = *in
|
||||
if in.Codes != nil {
|
||||
in, out := &in.Codes, &out.Codes
|
||||
*out = make([]HTTPRouteRetryStatusCode, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Attempts != nil {
|
||||
in, out := &in.Attempts, &out.Attempts
|
||||
*out = new(int)
|
||||
**out = **in
|
||||
}
|
||||
if in.Backoff != nil {
|
||||
in, out := &in.Backoff, &out.Backoff
|
||||
*out = new(Duration)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteRetry.
|
||||
func (in *HTTPRouteRetry) DeepCopy() *HTTPRouteRetry {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HTTPRouteRetry)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPRouteRule) DeepCopyInto(out *HTTPRouteRule) {
|
||||
*out = *in
|
||||
if in.Name != nil {
|
||||
in, out := &in.Name, &out.Name
|
||||
*out = new(SectionName)
|
||||
**out = **in
|
||||
}
|
||||
if in.Matches != nil {
|
||||
in, out := &in.Matches, &out.Matches
|
||||
*out = make([]HTTPRouteMatch, len(*in))
|
||||
@@ -506,6 +740,16 @@ func (in *HTTPRouteRule) DeepCopyInto(out *HTTPRouteRule) {
|
||||
*out = new(HTTPRouteTimeouts)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.Retry != nil {
|
||||
in, out := &in.Retry, &out.Retry
|
||||
*out = new(HTTPRouteRetry)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.SessionPersistence != nil {
|
||||
in, out := &in.SessionPersistence, &out.SessionPersistence
|
||||
*out = new(SessionPersistence)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -633,6 +877,27 @@ func (in *LocalObjectReference) DeepCopy() *LocalObjectReference {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ObjectReference) DeepCopyInto(out *ObjectReference) {
|
||||
*out = *in
|
||||
if in.Namespace != nil {
|
||||
in, out := &in.Namespace, &out.Namespace
|
||||
*out = new(Namespace)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectReference.
|
||||
func (in *ObjectReference) DeepCopy() *ObjectReference {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ObjectReference)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ParentReference) DeepCopyInto(out *ParentReference) {
|
||||
*out = *in
|
||||
@@ -658,7 +923,7 @@ func (in *ParentReference) DeepCopyInto(out *ParentReference) {
|
||||
}
|
||||
if in.Port != nil {
|
||||
in, out := &in.Port, &out.Port
|
||||
*out = new(PortNumber)
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
@@ -751,3 +1016,44 @@ func (in *SecretObjectReference) DeepCopy() *SecretObjectReference {
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SessionPersistence) DeepCopyInto(out *SessionPersistence) {
|
||||
*out = *in
|
||||
if in.SessionName != nil {
|
||||
in, out := &in.SessionName, &out.SessionName
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
if in.AbsoluteTimeout != nil {
|
||||
in, out := &in.AbsoluteTimeout, &out.AbsoluteTimeout
|
||||
*out = new(Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.IdleTimeout != nil {
|
||||
in, out := &in.IdleTimeout, &out.IdleTimeout
|
||||
*out = new(Duration)
|
||||
**out = **in
|
||||
}
|
||||
if in.Type != nil {
|
||||
in, out := &in.Type, &out.Type
|
||||
*out = new(SessionPersistenceType)
|
||||
**out = **in
|
||||
}
|
||||
if in.CookieConfig != nil {
|
||||
in, out := &in.CookieConfig, &out.CookieConfig
|
||||
*out = new(CookieConfig)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SessionPersistence.
|
||||
func (in *SessionPersistence) DeepCopy() *SessionPersistence {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(SessionPersistence)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
// Package v1beta1 contains API Schema definitions for the
|
||||
// gateway.networking.k8s.io API group.
|
||||
|
||||
// +k8s:deepcopy-gen=package
|
||||
|
||||
package v1beta1
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
)
|
||||
|
||||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:resource:categories=gateway-api
|
||||
// +kubebuilder:storageversion
|
||||
@@ -40,6 +41,7 @@ type HTTPRoute struct {
|
||||
Status HTTPRouteStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// HTTPRouteList contains a list of HTTPRoute.
|
||||
|
||||
@@ -2,11 +2,14 @@
|
||||
// +build !ignore_autogenerated
|
||||
|
||||
/*
|
||||
Copyright The Kubernetes Authors.
|
||||
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.
|
||||
@@ -14,13 +17,13 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
// Code generated by deepcopy-gen. DO NOT EDIT.
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
@@ -46,6 +49,7 @@ func (in *BackendObjectReference) DeepCopyInto(out *BackendObjectReference) {
|
||||
*out = new(PortNumber)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendObjectReference.
|
||||
@@ -67,6 +71,7 @@ func (in *BackendRef) DeepCopyInto(out *BackendRef) {
|
||||
*out = new(int32)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackendRef.
|
||||
@@ -89,6 +94,7 @@ func (in *CommonRouteSpec) DeepCopyInto(out *CommonRouteSpec) {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonRouteSpec.
|
||||
@@ -112,6 +118,7 @@ func (in *HTTPBackendRef) DeepCopyInto(out *HTTPBackendRef) {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPBackendRef.
|
||||
@@ -127,6 +134,7 @@ func (in *HTTPBackendRef) DeepCopy() *HTTPBackendRef {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPHeader) DeepCopyInto(out *HTTPHeader) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPHeader.
|
||||
@@ -157,6 +165,7 @@ func (in *HTTPHeaderFilter) DeepCopyInto(out *HTTPHeaderFilter) {
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPHeaderFilter.
|
||||
@@ -177,6 +186,7 @@ func (in *HTTPHeaderMatch) DeepCopyInto(out *HTTPHeaderMatch) {
|
||||
*out = new(HeaderMatchType)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPHeaderMatch.
|
||||
@@ -202,6 +212,7 @@ func (in *HTTPPathMatch) DeepCopyInto(out *HTTPPathMatch) {
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPPathMatch.
|
||||
@@ -227,6 +238,7 @@ func (in *HTTPPathModifier) DeepCopyInto(out *HTTPPathModifier) {
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPPathModifier.
|
||||
@@ -247,6 +259,7 @@ func (in *HTTPQueryParamMatch) DeepCopyInto(out *HTTPQueryParamMatch) {
|
||||
*out = new(QueryParamMatchType)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPQueryParamMatch.
|
||||
@@ -263,6 +276,7 @@ func (in *HTTPQueryParamMatch) DeepCopy() *HTTPQueryParamMatch {
|
||||
func (in *HTTPRequestMirrorFilter) DeepCopyInto(out *HTTPRequestMirrorFilter) {
|
||||
*out = *in
|
||||
in.BackendRef.DeepCopyInto(&out.BackendRef)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRequestMirrorFilter.
|
||||
@@ -303,6 +317,7 @@ func (in *HTTPRequestRedirectFilter) DeepCopyInto(out *HTTPRequestRedirectFilter
|
||||
*out = new(int)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRequestRedirectFilter.
|
||||
@@ -322,6 +337,7 @@ func (in *HTTPRoute) DeepCopyInto(out *HTTPRoute) {
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
in.Status.DeepCopyInto(&out.Status)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRoute.
|
||||
@@ -375,6 +391,7 @@ func (in *HTTPRouteFilter) DeepCopyInto(out *HTTPRouteFilter) {
|
||||
*out = new(LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteFilter.
|
||||
@@ -399,6 +416,7 @@ func (in *HTTPRouteList) DeepCopyInto(out *HTTPRouteList) {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteList.
|
||||
@@ -446,6 +464,7 @@ func (in *HTTPRouteMatch) DeepCopyInto(out *HTTPRouteMatch) {
|
||||
*out = new(HTTPMethod)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteMatch.
|
||||
@@ -482,6 +501,7 @@ func (in *HTTPRouteRule) DeepCopyInto(out *HTTPRouteRule) {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteRule.
|
||||
@@ -510,6 +530,7 @@ func (in *HTTPRouteSpec) DeepCopyInto(out *HTTPRouteSpec) {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteSpec.
|
||||
@@ -526,6 +547,7 @@ func (in *HTTPRouteSpec) DeepCopy() *HTTPRouteSpec {
|
||||
func (in *HTTPRouteStatus) DeepCopyInto(out *HTTPRouteStatus) {
|
||||
*out = *in
|
||||
in.RouteStatus.DeepCopyInto(&out.RouteStatus)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRouteStatus.
|
||||
@@ -551,6 +573,7 @@ func (in *HTTPURLRewriteFilter) DeepCopyInto(out *HTTPURLRewriteFilter) {
|
||||
*out = new(HTTPPathModifier)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPURLRewriteFilter.
|
||||
@@ -566,6 +589,7 @@ func (in *HTTPURLRewriteFilter) DeepCopy() *HTTPURLRewriteFilter {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *LocalObjectReference) DeepCopyInto(out *LocalObjectReference) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalObjectReference.
|
||||
@@ -606,6 +630,7 @@ func (in *ParentReference) DeepCopyInto(out *ParentReference) {
|
||||
*out = new(PortNumber)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ParentReference.
|
||||
@@ -629,6 +654,7 @@ func (in *RouteParentStatus) DeepCopyInto(out *RouteParentStatus) {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteParentStatus.
|
||||
@@ -651,6 +677,7 @@ func (in *RouteStatus) DeepCopyInto(out *RouteStatus) {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteStatus.
|
||||
@@ -681,6 +708,7 @@ func (in *SecretObjectReference) DeepCopyInto(out *SecretObjectReference) {
|
||||
*out = new(Namespace)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretObjectReference.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package gloo
|
||||
|
||||
const (
|
||||
GlooGroupName = "gloo.solo.io"
|
||||
GatewayGroupName = "gateway.solo.io"
|
||||
GlooGroupName = "gloo.solo.io"
|
||||
)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
v1 "github.com/fluxcd/flagger/pkg/apis/gloo/gateway/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
@@ -44,7 +43,7 @@ type UpstreamSslConfig struct {
|
||||
|
||||
/** SSLSecrets -- only one of these should be set */
|
||||
*UpstreamSslConfig_Sds `json:"sds,omitempty"`
|
||||
SecretRef *v1.ResourceRef `json:"secretRef,omitempty"`
|
||||
SecretRef *ResourceRef `json:"secretRef,omitempty"`
|
||||
*UpstreamSslConfig_SslFiles `json:"sslFiles,omitempty"`
|
||||
}
|
||||
|
||||
@@ -139,3 +138,9 @@ type UpstreamList struct {
|
||||
|
||||
Items []Upstream `json:"items"`
|
||||
}
|
||||
|
||||
// ResourceRef references resources across namespaces
|
||||
type ResourceRef struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Namespace string `json:"namespace,omitempty"`
|
||||
}
|
||||
@@ -22,7 +22,6 @@ limitations under the License.
|
||||
package v1
|
||||
|
||||
import (
|
||||
gatewayv1 "github.com/fluxcd/flagger/pkg/apis/gloo/gateway/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
@@ -228,6 +227,22 @@ func (in *LoadBalancerConfigRoundRobin) DeepCopy() *LoadBalancerConfigRoundRobin
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ResourceRef) DeepCopyInto(out *ResourceRef) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResourceRef.
|
||||
func (in *ResourceRef) DeepCopy() *ResourceRef {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(ResourceRef)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SlowStartConfig) DeepCopyInto(out *SlowStartConfig) {
|
||||
*out = *in
|
||||
@@ -403,7 +418,7 @@ func (in *UpstreamSslConfig) DeepCopyInto(out *UpstreamSslConfig) {
|
||||
}
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(gatewayv1.ResourceRef)
|
||||
*out = new(ResourceRef)
|
||||
**out = **in
|
||||
}
|
||||
if in.UpstreamSslConfig_SslFiles != nil {
|
||||
5
pkg/apis/gloogateway/register.go
Normal file
5
pkg/apis/gloogateway/register.go
Normal file
@@ -0,0 +1,5 @@
|
||||
package gloogateway
|
||||
|
||||
const (
|
||||
GatewayGroupName = "gateway.solo.io"
|
||||
)
|
||||
@@ -1,14 +1,14 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/fluxcd/flagger/pkg/apis/gloo"
|
||||
"github.com/fluxcd/flagger/pkg/apis/gloogateway"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: gloo.GatewayGroupName, Version: "v1"}
|
||||
var SchemeGroupVersion = schema.GroupVersion{Group: gloogateway.GatewayGroupName, Version: "v1"}
|
||||
|
||||
// Kind takes an unqualified kind and returns back a Group qualified GroupKind
|
||||
func Kind(kind string) schema.GroupKind {
|
||||
@@ -103,6 +103,8 @@ type AdvancedConfig struct {
|
||||
|
||||
// HorizontalPodAutoscalerConfig specifies horizontal scale config
|
||||
type HorizontalPodAutoscalerConfig struct {
|
||||
// +optional
|
||||
Name string `json:"name,omitempty"`
|
||||
// +optional
|
||||
Behavior *autoscalingv2beta2.HorizontalPodAutoscalerBehavior `json:"behavior,omitempty"`
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
package traefik
|
||||
|
||||
const (
|
||||
GroupName = "traefik.containo.us"
|
||||
GroupName = "traefik.io"
|
||||
)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// +k8s:deepcopy-gen=package
|
||||
|
||||
// Package v1alpha1 is the v1alpha1 version of the API.
|
||||
// +groupName=traefik.containo.us
|
||||
// +groupName=traefik.io
|
||||
package v1alpha1
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/util/retry"
|
||||
|
||||
@@ -154,12 +155,10 @@ func (c *DeploymentController) ScaleToZero(cd *flaggerv1.Canary) error {
|
||||
return fmt.Errorf("deployment %s.%s get query error: %w", targetName, cd.Namespace, err)
|
||||
}
|
||||
|
||||
depCopy := dep.DeepCopy()
|
||||
depCopy.Spec.Replicas = int32p(0)
|
||||
|
||||
_, err = c.kubeClient.AppsV1().Deployments(dep.Namespace).Update(context.TODO(), depCopy, metav1.UpdateOptions{})
|
||||
patch := []byte(fmt.Sprintf(`{"spec":{"replicas": %d}}`, 0))
|
||||
_, err = c.kubeClient.AppsV1().Deployments(dep.Namespace).Patch(context.TODO(), dep.GetName(), types.MergePatchType, patch, metav1.PatchOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("deployment %s.%s update query error: %w", targetName, cd.Namespace, err)
|
||||
return fmt.Errorf("deployment %s.%s patch query error: %w", targetName, cd.Namespace, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -210,12 +209,11 @@ func (c *DeploymentController) ScaleFromZero(cd *flaggerv1.Canary) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
depCopy := dep.DeepCopy()
|
||||
depCopy.Spec.Replicas = replicas
|
||||
|
||||
_, err = c.kubeClient.AppsV1().Deployments(dep.Namespace).Update(context.TODO(), depCopy, metav1.UpdateOptions{})
|
||||
patch := []byte(fmt.Sprintf(`{"spec":{"replicas": %d}}`, *replicas))
|
||||
_, err = c.kubeClient.AppsV1().Deployments(dep.Namespace).Patch(context.TODO(), dep.GetName(), types.MergePatchType, patch, metav1.PatchOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("scaling up %s.%s to %v failed: %v", depCopy.GetName(), depCopy.Namespace, replicas, err)
|
||||
return fmt.Errorf("scaling up %s.%s to %d failed: %v", dep.GetName(), dep.Namespace, *replicas, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -388,11 +386,10 @@ func (c *DeploymentController) scale(cd *flaggerv1.Canary, replicas int32) error
|
||||
return fmt.Errorf("deployment %s.%s query error: %w", targetName, cd.Namespace, err)
|
||||
}
|
||||
|
||||
depCopy := dep.DeepCopy()
|
||||
depCopy.Spec.Replicas = int32p(replicas)
|
||||
_, err = c.kubeClient.AppsV1().Deployments(dep.Namespace).Update(context.TODO(), depCopy, metav1.UpdateOptions{})
|
||||
patch := []byte(fmt.Sprintf(`{"spec":{"replicas": %d}}`, replicas))
|
||||
_, err = c.kubeClient.AppsV1().Deployments(dep.Namespace).Patch(context.TODO(), dep.GetName(), types.MergePatchType, patch, metav1.PatchOptions{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("scaling %s.%s to %v failed: %w", depCopy.GetName(), depCopy.Namespace, replicas, err)
|
||||
return fmt.Errorf("scaling %s.%s to %d failed: %w", dep.GetName(), dep.Namespace, replicas, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user