diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index b4717dc18..8c1dcd419 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -3,11 +3,11 @@ PLEASE READ THIS MESSAGE. Documentation: - for Traefik v2: use branch v2.11 (fixes only) -- for Traefik v3: use branch v3.5 +- for Traefik v3: use branch v3.6 Bug: - for Traefik v2: use branch v2.11 (security fixes only) -- for Traefik v3: use branch v3.5 +- for Traefik v3: use branch v3.6 Enhancements: - use branch master diff --git a/.github/workflows/check_doc.yaml b/.github/workflows/check_doc.yaml new file mode 100644 index 000000000..5fea9809c --- /dev/null +++ b/.github/workflows/check_doc.yaml @@ -0,0 +1,63 @@ +name: Check Documentation + +on: + pull_request: + branches: + - '*' + paths: + - '.github/workflows/check_doc.yaml' + - 'docs/**' + +jobs: + + docs: + name: lint, build and verify + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Install markdownlint + run: | + npm install --global markdownlint@0.29.0 markdownlint-cli@0.35.0 + + - name: Lint + run: ./docs/scripts/lint.sh docs + + - name: Setup python + uses: actions/setup-python@v6 + with: + python-version: '3.12' + cache: 'pip' + cache-dependency-path: "./docs/requirements.txt" + + - name: Build documentation + working-directory: ./docs + run: | + pip install -r requirements.txt + mkdocs build --strict + + - name: Setup ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.4' + + - name: Install html-proofer + run: | + gem install nokogiri --version 1.18.6 --no-document -- --use-system-libraries + gem install html-proofer --version 5.0.10 --no-document -- --use-system-libraries + env: + NOKOGIRI_USE_SYSTEM_LIBRARIES: "true" + + # Comes from https://github.com/gjtorikian/html-proofer?tab=readme-ov-file#caching-with-continuous-integration + - name: Cache HTMLProofer + uses: actions/cache@v4 + with: + path: tmp/.htmlproofer + key: ${{ runner.os }}-htmlproofer + + - name: Verify + run: ./docs/scripts/verify.sh docs/site diff --git a/.github/workflows/check_doc.yml b/.github/workflows/check_doc.yml deleted file mode 100644 index 48afa3298..000000000 --- a/.github/workflows/check_doc.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Check Documentation - -on: - pull_request: - branches: - - '*' - -jobs: - - docs: - name: Check, verify and build documentation - runs-on: ubuntu-latest - - steps: - - name: Check out code - uses: actions/checkout@v5 - with: - fetch-depth: 0 - - - name: Check documentation - run: make docs-pull-images docs - env: - # These variables are not passed to workflows that are triggered by a pull request from a fork. - DOCS_VERIFY_SKIP: ${{ vars.DOCS_VERIFY_SKIP }} - DOCS_LINT_SKIP: ${{ vars.DOCS_LINT_SKIP }} diff --git a/.github/workflows/documentation.yml b/.github/workflows/documentation.yaml similarity index 100% rename from .github/workflows/documentation.yml rename to .github/workflows/documentation.yaml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index db2c59183..03d864762 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -10,7 +10,7 @@ env: CGO_ENABLED: 0 VERSION: ${{ github.ref_name }} TRAEFIKER_EMAIL: "traefiker@traefik.io" - CODENAME: chabichou + CODENAME: ramequin jobs: @@ -24,7 +24,7 @@ jobs: strategy: matrix: - os: [ linux-amd64, linux-386, linux-arm, linux-arm64, linux-ppc64le, linux-s390x, linux-riscv64, darwin, windows-amd64, windows-arm64, windows-386, freebsd, openbsd ] + os: [ linux-amd64, linux-386, linux-arm, linux-arm64, linux-ppc64le, linux-s390x, linux-riscv64, darwin-amd64, darwin-arm64, windows-amd64, windows-arm64, windows-386, freebsd-amd64, freebsd-386, openbsd-amd64, openbsd-386, openbsd-riscv64 ] needs: - build-webui @@ -130,7 +130,7 @@ jobs: --exclude dist . chown -R "$(id -u)":"$(id -g)" dist/ - gh release create ${VERSION} ./dist/**/traefik*.{zip,tar.gz} ./dist/traefik*.{tar.gz,txt} --repo traefik/traefik --title ${VERSION} --notes ${VERSION} --latest=true + gh release create ${VERSION} ./dist/**/traefik*.{zip,tar.gz} ./dist/traefik*.{tar.gz,txt} --repo traefik/traefik --title ${VERSION} --notes ${VERSION} --latest=false ./script/deploy.sh diff --git a/.github/workflows/template-webui.yaml b/.github/workflows/template-webui.yaml index b841c90f5..16af7d1e4 100644 --- a/.github/workflows/template-webui.yaml +++ b/.github/workflows/template-webui.yaml @@ -1,6 +1,8 @@ name: Build Web UI on: workflow_call: {} +env: + SAFE_CHAIN_MINIMUM_PACKAGE_AGE_HOURS: 360 # 15 days jobs: build-webui: @@ -22,6 +24,12 @@ jobs: cache: yarn cache-dependency-path: webui/yarn.lock + - name: Setup safe-chain + working-directory: ./webui + run: | + npm i -g @aikidosec/safe-chain + safe-chain setup-ci + - name: Build webui working-directory: ./webui run: | diff --git a/.github/workflows/test-conformance.yaml b/.github/workflows/test-gateway-api-conformance.yaml similarity index 56% rename from .github/workflows/test-conformance.yaml rename to .github/workflows/test-gateway-api-conformance.yaml index 6373d8f92..fbeae3d27 100644 --- a/.github/workflows/test-conformance.yaml +++ b/.github/workflows/test-gateway-api-conformance.yaml @@ -5,18 +5,19 @@ on: branches: - '*' paths: - - '.github/workflows/test-conformance.yaml' + - '.github/workflows/test-gateway-api-conformance.yaml' - 'pkg/provider/kubernetes/gateway/**' - - 'integration/fixtures/k8s-conformance/**' - - 'integration/k8s_conformance_test.go' + - 'integration/fixtures/gateway-api-conformance/**' + - 'integration/gateway_api_conformance_test.go' + - 'integration/integration_test.go' env: - GO_VERSION: '1.23' + GO_VERSION: '1.24' CGO_ENABLED: 0 jobs: - test-conformance: + test-gateway-api-conformance: runs-on: ubuntu-latest steps: @@ -30,7 +31,11 @@ jobs: with: go-version: ${{ env.GO_VERSION }} - - name: K8s Gateway API conformance test and report + - name: Avoid generating webui + run: | + touch webui/static/index.html + + - name: Gateway API conformance test and report run: | make test-gateway-api-conformance git diff --exit-code diff --git a/.github/workflows/test-integration.yaml b/.github/workflows/test-integration.yaml index 0f7504c06..5125d19c6 100644 --- a/.github/workflows/test-integration.yaml +++ b/.github/workflows/test-integration.yaml @@ -30,6 +30,10 @@ jobs: go-version: ${{ env.GO_VERSION }} check-latest: true + - name: Avoid generating webui + run: | + touch webui/static/index.html + - name: Build binary run: make binary-linux-amd64 diff --git a/.github/workflows/test-knative-conformance.yaml b/.github/workflows/test-knative-conformance.yaml new file mode 100644 index 000000000..4e7b8be6a --- /dev/null +++ b/.github/workflows/test-knative-conformance.yaml @@ -0,0 +1,51 @@ +name: Test Knative conformance + +on: + pull_request: + branches: + - '*' + paths: + - '.github/workflows/test-knative-conformance.yaml' + - 'pkg/provider/kubernetes/knative/**' + - 'integration/fixtures/knative/**' + - 'integration/knative_conformance_test.go' + - 'integration/integration_test.go' + +env: + GO_VERSION: '1.24' + CGO_ENABLED: 0 + +jobs: + + test-knative-conformance: + runs-on: ubuntu-latest + + steps: + - name: Check out code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Go ${{ env.GO_VERSION }} + uses: actions/setup-go@v5 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Set up KO + uses: ko-build/setup-ko@v0.6 + env: + KO_DOCKER_REPO: ko.local + + - name: Upload Test Images + run: | + # Download the test image templates. + go mod vendor + ./integration/fixtures/knative/upload-test-images.sh + + - name: Avoid generating webui + run: | + touch webui/static/index.html + + - name: Knative conformance test + run: | + make test-knative-conformance diff --git a/.github/workflows/test-unit.yaml b/.github/workflows/test-unit.yaml index d8d612a51..d60e359f0 100644 --- a/.github/workflows/test-unit.yaml +++ b/.github/workflows/test-unit.yaml @@ -79,6 +79,11 @@ jobs: cache: 'yarn' cache-dependency-path: webui/yarn.lock + - name: Setup safe-chain + run: | + npm i -g @aikidosec/safe-chain + safe-chain setup-ci + - name: UI unit tests working-directory: ./webui env: diff --git a/.github/workflows/validate.yaml b/.github/workflows/validate.yaml index 470816bd5..ed46d1599 100644 --- a/.github/workflows/validate.yaml +++ b/.github/workflows/validate.yaml @@ -8,7 +8,7 @@ on: env: GO_VERSION: '1.24' GOLANGCI_LINT_VERSION: v2.0.2 - MISSPELL_VERSION: v0.6.0 + MISSPELL_VERSION: v0.7.0 jobs: diff --git a/.gitignore b/.gitignore index 1754b1c70..741873dc9 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,4 @@ plugins-storage/ plugins-local/ traefik_changelog.md integration/tailscale.secret -integration/conformance-reports/**/experimental-dev-default-report.yaml +integration/gateway-api-conformance-reports/**/experimental-dev-default-report.yaml diff --git a/.golangci.yml b/.golangci.yml index 25de20ba1..50afac812 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -318,6 +318,12 @@ linters: - recvcheck - path: pkg/proxy/httputil/bufferpool.go text: 'SA6002: argument should be pointer-like to avoid allocations' + - path: integration/integration_test.go + text: 'var (gatewayAPIConformanceRunTest|traefikVersion) is unused' + - path: pkg/server/router/router.go + text: 'appendAssign: append result not assigned to the same slice' + linters: + - gocritic paths: - pkg/provider/kubernetes/crd/generated/ diff --git a/.goreleaser.yml.tmpl b/.goreleaser.yml.tmpl index fbd27891b..6a0e9e878 100644 --- a/.goreleaser.yml.tmpl +++ b/.goreleaser.yml.tmpl @@ -54,10 +54,12 @@ changelog: archives: - id: traefik name_template: '{{ .ProjectName }}_v{{ .Version }}_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}' - format: tar.gz + formats: + - tar.gz format_overrides: - goos: windows - format: zip + formats: + - zip files: - LICENSE.md - CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md index bf46d9ae7..135a288b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,276 @@ +## [v3.6.7](https://github.com/traefik/traefik/tree/v3.6.7) (2026-01-14) +[All Commits](https://github.com/traefik/traefik/compare/v3.6.6...v3.6.7) + +**Bug fixes:** +- **[acme]** Bump github.com/go-acme/lego/v4 to v4.31.0 ([#12529](https://github.com/traefik/traefik/pull/12529) by [ldez](https://github.com/ldez)) +- **[acme]** Add missing renew options ([#12467](https://github.com/traefik/traefik/pull/12467) by [ldez](https://github.com/ldez)) +- **[acme]** Replace hardcoded references to LetsEncrypt in log messages ([#12464](https://github.com/traefik/traefik/pull/12464) by [schildbach](https://github.com/schildbach)) +- **[k8s/ingress-nginx]** Fix use-regex nginx annotation ([#12531](https://github.com/traefik/traefik/pull/12531) by [LBF38](https://github.com/LBF38)) +- **[k8s/ingress-nginx]** Prevent Ingress Nginx provider http router to attach to an entrypoint with TLS ([#12528](https://github.com/traefik/traefik/pull/12528) by [rtribotte](https://github.com/rtribotte)) +- **[k8s/ingress]** Fix panic for empty defaultBackend and defaultBackend without resources ([#12509](https://github.com/traefik/traefik/pull/12509) by [gndz07](https://github.com/gndz07)) +- **[k8s]** Fix condition used for serving and fenced endpoints ([#12521](https://github.com/traefik/traefik/pull/12521) by [LBF38](https://github.com/LBF38)) +- **[webui]** Validate X-Forwarded-Prefix value for dashboard redirect ([#12514](https://github.com/traefik/traefik/pull/12514) by [LBF38](https://github.com/LBF38)) +- **[acme]** Add timeout to ACME-TLS/1 challenge handshake ([#12516](https://github.com/traefik/traefik/pull/12516) by [LBF38](https://github.com/LBF38)) +- **[server]** Make encoded character options opt-in ([#12540](https://github.com/traefik/traefik/pull/12540) by [gndz07](https://github.com/gndz07)) + +**Documentation:** +- **[docker/swarm]** Update swarm.md traefik version ([#12508](https://github.com/traefik/traefik/pull/12508) by [DBouraoui](https://github.com/DBouraoui)) +- **[k8s/ingress-nginx]** Fix ingress-nginx annotations documentation ([#12510](https://github.com/traefik/traefik/pull/12510) by [nmengin](https://github.com/nmengin)) +- **[k8s]** Fix Kubernetes reference yml file ([#12406](https://github.com/traefik/traefik/pull/12406) by [mmatur](https://github.com/mmatur)) +- Fix code copy button positioning ([#12520](https://github.com/traefik/traefik/pull/12520) by [AnuragEkkati](https://github.com/AnuragEkkati)) +- Fix typo in kubernetes.md ([#12515](https://github.com/traefik/traefik/pull/12515) by [EdwardSalkeld](https://github.com/EdwardSalkeld)) +- Bring back security section on API & Dashboard documentation page ([#12507](https://github.com/traefik/traefik/pull/12507) by [gndz07](https://github.com/gndz07)) +- Fix link description in Traefik Proxy documentation ([#12488](https://github.com/traefik/traefik/pull/12488) by [schaerfo](https://github.com/schaerfo)) +- Add product comparison matrix and features page ([#12037](https://github.com/traefik/traefik/pull/12037) by [sheddy-traefik](https://github.com/sheddy-traefik)) + +**Misc:** +- Merge branch v2.11 into v3.6 ([#12552](https://github.com/traefik/traefik/pull/12552) by [rtribotte](https://github.com/rtribotte)) +- Merge branch v2.11 into v3.6 ([#12533](https://github.com/traefik/traefik/pull/12533) by [mmatur](https://github.com/mmatur)) +- Merge branch v2.11 into v3.6 ([#12497](https://github.com/traefik/traefik/pull/12497) by [mmatur](https://github.com/mmatur)) + +## [v2.11.35](https://github.com/traefik/traefik/tree/v2.11.35) (2026-01-14) +[All Commits](https://github.com/traefik/traefik/compare/v2.11.34...v2.11.35) + +**Bug fixes:** +- **[acme]** Add timeout to ACME-TLS/1 challenge handshake ([#12516](https://github.com/traefik/traefik/pull/12516) by [LBF38](https://github.com/LBF38)) +- **[server]** Make encoded character options opt-in ([#12540](https://github.com/traefik/traefik/pull/12540) by [gndz07](https://github.com/gndz07)) + +## [v3.6.6](https://github.com/traefik/traefik/tree/v3.6.6) (2025-12-29) +[All Commits](https://github.com/traefik/traefik/compare/v3.6.5...v3.6.6) + +**Bug fixes:** +- **[acme]** Bump github.com/go-acme/lego/v4 to v4.30.1 ([#12432](https://github.com/traefik/traefik/pull/12432) by [ldez](https://github.com/ldez)) +- **[http3]** Bump github.com/quic-go/quic-go to v0.58.0 ([#12448](https://github.com/traefik/traefik/pull/12448) by [GreyXor](https://github.com/GreyXor)) +- **[redis]** Fix mutually exclusive verification for Redis ([#12442](https://github.com/traefik/traefik/pull/12442) by [juliens](https://github.com/juliens)) +- **[server]** Fix deny encoded characters ([#12454](https://github.com/traefik/traefik/pull/12454) by [rtribotte](https://github.com/rtribotte)) + +**Documentation:** +- **[k8s/ingress,k8s]** Fix Kubernetes Ingress provider documentation ([#12443](https://github.com/traefik/traefik/pull/12443) by [nmengin](https://github.com/nmengin)) +- **[k8s/ingress-nginx]** Add RBAC documentation for Ingress NGINX provider ([#12445](https://github.com/traefik/traefik/pull/12445) by [nmn3m](https://github.com/nmn3m)) +- **[k8s]** Improve the K8S multi-tenancy security note ([#12444](https://github.com/traefik/traefik/pull/12444) by [nmengin](https://github.com/nmengin)) +- Restore documentation on http.maxHeaderBytes ([#12440](https://github.com/traefik/traefik/pull/12440) by [mloiseleur](https://github.com/mloiseleur)) +- Fix Menu Item Naming ([#12431](https://github.com/traefik/traefik/pull/12431) by [sheddy-traefik](https://github.com/sheddy-traefik)) + +**Misc:** +- Merge branch v2.11 into v3.6 ([#12475](https://github.com/traefik/traefik/pull/12475) by [mmatur](https://github.com/mmatur)) +- Merge branch v2.11 into v3.6 ([#12438](https://github.com/traefik/traefik/pull/12438) by [kevinpollet](https://github.com/kevinpollet)) + +## [v2.11.34](https://github.com/traefik/traefik/tree/v2.11.34) (2025-12-23) +[All Commits](https://github.com/traefik/traefik/compare/v2.11.33...v2.11.34) + +**Bug fixes:** +- **[server]** Fix deny encoded characters ([#12457](https://github.com/traefik/traefik/pull/12457) by [rtribotte](https://github.com/rtribotte)) + +## [v2.11.33](https://github.com/traefik/traefik/tree/v2.11.33) (2025-12-17) +[All Commits](https://github.com/traefik/traefik/compare/v2.11.32...v2.11.33) + +**Bug fixes:** +- **[server]** Print access logs for rejected requests and warn about new behavior ([#12426](https://github.com/traefik/traefik/pull/12426) by [rtribotte](https://github.com/rtribotte)) + +**Documentation:** +- Clarify doc about encoded characters rejection ([#12391](https://github.com/traefik/traefik/pull/12391) by [rtribotte](https://github.com/rtribotte)) +- Fix encoded characters entryPoint option documentation ([#12384](https://github.com/traefik/traefik/pull/12384) by [rtribotte](https://github.com/rtribotte)) +- Fix encoded characters option documentation ([#12373](https://github.com/traefik/traefik/pull/12373) by [kevinpollet](https://github.com/kevinpollet)) + +## [v3.6.5](https://github.com/traefik/traefik/tree/v3.6.5) (2025-12-16) +[All Commits](https://github.com/traefik/traefik/compare/v3.6.4...v3.6.5) + +**Bug fixes:** +- **[k8s/ingress-nginx]** Fix NGINX sslredirect annotation support ([#12387](https://github.com/traefik/traefik/pull/12387) by [rtribotte](https://github.com/rtribotte)) +- **[server]** Print access logs for rejected requests and warn about new behavior ([#12424](https://github.com/traefik/traefik/pull/12424) by [kevinpollet](https://github.com/kevinpollet)) + +**Documentation:** +- **[k8s/ingress-nginx]** Add auth-signin to unsupported nginx annotations list ([#12370](https://github.com/traefik/traefik/pull/12370) by [fibsifan](https://github.com/fibsifan)) +- Add a Breaking change note to the changelog ([#12398](https://github.com/traefik/traefik/pull/12398) by [nmengin](https://github.com/nmengin)) +- Fix encodedCharacters entryPoint option documentation ([#12385](https://github.com/traefik/traefik/pull/12385) by [rtribotte](https://github.com/rtribotte)) + +## [v3.6.4](https://github.com/traefik/traefik/tree/v3.6.4) (2025-12-05) +[All Commits](https://github.com/traefik/traefik/compare/v3.6.2...v3.6.4) + +**CVE's fixed:** +- [CVE-2025-66490](https://nvd.nist.gov/vuln/detail/CVE-2025-66490) (Advisory [GHSA-gm3x-23wp-hc2c](https://github.com/traefik/traefik/security/advisories/GHSA-gm3x-23wp-hc2c)): **Breaking Change** please read the [migration guide](https://doc.traefik.io/traefik/v3.6/migrate/v3/#v364). +- [CVE-2025-66491](https://nvd.nist.gov/vuln/detail/CVE-2025-66491) (Advisory [GHSA-7vww-mvcr-x6vj](https://github.com/traefik/traefik/security/advisories/GHSA-7vww-mvcr-x6vj)) + +**Important:** Please read the [migration guide](https://doc.traefik.io/traefik/v3.6/migrate/v3/#v364). + +**Bug fixes:** +- **[server]** Reject suspicious encoded characters ([#12360](https://github.com/traefik/traefik/pull/12360) by [rtribotte](https://github.com/rtribotte)) +- **[plugins]** Validate plugin module name ([#12291](https://github.com/traefik/traefik/pull/12291) by [kevinpollet](https://github.com/kevinpollet)) +- **[http3]** Bump github.com/quic-go/quic-go to v0.57.1 ([#12319](https://github.com/traefik/traefik/pull/12319) by [GreyXor](https://github.com/GreyXor)) +- **[http3]** Bump github.com/quic-go/quic-go to v0.57.0 ([#12308](https://github.com/traefik/traefik/pull/12308) by [GreyXor](https://github.com/GreyXor)) +- **[server]** Bump golang.org/x/crypto to v0.45.0 ([#12296](https://github.com/traefik/traefik/pull/12296) by [kevinpollet](https://github.com/kevinpollet)) +- **[acme]** Bump github.com/go-acme/lego/v4 to v4.29.0 ([#12333](https://github.com/traefik/traefik/pull/12333) by [ldez](https://github.com/ldez)) +- **[k8s/ingress-nginx]** Fix SSL redirect to match NGINX behavior ([#12361](https://github.com/traefik/traefik/pull/12361) by [mmatur](https://github.com/mmatur)) +- **[k8s/ingress-nginx]** Fix the service name for ingress-nginx provider ([#12352](https://github.com/traefik/traefik/pull/12352) by [mmatur](https://github.com/mmatur)) +- **[k8s/ingress-nginx]** Fix nginx.ingress.kubernetes.io/proxy-ssl-verify annotation support ([#12351](https://github.com/traefik/traefik/pull/12351) by [rtribotte](https://github.com/rtribotte)) +- **[middleware,authentication]** Change ForwardAuth error log level from DEBUG to ERROR ([#12324](https://github.com/traefik/traefik/pull/12324) by [murataslan1](https://github.com/murataslan1)) + +**Documentation:** +- **[api]** Fix typo in API dashboard configuration instructions ([#12335](https://github.com/traefik/traefik/pull/12335) by [NAICOLAS](https://github.com/NAICOLAS)) +- **[docker]** Add documentation for loadbalancer.server.url in Docker and Swarm providers ([#12289](https://github.com/traefik/traefik/pull/12289) by [webash](https://github.com/webash)) +- **[k8s/gatewayapi]** Fix links of Helm chart values reference to providers.kubernetesGateway.enabled ([#12315](https://github.com/traefik/traefik/pull/12315) by [shouhei](https://github.com/shouhei)) +- **[k8s/ingress-nginx]** Fix default value of ingress-nginx provider in documentation ([#12328](https://github.com/traefik/traefik/pull/12328) by [mloiseleur](https://github.com/mloiseleur)) +- **[k8s/ingress-nginx]** NGINX Ingress Controller to Traefik Migration Guide ([#12318](https://github.com/traefik/traefik/pull/12318) by [sheddy-traefik](https://github.com/sheddy-traefik)) +- **[k8s/ingress-nginx]** Improve the configuration options display of the Kubernetes ingress-nginx provider ([#12297](https://github.com/traefik/traefik/pull/12297) by [mloiseleur](https://github.com/mloiseleur)) +- **[k8s/ingress-nginx]** Improve ingress-nginx provider documentation ([#12288](https://github.com/traefik/traefik/pull/12288) by [sheddy-traefik](https://github.com/sheddy-traefik)) +- **[service]** Fix loadbalancer doc for highest random weight ([#12283](https://github.com/traefik/traefik/pull/12283) by [ozon2](https://github.com/ozon2)) +- Correctly Format the HTTP Service Documentation ([#12311](https://github.com/traefik/traefik/pull/12311) by [sheddy-traefik](https://github.com/sheddy-traefik)) +- Add documentation about checkNewVersion ([#12298](https://github.com/traefik/traefik/pull/12298) by [darkweaver87](https://github.com/darkweaver87)) + +**Misc:** +- Merge branch v2.11 into v3.6 ([#12364](https://github.com/traefik/traefik/pull/12364) by [kevinpollet](https://github.com/kevinpollet)) +- Merge branch v2.11 into v3.6 ([#12341](https://github.com/traefik/traefik/pull/12341) by [mmatur](https://github.com/mmatur)) +- Merge branch v2.11 into v3.6 ([#12368](https://github.com/traefik/traefik/pull/12368) by [mmatur](https://github.com/mmatur)) + +## [v3.6.3](https://github.com/traefik/traefik/tree/v3.6.3) (2025-12-04) +[All Commits](https://github.com/traefik/traefik/compare/v3.6.2...v3.6.3) + +Release canceled. + +## [v2.11.32](https://github.com/traefik/traefik/tree/v2.11.32) (2025-12-04) +[All Commits](https://github.com/traefik/traefik/compare/v2.11.31...v2.11.32) + +**Bug fixes:** +- **[server]** Reject suspicious encoded characters ([#12360](https://github.com/traefik/traefik/pull/12360) by [rtribotte](https://github.com/rtribotte)) +- **[plugins]** Validate plugin module name ([#12291](https://github.com/traefik/traefik/pull/12291) by [kevinpollet](https://github.com/kevinpollet)) +- **[http3]** Bump github.com/quic-go/quic-go to v0.57.1 ([#12319](https://github.com/traefik/traefik/pull/12319) by [GreyXor](https://github.com/GreyXor)) +- **[http3]** Bump github.com/quic-go/quic-go to v0.57.0 ([#12308](https://github.com/traefik/traefik/pull/12308) by [GreyXor](https://github.com/GreyXor)) +- **[server]** Bump golang.org/x/crypto to v0.45.0 ([#12296](https://github.com/traefik/traefik/pull/12296) by [kevinpollet](https://github.com/kevinpollet)) + +**Documentation:** +- Update SECURITY.md to streamline information ([#12310](https://github.com/traefik/traefik/pull/12310) by [emilevauge](https://github.com/emilevauge)) +- Update SECURITY.md ([#12304](https://github.com/traefik/traefik/pull/12304) by [cwayne18](https://github.com/cwayne18)) + +## [v3.6.2](https://github.com/traefik/traefik/tree/v3.6.2) (2025-11-18) +[All Commits](https://github.com/traefik/traefik/compare/v3.6.1...v3.6.2) + +**Bug fixes:** +- **[k8s/ingress-nginx]** Deprecate Kubernetes Ingress NGINX provider experimental flag ([#12286](https://github.com/traefik/traefik/pull/12286) by [rtribotte](https://github.com/rtribotte)) + +## [v3.6.1](https://github.com/traefik/traefik/tree/v3.6.1) (2025-11-13) +[All Commits](https://github.com/traefik/traefik/compare/v3.6.0...v3.6.1) + +**Bug fixes:** +- **[docker]** Auto-negotiate Docker API Version ([#12256](https://github.com/traefik/traefik/pull/12256) by [felixbuenemann](https://github.com/felixbuenemann)) +- **[server]** Fix multi-layer routing with models ([#12258](https://github.com/traefik/traefik/pull/12258) by [juliens](https://github.com/juliens)) +- **[udp]** Revert "Avoid allocations in readLoop by using sync.Pool" ([#12267](https://github.com/traefik/traefik/pull/12267) by [kevinpollet](https://github.com/kevinpollet)) +- **[webui]** Fix blocked navigation on Safari ([#12231](https://github.com/traefik/traefik/pull/12231) by [gndz07](https://github.com/gndz07)) +- **[webui]** Restore remote Upgrade to Hub button web component ([#12219](https://github.com/traefik/traefik/pull/12219) by [gndz07](https://github.com/gndz07)) + +**Documentation:** +- **[k8s]** Fix Nginx provider documentation ([#12266](https://github.com/traefik/traefik/pull/12266) by [nmengin](https://github.com/nmengin)) +- **[k8s]** Fix Gateway API version and the list of features supported ([#12254](https://github.com/traefik/traefik/pull/12254) by [nmengin](https://github.com/nmengin)) + +## [v2.11.31](https://github.com/traefik/traefik/tree/v2.11.31) (2025-11-13) +[All Commits](https://github.com/traefik/traefik/compare/v2.11.30...v2.11.31) + +**Bug fixes:** +- **[docker,docker/swarm]** Auto-negotiate Docker API version ([#12262](https://github.com/traefik/traefik/pull/12262) by [kevinpollet](https://github.com/kevinpollet)) + +## [v3.6.0](https://github.com/traefik/traefik/tree/v3.6.0) (2025-11-07) +[All Commits](https://github.com/traefik/traefik/compare/v3.5.0-rc1...v3.6.0) + +**Enhancements:** +- **[acme]** Add new certificatesresolvers options ([#11977](https://github.com/traefik/traefik/pull/11977) by [ldez](https://github.com/ldez)) +- **[consul,consulcatalog,nomad]** Log provider namespace during startup ([#12002](https://github.com/traefik/traefik/pull/12002) by [shreealt](https://github.com/shreealt)) +- **[docker]** Allow discovering non-running Docker containers ([#10645](https://github.com/traefik/traefik/pull/10645) by [acouvreur](https://github.com/acouvreur)) +- **[ecs]** AWS ECS IPv6 Support ([#12179](https://github.com/traefik/traefik/pull/12179) by [wizbit](https://github.com/wizbit)) +- **[file,k8s/crd,service]** Add least time load balancing strategy ([#12167](https://github.com/traefik/traefik/pull/12167) by [sdelicata](https://github.com/sdelicata)) +- **[healthcheck,tcp]** Add TCP Healthcheck ([#11238](https://github.com/traefik/traefik/pull/11238) by [ddtmachado](https://github.com/ddtmachado)) +- **[healthcheck]** Add passive health checks ([#11351](https://github.com/traefik/traefik/pull/11351) by [Nelwhix](https://github.com/Nelwhix)) +- **[k8s/crd]** Add highest random weight in Kubernetes CRD ([#12061](https://github.com/traefik/traefik/pull/12061) by [lbenguigui](https://github.com/lbenguigui)) +- **[k8s/gatewayapi]** Bump sigs.k8s.io/gateway-api to v1.4.0 ([#12140](https://github.com/traefik/traefik/pull/12140) by [kevinpollet](https://github.com/kevinpollet)) +- **[k8s/ingress]** Allow publishing services with type ExternalName ([#12065](https://github.com/traefik/traefik/pull/12065) by [james-callahan](https://github.com/james-callahan)) +- **[k8s]** Add Knative provider ([#11448](https://github.com/traefik/traefik/pull/11448) by [idurgakalyan](https://github.com/idurgakalyan)) +- **[middleware,authentication]** Add warning when maxBodySize is not set ([#12085](https://github.com/traefik/traefik/pull/12085) by [kianelbo](https://github.com/kianelbo)) +- **[middleware,server]** Multi-layer routing ([#12130](https://github.com/traefik/traefik/pull/12130) by [sdelicata](https://github.com/sdelicata)) +- **[plugins]** Support syscall ([#11939](https://github.com/traefik/traefik/pull/11939) by [david-garcia-garcia](https://github.com/david-garcia-garcia)) +- **[server]** Implement HTTP2 HPACK table size options ([#12050](https://github.com/traefik/traefik/pull/12050) by [GCHQDeveloper548](https://github.com/GCHQDeveloper548)) +- **[service,udp]** Avoid allocations in readLoop by using sync.Pool ([#12029](https://github.com/traefik/traefik/pull/12029) by [arturmelanchyk](https://github.com/arturmelanchyk)) +- **[service]** Add HighestRandomWeight load balancing algorithm ([#9946](https://github.com/traefik/traefik/pull/9946) by [mathieuHa](https://github.com/mathieuHa)) +- **[webui]** Add Traefik Hub demo in dashboard ([#12193](https://github.com/traefik/traefik/pull/12193) by [gndz07](https://github.com/gndz07)) +- **[webui]** Reduce vertical padding in dashboard table rows for more compact layout ([#12145](https://github.com/traefik/traefik/pull/12145) by [leccelecce](https://github.com/leccelecce)) + +**Bug fixes:** +- **[server]** Make the aggregator compute provider namespace for router's parentRefs ([#12235](https://github.com/traefik/traefik/pull/12235) by [rtribotte](https://github.com/rtribotte)) + +**Documentation:** +- Prepare release v3.6.0-rc1 ([#12211](https://github.com/traefik/traefik/pull/12211) by [kevinpollet](https://github.com/kevinpollet)) +- Fix broken link to migration guide on readme ([#12021](https://github.com/traefik/traefik/pull/12021) by [0slb](https://github.com/0slb)) +- Fix broken links in TCP Service and HTTP Router documentation ([#12215](https://github.com/traefik/traefik/pull/12215) by [sheddy-traefik](https://github.com/sheddy-traefik)) +- Fix typo in v3.6 migration guide ([#12212](https://github.com/traefik/traefik/pull/12212) by [jnoordsij](https://github.com/jnoordsij)) + +**Misc:** +- Merge branch v3.5 into master ([#12210](https://github.com/traefik/traefik/pull/12210) by [rtribotte](https://github.com/rtribotte)) +- Merge branch v3.5 into master ([#12191](https://github.com/traefik/traefik/pull/12191) by [rtribotte](https://github.com/rtribotte)) +- Merge branch v3.5 into master ([#12188](https://github.com/traefik/traefik/pull/12188) by [rtribotte](https://github.com/rtribotte)) +- Merge branch v3.5 into master ([#12160](https://github.com/traefik/traefik/pull/12160) by [rtribotte](https://github.com/rtribotte)) +- Merge branch v3.5 into master ([#12136](https://github.com/traefik/traefik/pull/12136) by [kevinpollet](https://github.com/kevinpollet)) +- Merge branch v3.5 into master ([#12120](https://github.com/traefik/traefik/pull/12120) by [rtribotte](https://github.com/rtribotte)) +- Merge branch v3.5 into master ([#12095](https://github.com/traefik/traefik/pull/12095) by [kevinpollet](https://github.com/kevinpollet)) +- Merge branch v3.5 into master ([#12051](https://github.com/traefik/traefik/pull/12051) by [rtribotte](https://github.com/rtribotte)) +- Merge branch v3.5 into master ([#11976](https://github.com/traefik/traefik/pull/11976) by [kevinpollet](https://github.com/kevinpollet)) +- Merge branch v3.5 into master ([#11940](https://github.com/traefik/traefik/pull/11940) by [kevinpollet](https://github.com/kevinpollet)) +- Merge branch v3.5 into master ([#11900](https://github.com/traefik/traefik/pull/11900) by [kevinpollet](https://github.com/kevinpollet)) +- Merge branch v3.5 into v3.6 ([#12242](https://github.com/traefik/traefik/pull/12242) by [kevinpollet](https://github.com/kevinpollet)) + +## [v3.5.6](https://github.com/traefik/traefik/tree/v3.5.6) (2025-11-07) +[All Commits](https://github.com/traefik/traefik/compare/v3.5.4...v3.5.6) + +**Bug fixes:** +- **[acme]** Bump github.com/go-acme/lego/v4 to v4.28.0 ([#12218](https://github.com/traefik/traefik/pull/12218) by [ldez](https://github.com/ldez)) +- **[server]** Filter unknown nodes with file and env for the deprecation loader ([#12227](https://github.com/traefik/traefik/pull/12227) by [rtribotte](https://github.com/rtribotte)) + +**Documentation:** +- **[acme]** Add missing ACME options and clean up table for more visibility ([#12208](https://github.com/traefik/traefik/pull/12208) by [sheddy-traefik](https://github.com/sheddy-traefik)) +- **[middleware]** Fix default encodings in compress middleware ([#12216](https://github.com/traefik/traefik/pull/12216) by [Belphemur](https://github.com/Belphemur)) +- Update Configuration Overview Page ([#12202](https://github.com/traefik/traefik/pull/12202) by [sheddy-traefik](https://github.com/sheddy-traefik)) + +## [v3.5.5](https://github.com/traefik/traefik/tree/v3.5.5) (2025-11-07) +[All Commits](https://github.com/traefik/traefik/compare/v3.5.4...v3.5.5) + +Release canceled. + +## [v3.6.0-rc1](https://github.com/traefik/traefik/tree/v3.6.0-rc1) (2025-10-28) +[All Commits](https://github.com/traefik/traefik/compare/v3.5.0-rc1...v3.6.0-rc1) + +**Enhancements:** +- **[acme]** Add new certificatesresolvers options ([#11977](https://github.com/traefik/traefik/pull/11977) by [ldez](https://github.com/ldez)) +- **[consul,consulcatalog,nomad]** Log provider namespace during startup ([#12002](https://github.com/traefik/traefik/pull/12002) by [shreealt](https://github.com/shreealt)) +- **[docker]** Allow discovering non-running Docker containers ([#10645](https://github.com/traefik/traefik/pull/10645) by [acouvreur](https://github.com/acouvreur)) +- **[ecs]** AWS ECS IPv6 Support ([#12179](https://github.com/traefik/traefik/pull/12179) by [wizbit](https://github.com/wizbit)) +- **[file,k8s/crd,service]** Add least time load balancing strategy ([#12167](https://github.com/traefik/traefik/pull/12167) by [sdelicata](https://github.com/sdelicata)) +- **[healthcheck,tcp]** Add TCP Healthcheck ([#11238](https://github.com/traefik/traefik/pull/11238) by [ddtmachado](https://github.com/ddtmachado)) +- **[healthcheck]** Add passive health checks ([#11351](https://github.com/traefik/traefik/pull/11351) by [Nelwhix](https://github.com/Nelwhix)) +- **[k8s/crd]** Add highest random weight in Kubernetes CRD ([#12061](https://github.com/traefik/traefik/pull/12061) by [lbenguigui](https://github.com/lbenguigui)) +- **[k8s/gatewayapi]** Bump sigs.k8s.io/gateway-api to v1.4.0 ([#12140](https://github.com/traefik/traefik/pull/12140) by [kevinpollet](https://github.com/kevinpollet)) +- **[k8s/ingress]** Allow publishing services with type ExternalName ([#12065](https://github.com/traefik/traefik/pull/12065) by [james-callahan](https://github.com/james-callahan)) +- **[k8s]** Add Knative provider ([#11448](https://github.com/traefik/traefik/pull/11448) by [idurgakalyan](https://github.com/idurgakalyan)) +- **[middleware,authentication]** Add warning when maxBodySize is not set ([#12085](https://github.com/traefik/traefik/pull/12085) by [kianelbo](https://github.com/kianelbo)) +- **[middleware,server]** Multi-layer routing ([#12130](https://github.com/traefik/traefik/pull/12130) by [sdelicata](https://github.com/sdelicata)) +- **[plugins]** Support syscall ([#11939](https://github.com/traefik/traefik/pull/11939) by [david-garcia-garcia](https://github.com/david-garcia-garcia)) +- **[server]** Implement HTTP2 HPACK table size options ([#12050](https://github.com/traefik/traefik/pull/12050) by [GCHQDeveloper548](https://github.com/GCHQDeveloper548)) +- **[service,udp]** Avoid allocations in readLoop by using sync.Pool ([#12029](https://github.com/traefik/traefik/pull/12029) by [arturmelanchyk](https://github.com/arturmelanchyk)) +- **[service]** Add HighestRandomWeight load balancing algorithm ([#9946](https://github.com/traefik/traefik/pull/9946) by [mathieuHa](https://github.com/mathieuHa)) +- **[webui]** Add Traefik Hub demo in dashboard ([#12193](https://github.com/traefik/traefik/pull/12193) by [gndz07](https://github.com/gndz07)) +- **[webui]** Reduce vertical padding in dashboard table rows for more compact layout ([#12145](https://github.com/traefik/traefik/pull/12145) by [leccelecce](https://github.com/leccelecce)) + +**Documentation:** +- Fix broken link to migration guide on readme ([#12021](https://github.com/traefik/traefik/pull/12021) by [0slb](https://github.com/0slb)) + +**Misc:** +- Merge branch v3.5 into master ([#12210](https://github.com/traefik/traefik/pull/12210) by [rtribotte](https://github.com/rtribotte)) +- Merge branch v3.5 into master ([#12191](https://github.com/traefik/traefik/pull/12191) by [rtribotte](https://github.com/rtribotte)) +- Merge branch v3.5 into master ([#12188](https://github.com/traefik/traefik/pull/12188) by [rtribotte](https://github.com/rtribotte)) +- Merge branch v3.5 into master ([#12160](https://github.com/traefik/traefik/pull/12160) by [rtribotte](https://github.com/rtribotte)) +- Merge branch v3.5 into master ([#12136](https://github.com/traefik/traefik/pull/12136) by [kevinpollet](https://github.com/kevinpollet)) +- Merge branch v3.5 into master ([#12120](https://github.com/traefik/traefik/pull/12120) by [rtribotte](https://github.com/rtribotte)) +- Merge branch v3.5 into master ([#12095](https://github.com/traefik/traefik/pull/12095) by [kevinpollet](https://github.com/kevinpollet)) +- Merge branch v3.5 into master ([#12051](https://github.com/traefik/traefik/pull/12051) by [rtribotte](https://github.com/rtribotte)) +- Merge branch v3.5 into master ([#11976](https://github.com/traefik/traefik/pull/11976) by [kevinpollet](https://github.com/kevinpollet)) +- Merge branch v3.5 into master ([#11940](https://github.com/traefik/traefik/pull/11940) by [kevinpollet](https://github.com/kevinpollet)) +- Merge branch v3.5 into master ([#11900](https://github.com/traefik/traefik/pull/11900) by [kevinpollet](https://github.com/kevinpollet)) + ## [v3.5.4](https://github.com/traefik/traefik/tree/v3.5.4) (2025-10-28) [All Commits](https://github.com/traefik/traefik/compare/v3.5.3...v3.5.4) diff --git a/Dockerfile b/Dockerfile index 10ee5e2b5..0d64f6209 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:1.2 -FROM alpine:3.22 +FROM alpine:3.23 RUN apk add --no-cache --no-progress ca-certificates tzdata diff --git a/Makefile b/Makefile index 470b5c3e8..7f361cfb2 100644 --- a/Makefile +++ b/Makefile @@ -100,10 +100,15 @@ test-integration: GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration -test.timeout=20m -failfast -v $(TESTFLAGS) .PHONY: test-gateway-api-conformance -#? test-gateway-api-conformance: Run the conformance tests +#? test-gateway-api-conformance: Run the Gateway API conformance tests test-gateway-api-conformance: build-image-dirty - # In case of a new Minor/Major version, the k8sConformanceTraefikVersion needs to be updated. - GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration -v -test.run K8sConformanceSuite -k8sConformance -k8sConformanceTraefikVersion="v3.5" $(TESTFLAGS) + # In case of a new Minor/Major version, the traefikVersion needs to be updated. + GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration -v -tags gatewayAPIConformance -test.run GatewayAPIConformanceSuite -traefikVersion="v3.6" $(TESTFLAGS) + +.PHONY: test-knative-conformance +#? test-knative-conformance: Run the Knative conformance tests +test-knative-conformance: build-image-dirty + GOOS=$(GOOS) GOARCH=$(GOARCH) go test ./integration/integration_test.go ./integration/knative_conformance_test.go -v -tags knativeConformance -test.run KnativeConformanceSuite .PHONY: test-ui-unit #? test-ui-unit: Run the unit tests for the webui diff --git a/README.md b/README.md index 472057a3a..8cd3df7d0 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Pointing Traefik at your orchestrator should be the _only_ configuration step yo --- -:warning: When migrating to a new major version of Traefik, please refer to the [migration guide](https://doc.traefik.io/traefik/migration/v2-to-v3/) to ensure a smooth transition and to be aware of any breaking changes. +:warning: When migrating to a new major version of Traefik, please refer to the [migration guide](https://doc.traefik.io/traefik/migrate/v2-to-v3/) to ensure a smooth transition and to be aware of any breaking changes. ## Overview diff --git a/SECURITY.md b/SECURITY.md index c9a2670f6..240fba79b 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,10 +1,5 @@ # Security Policy -You can join our security mailing list to be aware of the latest announcements from our security team. -You can subscribe by sending an email to security+subscribe@traefik.io or on [the online viewer](https://groups.google.com/a/traefik.io/forum/#!forum/security). - -Reported vulnerabilities can be found on [cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik). - ## Supported Versions - We usually release 3/4 new versions (e.g. 1.1.0, 1.2.0, 1.3.0) per year. @@ -17,10 +12,10 @@ We use [Semantic Versioning](https://semver.org/). | Version | Supported | |-----------|--------------------| -| `2.2.x` | :white_check_mark: | -| `< 2.2.x` | :x: | -| `1.7.x` | :white_check_mark: | -| `< 1.7.x` | :x: | +| `3.6.x` | :white_check_mark: | +| `< 3.6.x` | :x: | +| `2.11.x` | :white_check_mark: | +| `< 2.11.x` | :x: | ## Reporting a Vulnerability @@ -28,3 +23,5 @@ We want to keep Traefik safe for everyone. If you've discovered a security vulnerability in Traefik, we appreciate your help in disclosing it to us in a responsible manner, by creating a [security advisory](https://github.com/traefik/traefik/security/advisories). + +Reported vulnerabilities can be found on [cve.mitre.org](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=traefik). diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go index 3f35b280f..88b316c21 100644 --- a/cmd/traefik/traefik.go +++ b/cmd/traefik/traefik.go @@ -98,6 +98,11 @@ func runCmd(staticConfiguration *static.Configuration) error { return fmt.Errorf("setting up logger: %w", err) } + log.Warn().Msg("Traefik can reject some encoded characters in the request path." + + "When your backend is not fully compliant with [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986)," + + "it is recommended to set these options to `false` to avoid split-view situation." + + "Refer to the documentation for more details: https://doc.traefik.io/traefik/v3.6/migrate/v3/#encoded-characters-configuration-default-values") + http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment staticConfiguration.SetEffectiveConfiguration() @@ -115,9 +120,7 @@ func runCmd(staticConfiguration *static.Configuration) error { log.Debug().RawJSON("staticConfiguration", []byte(redactedStaticConfiguration)).Msg("Static configuration loaded [json]") } - if staticConfiguration.Global.CheckNewVersion { - checkNewVersion() - } + checkNewVersion(staticConfiguration) stats(staticConfiguration) @@ -620,13 +623,28 @@ func setupTracing(ctx context.Context, conf *static.Tracing) (*tracing.Tracer, i return tracer, closer } -func checkNewVersion() { - ticker := time.Tick(24 * time.Hour) - safe.Go(func() { - for time.Sleep(10 * time.Minute); ; <-ticker { - version.CheckNewVersion() - } - }) +func checkNewVersion(staticConfiguration *static.Configuration) { + logger := log.With().Logger() + + if staticConfiguration.Global.CheckNewVersion { + logger.Info().Msg(`Version check is enabled.`) + logger.Info().Msg(`Traefik checks for new releases to notify you if your version is out of date.`) + logger.Info().Msg(`It also collects usage data during this process.`) + logger.Info().Msg(`Check the documentation to get more info: https://doc.traefik.io/traefik/contributing/data-collection/`) + + ticker := time.Tick(24 * time.Hour) + safe.Go(func() { + for time.Sleep(10 * time.Minute); ; <-ticker { + version.CheckNewVersion() + } + }) + } else { + logger.Info().Msg(` +Version check is disabled. +You will not be notified if a new version is available. +More details: https://doc.traefik.io/traefik/contributing/data-collection/ +`) + } } func stats(staticConfiguration *static.Configuration) { diff --git a/docs/check.Dockerfile b/docs/check.Dockerfile index 2a03cf8af..d89f9efa9 100644 --- a/docs/check.Dockerfile +++ b/docs/check.Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.22 +FROM alpine:3.23 RUN apk --no-cache --no-progress add \ build-base \ @@ -34,6 +34,7 @@ RUN apk --no-cache --no-progress add \ COPY ./scripts/verify.sh /verify.sh COPY ./scripts/lint.sh /lint.sh +COPY ./scripts/lint-yaml.sh /lint-yaml.sh WORKDIR /app VOLUME ["/tmp","/app"] diff --git a/docs/content/assets/css/code-copy.css b/docs/content/assets/css/code-copy.css new file mode 100644 index 000000000..3273b0ec2 --- /dev/null +++ b/docs/content/assets/css/code-copy.css @@ -0,0 +1,18 @@ +/* Fix positioning of the built-in clipboard button for code blocks. + * In this theme, the button can end up positioned relative to , + * so anchor it to the code block container instead. + */ + +.md-typeset pre.highlight { + position: relative; +} + +.md-typeset pre.highlight > button.md-clipboard { + position: absolute; + top: .25rem; + right: .25rem; + z-index: 10; + opacity: 1; + visibility: visible; +} + diff --git a/docs/content/assets/js/extra.js b/docs/content/assets/js/extra.js index eb0cc12ff..be5a62105 100644 --- a/docs/content/assets/js/extra.js +++ b/docs/content/assets/js/extra.js @@ -1,4 +1,14 @@ /* Highlight */ (function(hljs) { hljs.initHighlightingOnLoad(); -})(hljs); \ No newline at end of file +})(hljs); + +/* Scarf Analytics - cookieless, anonymous company-level intelligence */ +(function() { + var img = document.createElement('img'); + img.src = 'https://static.scarf.sh/a.png?x-pxid=1a49232a-b165-4015-8ed2-a1092f1f0d83'; + img.referrerPolicy = 'no-referrer-when-downgrade'; + img.loading = 'eager'; + img.style.cssText = 'visibility:hidden;position:absolute;width:1px;height:1px;'; + document.body.appendChild(img); +})(); \ No newline at end of file diff --git a/docs/content/contributing/data-collection.md b/docs/content/contributing/data-collection.md index 645170b58..5153b038c 100644 --- a/docs/content/contributing/data-collection.md +++ b/docs/content/contributing/data-collection.md @@ -1,17 +1,72 @@ --- title: "Traefik Data Collection Documentation" -description: "To learn more about how Traefik is being used and improve it, we collect anonymous usage statistics from running instances. Read the technical documentation." +description: "Learn what data Traefik shares, how it is used, and how you can control it. This documentation explains both version check and anonymous usage statistics data. Read the technical documentation." --- # Data Collection -Understanding How Traefik is Being Used +Understanding the data Traefik shares and how it is used {: .subtitle } -## Configuration Example +## Introduction -Understanding how you use Traefik is very important to us: it helps us improve the solution in many different ways. -For this very reason, the sendAnonymousUsage option is mandatory: we want you to take time to consider whether or not you wish to share anonymous data with us, so we can benefit from your experience and use cases. +Protecting user privacy is essential to Traefik Labs, and we design every data-sharing mechanism with transparency and minimalism in mind. +This page describes the two types of data exchanged by Traefik and how to configure them. + +For more details on how your data is handled, please refer to our [Privacy and Cookie Policy](https://traefik.io/legal/privacy-and-cookie-policy). + +## Configuration Overview + +Traefik provides two independent mechanisms: + +- `checkNewVersion`, enabled by default. You may disable it at any time. +- `sendAnonymousUsage`, which requires explicit opt‑in. + +Examples below show how to activate or deactivate both of them. + +```yaml tab="YAML" +global: + checkNewVersion: true # set to false to disable + sendAnonymousUsage: false # set to true to enable +``` + +```toml tab="TOML" +[global] + checkNewVersion = true # set to false to disable + sendAnonymousUsage = false # set to true to enable +``` + +```bash tab="CLI" +--global.checkNewVersion=true # set to false to disable +--global.sendAnonymousUsage=false # set to true to enable +``` + +A log message at startup clearly indicates whether each of those options are enabled or disabled. + +## Version Check (`checkNewVersion`) – Opt-out + +Traefik periodically contacts `update.traefik.io` to determine whether a newer version is available. +When this request is made, Traefik shares the **running version** and the **public IP** of the instance. +The IP is used to build global usage statistics and does not influence the version comparison. + +This mechanism helps you stay informed about updates and provides TraefikLabs with a broad view of which versions are deployed in the wild. + +The collected IP addresses are also used for marketing purposes, specifically to detect companies running Traefik and offer them adapted support contracts, enterprise features, and tailored services. + +If you want to explore the implementation, you can read the version check source code: [version.go](https://github.com/traefik/traefik/blob/master/pkg/version/version.go) + +## Anonymous Usage Data (`sendAnonymousUsage`) – Opt‑in + +Traefik can also collect anonymous usage statistics once per day, starting 10 minutes after it starts running. +These statistics include: + +- the Traefik version, +- a hash of the configuration, +- an anonymized version of the static configuration (all sensitive fields removed: tokens, passwords, URLs, IP addresses, domains, emails, etc.). + +This feature comes from this [public proposal](https://github.com/traefik/traefik/issues/2369). + +This information helps TraefikLabs understand how Traefik is used in general and prioritize features and provider support accordingly. Dynamic configuration (routers and services) is never collected. !!! example "Enabling Data Collection" @@ -32,21 +87,6 @@ For this very reason, the sendAnonymousUsage option is mandatory: we want you to --global.sendAnonymousUsage ``` -## Collected Data - -This feature comes from this [public proposal](https://github.com/traefik/traefik/issues/2369). - -In order to help us learn more about how Traefik is being used and improve it, we collect anonymous usage statistics from running instances. -Those data help us prioritize our developments and focus on what's important for our users (for example, which provider is popular, and which is not). - -### What's collected / when ? - -Once a day (the first call begins 10 minutes after the start of Traefik), we collect: - -- the Traefik version number -- a hash of the configuration -- an **anonymized version** of the static configuration (token, username, password, URL, IP, domain, email, etc., are removed). - !!! info - We do not collect the dynamic configuration information (routers & services). @@ -93,8 +133,9 @@ providers: insecureSkipVerify: true ``` -## The Code for Data Collection +### The Code for Anonymous Usage Collection -If you want to dig into more details, here is the source code of the collecting system: [collector.go](https://github.com/traefik/traefik/blob/master/pkg/collector/collector.go) +If you want to explore the implementation, you can read the collector source code: +[collector.go](https://github.com/traefik/traefik/blob/master/pkg/collector/collector.go) -By default, we anonymize all configuration fields, except fields tagged with `export=true`. +Traefik anonymizes all configuration fields by default, except those explicitly marked with `export=true`. diff --git a/docs/content/deprecation/releases.md b/docs/content/deprecation/releases.md index c3ec3336a..1f3617fe8 100644 --- a/docs/content/deprecation/releases.md +++ b/docs/content/deprecation/releases.md @@ -6,25 +6,14 @@ Below is a non-exhaustive list of versions and their maintenance status: | Version | Release Date | Active Support | Security Support | |---------|--------------|--------------------|-------------------| -| 3.5 | Jul 23, 2025 | Yes | Yes | +| 3.6 | Nov 07, 2025 | Yes | Yes | +| 3.5 | Jul 23, 2025 | Ended Nov 07, 2025 | No | | 3.4 | May 05, 2025 | Ended Jul 23, 2025 | No | | 3.3 | Jan 06, 2025 | Ended May 05, 2025 | No | | 3.2 | Oct 28, 2024 | Ended Jan 06, 2025 | No | | 3.1 | Jul 15, 2024 | Ended Oct 28, 2024 | No | | 3.0 | Apr 29, 2024 | Ended Jul 15, 2024 | No | | 2.11 | Feb 12, 2024 | Ended Apr 29, 2025 | Ends Feb 01, 2026 | -| 2.10 | Apr 24, 2023 | Ended Feb 12, 2024 | No | -| 2.9 | Oct 03, 2022 | Ended Apr 24, 2023 | No | -| 2.8 | Jun 29, 2022 | Ended Oct 03, 2022 | No | -| 2.7 | May 24, 2022 | Ended Jun 29, 2022 | No | -| 2.6 | Jan 24, 2022 | Ended May 24, 2022 | No | -| 2.5 | Aug 17, 2021 | Ended Jan 24, 2022 | No | -| 2.4 | Jan 19, 2021 | Ended Aug 17, 2021 | No | -| 2.3 | Sep 23, 2020 | Ended Jan 19, 2021 | No | -| 2.2 | Mar 25, 2020 | Ended Sep 23, 2020 | No | -| 2.1 | Dec 11, 2019 | Ended Mar 25, 2020 | No | -| 2.0 | Sep 16, 2019 | Ended Dec 11, 2019 | No | -| 1.7 | Sep 24, 2018 | Ended Dec 31, 2021 | No | ??? example "Active Support / Security Support" diff --git a/docs/content/features/index.md b/docs/content/features/index.md new file mode 100644 index 000000000..335b7408f --- /dev/null +++ b/docs/content/features/index.md @@ -0,0 +1,148 @@ +--- +title: "Traefik Product Features Comparison" +description: "Compare features across Traefik Proxy, Traefik Hub API Gateway (including AI Gateway capabilities), and Traefik Hub API Management to choose the right solution for your needs." +--- + +# Traefik Product Features Comparison + +The Traefik ecosystem offers multiple products designed to meet different requirements, from basic reverse proxy functionality to comprehensive API management and AI gateway capabilities. This comparison matrix helps you understand the features available in each product and choose the right solution for your use case. + +## Product Overview + +- **Traefik Proxy** is the open-source application proxy that serves as the foundation for all Traefik products. It provides essential reverse proxy, load balancing, and service discovery capabilities. + +- **[Traefik Hub API Gateway](https://traefik.io/solutions/api-gateway/)** builds on Traefik Proxy with enterprise-grade security, distributed features, and advanced access control for cloud-native API gateway scenarios. It includes **AI Gateway capabilities** that transform any AI endpoint into a managed API. + +- **[Traefik Hub API Management](https://traefik.io/solutions/api-management/)** adds comprehensive API lifecycle management, developer portals, and organizational features for teams managing multiple APIs across environments. + +- **[Traefik AI Gateway](https://traefik.io/solutions/ai-gateway/)** transforms any AI endpoint into a managed API with unified access to multiple LLMs, centralized credential management, semantic caching, local inferencing, and comprehensive AI governance features. + +- **[Traefik MCP Gateway](https://traefik.io/solutions/mcp-gateway/)** provides secure, governed access to Model Context Protocol (MCP) servers for AI agents with task-based access control (TBAC), session-smart routing, and comprehensive audit capabilities for enterprise AI workflows. + +## Features Matrix + +| Feature | Traefik Proxy | Traefik Hub API Gateway | Traefik Hub API Management | +|---------|---------------|------------------------|---------------------------| +| **Core Networking** | | | | +| Services Auto-Discovery | ✓ | ✓ | ✓ | +| Graceful Configuration Reload | ✓ | ✓ | ✓ | +| Websockets, HTTP/2, HTTP/3, TCP, UDP, GRPC | ✓ | ✓ | ✓ | +| Real-time Logs, Access Logs, Metrics & Distributed Tracing | ✓ | ✓ | ✓ | +| Canary Deployments | ✓ | ✓ | ✓ | +| Let's Encrypt | ✓ | ✓ | ✓ | +| **Plugin Ecosystem** | | | | +| [Plugin Support](https://plugins.traefik.io/plugins) ([Go](https://github.com/traefik/yaegi), [WASM](https://webassembly.org/)) | ✓ | ✓ | ✓ | +| **Deployment & Operations** | | | | +| Hybrid cloud, multi-cloud & on-prem compatible | ✓ | ✓ | ✓ | +| Per-cluster dashboard | ✓ | ✓ | ✓ | +| GitOps-native declarative configuration | ✓ | ✓ | ✓ | +| **Authentication & Authorization** | | | | +| JWT Authentication | ✗ | ✓ | ✓ | +| OAuth 2.0 Token Introspection Authentication | ✗ | ✓ | ✓ | +| OAuth 2.0 Client Credentials Authentication | ✗ | ✓ | ✓ | +| OpenID Connect Authentication | ✗ | ✓ | ✓ | +| Lightweight Directory Access Protocol (LDAP) | ✗ | ✓ | ✓ | +| API Key Authentication | ✗ | ✓ | ✓ | +| **Security & Policy** | | | | +| Open Policy Agent | ✗ | ✓ | ✓ | +| Native Coraza Web Application Firewall (WAF) | ✗ | ✓ | ✓ | +| HashiCorp Vault Integration | ✗ | ✓ | ✓ | +| **Distributed Features** | | | | +| Distributed Let's Encrypt | ✗ | ✓ | ✓ | +| Distributed Rate Limit | ✗ | ✓ | ✓ | +| HTTP Caching | ✗ | ✓ | ✓ | +| **Compliance** | | | | +| FIPS 140-2 Compliance (Linux & Windows) | ✗ | ✓ | ✓ | +| **AI Gateway Capabilities** | | | | +| Unified Multi-LLM API Access | ✗ | ✓ | ✓ | +| Centralized AI Credential Management | ✗ | ✓ | ✓ | +| AI Provider Flexibility (OpenAI, Anthropic, Azure OpenAI, AWS Bedrock, etc.) | ✗ | ✓ | ✓ | +| Semantic Caching for AI Responses | ✗ | ✓ | ✓ | +| Content Guard & PII Protection | ✗ | ✓ | ✓ | +| AI-specific Observability & OpenTelemetry Integration | ✗ | ✓ | ✓ | +| Support for Local/Self-hosted LLMs & Inference (Ollama, Mistral, etc.) | ✗ | ✓ | ✓ | +| **MCP Gateway Capabilities** | | | | +| Task-Based Access Control (TBAC) for AI Agents | ✗ | ✓ | ✓ | +| MCP Servers Governance | ✗ | ✓ | ✓ | +| Session-Smart Load Balancing for Agent Workflows | ✗ | ✓ | ✓ | +| OAuth 2.1 / 2.0 Resource Server for MCP | ✗ | ✓ | ✓ | +| Fine-grained Policy Enforcement for AI Tools | ✗ | ✓ | ✓ | +| Audit-ready Observability for Agent Interactions | ✗ | ✓ | ✓ | +| **API Management** | | | | +| Flexible API grouping and versioning | ✗ | ✗ | ✓ | +| API Developer Portal | ✗ | ✗ | ✓ | +| OpenAPI Specifications Support | ✗ | ✗ | ✓ | +| Multi-cluster dashboard | ✗ | ✗ | ✓ | +| Built-in identity provider (or use your own) | ✗ | ✗ | ✓ | +| Configuration linter & change impact analysis | ✗ | ✗ | ✓ | +| Pre-built Grafana dashboards | ✗ | ✗ | ✓ | +| Event correlation for quick incident mitigation | ✗ | ✗ | ✓ | +| Traffic debugger | ✗ | ✓ | ✓ | +| **Support** | | | | +| Built-In Commercial Support | Add-on | ✓ | ✓ | + +## Choosing the Right Product + +### Start with Traefik Proxy + +Traefik Proxy is the ideal starting point for organizations looking for a reliable, open-source application proxy with essential networking capabilities. Deploy it as your default ingress tier if you need: + +- Basic reverse proxy and load balancing +- Service discovery for containerized applications +- Simple TLS termination and Let's Encrypt integration +- Cost-effective solution with community support (can upgrade to Traefik Hub for more features) + +### Upgrade to Traefik Hub API Gateway + +Traefik Hub API Gateway layers enterprise security, distributed coordination, and AI Gateway capabilities on top of Traefik Proxy. Upgrade to it when you need: + +- Enterprise security requirements (JWT, OIDC, LDAP) +- Distributed deployments across multiple clusters +- Advanced rate limiting and caching +- WAF and policy enforcement +- AI Gateway capabilities +- Commercial support + +### Consider Traefik AI Gateway + +Traefik AI Gateway unifies hosted and self-hosted LLM access under centralized control and observability. Consider it if you have: + +- Multi-LLM applications requiring unified API access +- Organizations using multiple AI providers (OpenAI, Anthropic, Azure OpenAI, AWS Bedrock, etc.) +- Local/self-hosted LLM deployments (Ollama, Mistral) +- Centralized AI credential and security management +- Cost optimization through semantic caching +- PII protection and content filtering for AI interactions +- Comprehensive AI observability and compliance requirements + +### Choose Traefik MCP Gateway + +Traefik MCP Gateway governs how AI agents interact with Model Context Protocol servers through task-aware policies and session-smart routing. Choose it if you need: + +- AI agent deployments requiring secure access to MCP servers +- Task-based access control (TBAC) for AI workflows +- Governance of Model Context Protocol interactions +- Session-smart routing for long-running agent conversations +- OAuth 2.1 / 2.0 compliant MCP server protection +- Audit-ready observability for AI agent activities +- Fine-grained policy enforcement for AI tools and resources + +### Choose Traefik Hub API Management + +Traefik Hub API Management extends the gateway foundation with API lifecycle tooling, developer experience features, and governance workflows. Choose it when you have: + +- Multiple APIs requiring centralized management +- Developer teams needing self-service portals +- Complex API versioning and lifecycle requirements +- Multi-cluster environments requiring unified dashboards +- Compliance and governance needs + +## Migration Path + +The Traefik ecosystem is designed for seamless upgrades. You can start with Traefik Proxy and add capabilities as your requirements grow: + +1. **Traefik Proxy** → **Hub API Gateway**: Add enterprise security, distributed features, and AI Gateway capabilities +2. **Hub API Gateway** → **Hub API Management**: Add comprehensive API management and governance features +3. **MCP Gateway**: Specialized solution for AI agent governance and Model Context Protocol management + +All products share the same core configuration concepts, making migration straightforward while preserving your existing configurations and operational knowledge. diff --git a/docs/content/getting-started/concepts.md b/docs/content/getting-started/concepts.md index 21e1817f7..a2bd774f8 100644 --- a/docs/content/getting-started/concepts.md +++ b/docs/content/getting-started/concepts.md @@ -57,4 +57,4 @@ You no longer need to create and synchronize configuration files cluttered with Traefik is able to use your cluster API to discover the services and read the attached information. In Traefik, these connectors are called [providers](../providers/overview.md "Link to overview about Traefik providers") because they *provide* the configuration to Traefik. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/getting-started/configuration-overview.md b/docs/content/getting-started/configuration-overview.md index 728004225..6aa4a7928 100644 --- a/docs/content/getting-started/configuration-overview.md +++ b/docs/content/getting-started/configuration-overview.md @@ -1,6 +1,6 @@ --- title: "Traefik Configuration Documentation" -description: "Get started with Traefik Proxy. This page will introduce you to the dynamic routing and startup configurations. Read the technical documentation." +description: "Get started with Traefik Proxy. This page will introduce you to the routing and install configurations. Read the technical documentation." --- # Configuration Introduction @@ -8,39 +8,37 @@ description: "Get started with Traefik Proxy. This page will introduce you to th How the Magic Happens {: .subtitle } -![Configuration](../assets/img/static-dynamic-configuration.png) - Configuration in Traefik can refer to two different things: -- The fully dynamic routing configuration (referred to as the _routing configuration_, formerly known as the _dynamic configuration_) -- The startup configuration (referred to as the _install configuration_, formerly known as the _static configuration_) +- The install (startup) configuration (formerly known as the _static configuration_) +- The routing configuration (formerly known as the _dynamic configuration_) -Elements in the install configuration_ set up connections to [providers](../providers/overview.md) and define the [entrypoints](../routing/entrypoints.md) Traefik will listen to (these elements don't change often). +Elements in the _install configuration_ set up connections to [providers](../providers/overview.md) and define the [entrypoints](../routing/entrypoints.md) Traefik will listen to (these elements don't change often). -The _dynamic configuration_ contains everything that defines how the requests are handled by your system. +The _routing configuration_ contains everything that defines how the requests are handled by your system. This configuration can change and is seamlessly hot-reloaded, without any request interruption or connection loss. !!! warning "Incompatible Configuration" Please be aware that the old configurations for Traefik v1.x are NOT compatible with the v2.x config as of now. If you are running v2, please ensure you are using a v2 configuration. -## The Dynamic Configuration +## The Routing Configuration -Traefik gets its _dynamic configuration_ from [providers](../providers/overview.md): whether an orchestrator, a service registry, or a plain old configuration file. +Traefik gets its _routing configuration_ from [providers](../providers/overview.md): whether an orchestrator, a service registry, or a plain old configuration file. Since this configuration is specific to your infrastructure choices, we invite you to refer to the [dedicated section of this documentation](../routing/overview.md). !!! info "" - In the [Quick Start example](../getting-started/quick-start.md), the routing configuration comes from docker in the form of labels attached to your containers. + In the [Quick Start example](../getting-started/docker.md), the whoami application routing configuration comes from docker in the form of a label attached to the whoami container. !!! info "HTTPS Certificates also belong to the routing configuration." You can add / update / remove them without restarting your Traefik instance. -## The Static Configuration +## The Install Configuration -There are three different, **mutually exclusive** (i.e. you can use only one at the same time), ways to define static configuration options in Traefik: +There are three different, **mutually exclusive** (i.e. you can use only one at the same time), ways to define install configuration options in Traefik: 1. In a configuration file 1. In the command-line arguments @@ -56,7 +54,7 @@ Once positioned, this option sets (and resets) all the default values of the sub ### Configuration File -At startup, Traefik searches for static configuration in a file named `traefik.yml` (or `traefik.yaml` or `traefik.toml`) in: +At startup, Traefik searches for install configuration in a file named `traefik.yml` (or `traefik.yaml` or `traefik.toml`) in: - `/etc/traefik/` - `$XDG_CONFIG_HOME/` @@ -79,14 +77,14 @@ traefik --help # or docker run traefik[:version] --help -# ex: docker run traefik:v3.5 --help +# ex: docker run traefik:v3.6 --help ``` Check the [CLI reference](../reference/install-configuration/configuration-options.md "Link to CLI reference overview") for an overview about all available arguments. ### Environment Variables -All available environment variables can be found in the [static configuration environment overview](../reference/install-configuration/configuration-options.md). +All available environment variables can be found in the [install configuration environment overview](../reference/install-configuration/configuration-options.md). ## Available Configuration Options @@ -94,4 +92,4 @@ All the configuration options are documented in their related section. You can browse the available features in the menu, the [providers](../providers/overview.md), or the [routing section](../routing/overview.md) to see them in action. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/getting-started/docker.md b/docs/content/getting-started/docker.md index 6cd151b32..028fb9670 100644 --- a/docs/content/getting-started/docker.md +++ b/docs/content/getting-started/docker.md @@ -36,7 +36,7 @@ This configuration: # docker-compose.yml services: traefik: - image: traefik:v3.5 + image: traefik:v3.6 command: - "--api.insecure=true" - "--providers.docker=true" @@ -84,7 +84,7 @@ docker run -d \ -p 8080:8080 \ -v $PWD/traefik.yml:/etc/traefik/traefik.yml \ -v /var/run/docker.sock:/var/run/docker.sock \ - traefik:v3.5 + traefik:v3.6 ``` ## Expose the Dashboard @@ -159,4 +159,4 @@ That's it! You've successfully deployed Traefik and configured routing in Docker - [Enable Metrics](../reference/install-configuration/observability/metrics.md) - [Learn more about Docker provider](../reference/install-configuration/providers/docker.md) -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/getting-started/faq.md b/docs/content/getting-started/faq.md index a64e04186..e808c4211 100644 --- a/docs/content/getting-started/faq.md +++ b/docs/content/getting-started/faq.md @@ -252,4 +252,4 @@ In which case, you should make sure your infrastructure is properly set up for a LEGO_DISABLE_CNAME_SUPPORT=true ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/getting-started/install-traefik.md b/docs/content/getting-started/install-traefik.md index 920db9530..54d3edad5 100644 --- a/docs/content/getting-started/install-traefik.md +++ b/docs/content/getting-started/install-traefik.md @@ -16,12 +16,12 @@ You can install Traefik with the following flavors: Choose one of the [official Docker images](https://hub.docker.com/_/traefik) and run it with one sample configuration file: -* [YAML](https://raw.githubusercontent.com/traefik/traefik/v3.5/traefik.sample.yml) -* [TOML](https://raw.githubusercontent.com/traefik/traefik/v3.5/traefik.sample.toml) +* [YAML](https://raw.githubusercontent.com/traefik/traefik/v3.6/traefik.sample.yml) +* [TOML](https://raw.githubusercontent.com/traefik/traefik/v3.6/traefik.sample.toml) ```shell docker run -d -p 8080:8080 -p 80:80 \ - -v $PWD/traefik.yml:/etc/traefik/traefik.yml traefik:v3.5 + -v $PWD/traefik.yml:/etc/traefik/traefik.yml traefik:v3.6 ``` For more details, go to the [Docker provider documentation](../providers/docker.md) @@ -29,7 +29,7 @@ For more details, go to the [Docker provider documentation](../providers/docker. !!! tip * Prefer a fixed version than the latest that could be an unexpected version. - ex: `traefik:v3.5` + ex: `traefik:v3.6` * Docker images are based from the [Alpine Linux Official image](https://hub.docker.com/_/alpine). * Any orchestrator using docker images can fetch the official Traefik docker image. @@ -144,4 +144,4 @@ And run it: All the details are available in the [Contributing Guide](../contributing/building-testing.md) -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/getting-started/kubernetes.md b/docs/content/getting-started/kubernetes.md index 1c85e6d12..882360547 100644 --- a/docs/content/getting-started/kubernetes.md +++ b/docs/content/getting-started/kubernetes.md @@ -331,4 +331,4 @@ That's it! You've successfully deployed Traefik and configured routing in a Kube - [Learn more about Kubernetes CRD provider](../reference/install-configuration/providers/kubernetes/kubernetes-crd.md) - [Learn more about Kubernetes Gateway API provider](../reference/install-configuration/providers/kubernetes/kubernetes-gateway.md) -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/https/acme.md b/docs/content/https/acme.md index 63680ea68..aa3b8ecec 100644 --- a/docs/content/https/acme.md +++ b/docs/content/https/acme.md @@ -201,6 +201,36 @@ when using the `TLS-ALPN-01` challenge, Traefik must be reachable by Let's Encry --certificatesresolvers.myresolver.acme.tlschallenge=true ``` +#### `Delay` + +_Optional, Default=0_ + +The delay between the creation of the challenge and the validation. +A value lower than or equal to zero means no delay. + +```yaml tab="File (YAML)" +certificatesResolvers: + myresolver: + acme: + # ... + tlsChallenge: + # ... + delay: 12 +``` + +```toml tab="File (TOML)" +[certificatesResolvers.myresolver.acme] + # ... + [certificatesResolvers.myresolver.acme.tlsChallenge] + # ... + delay = 12 +``` + +```bash tab="CLI" +# ... +--certificatesresolvers.myresolver.acme.tlschallenge.delay=12 +``` + ### `httpChallenge` Use the `HTTP-01` challenge to generate and renew ACME certificates by provisioning an HTTP resource under a well-known URI. @@ -252,6 +282,8 @@ when using the `HTTP-01` challenge, `certificatesresolvers.myresolver.acme.httpc #### `Delay` +_Optional, Default=0_ + The delay between the creation of the challenge and the validation. A value lower than or equal to zero means no delay. @@ -1004,6 +1036,39 @@ certificatesResolvers: # ... ``` +### `disableCommonName` + +_Optional, Default=false_ + +Disable common name inside CSR and certificates. + +It's recommended to disable the common name and required to get a certificate for IP. + +- https://letsencrypt.org/docs/profiles/#certificate-common-name +- https://community.letsencrypt.org/t/ip-san-error-csr-contains-ip-address-in-common-name/239012/7 + +```yaml tab="File (YAML)" +certificatesResolvers: + myresolver: + acme: + # ... + disableCommonName: true + # ... +``` + +```toml tab="File (TOML)" +[certificatesResolvers.myresolver.acme] + # ... + disableCommonName = true + # ... +``` + +```bash tab="CLI" +# ... +--certificatesresolvers.myresolver.acme.disableCommonName=true +# ... +``` + ### `keyType` _Optional, Default="RSA4096"_ @@ -1146,4 +1211,4 @@ If Let's Encrypt is not reachable, the following certificates will apply: !!! important For new (sub)domains which need Let's Encrypt authentication, the default Traefik certificate will be used until Traefik is restarted. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/https/overview.md b/docs/content/https/overview.md index 9261d0db8..ae07504d9 100644 --- a/docs/content/https/overview.md +++ b/docs/content/https/overview.md @@ -20,4 +20,4 @@ That is to say, how to obtain [TLS certificates](./tls.md#certificates-definitio either through a definition in the dynamic configuration, or through [Let's Encrypt](./acme.md) (ACME). And how to configure [TLS options](./tls.md#tls-options), and [certificates stores](./tls.md#certificates-stores). -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/https/tls.md b/docs/content/https/tls.md index 6b48e8e46..abda2dd93 100644 --- a/docs/content/https/tls.md +++ b/docs/content/https/tls.md @@ -587,4 +587,4 @@ spec: disableSessionTickets: true ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/index.md b/docs/content/index.md index de8516356..e8ebfc818 100644 --- a/docs/content/index.md +++ b/docs/content/index.md @@ -11,6 +11,8 @@ Traefik is an [open-source](https://github.com/traefik/traefik) Application Prox If you start with Traefik for service discovery and routing, you can seamlessly add [API management](https://traefik.io/solutions/api-management/), [API gateway](https://traefik.io/solutions/api-gateway/), [AI gateway](https://traefik.io/solutions/ai-gateway/), and [API mocking](https://traefik.io/solutions/api-mocking/) capabilities as needed. +For a detailed comparison of all Traefik products and their capabilities, see our [Product Features Comparison](./features/). + With 3.3 billion downloads and over 55k stars on GitHub, Traefik is used globally across hybrid cloud, multi-cloud, on prem, and bare metal environments running Kubernetes, Docker Swarm, AWS, [the list goes on](https://doc.traefik.io/traefik/reference/install-configuration/providers/overview/). Here’s how it works—Traefik receives requests on behalf of your system, identifies which components are responsible for handling them, and routes them securely. It automatically discovers the right configuration for your services by inspecting your infrastructure to identify relevant information and which service serves which request. diff --git a/docs/content/middlewares/http/basicauth.md b/docs/content/middlewares/http/basicauth.md index 75d3a9a0c..f7017842e 100644 --- a/docs/content/middlewares/http/basicauth.md +++ b/docs/content/middlewares/http/basicauth.md @@ -340,4 +340,4 @@ http: removeHeader = true ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/middlewares/http/forwardauth.md b/docs/content/middlewares/http/forwardauth.md index 5f60cd138..81bbcedd5 100644 --- a/docs/content/middlewares/http/forwardauth.md +++ b/docs/content/middlewares/http/forwardauth.md @@ -785,4 +785,4 @@ http: preserveRequestMethod = true ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/middlewares/http/headers.md b/docs/content/middlewares/http/headers.md index 419642391..a302d6231 100644 --- a/docs/content/middlewares/http/headers.md +++ b/docs/content/middlewares/http/headers.md @@ -422,4 +422,4 @@ Set `isDevelopment` to `true` when developing to mitigate the unwanted effects o Usually testing takes place using HTTP, not HTTPS, and on `localhost`, not your production domain. If you would like your development environment to mimic production with complete Host blocking, SSL redirects, and STS headers, leave this as `false`. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/middlewares/http/overview.md b/docs/content/middlewares/http/overview.md index cb40c56b4..27f0d4fd2 100644 --- a/docs/content/middlewares/http/overview.md +++ b/docs/content/middlewares/http/overview.md @@ -127,4 +127,4 @@ http: Please take a look at the community-contributed plugins in the [plugin catalog](https://plugins.traefik.io/plugins). -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/middlewares/http/redirectregex.md b/docs/content/middlewares/http/redirectregex.md index 5488117db..a28fa0ada 100644 --- a/docs/content/middlewares/http/redirectregex.md +++ b/docs/content/middlewares/http/redirectregex.md @@ -85,4 +85,4 @@ The `replacement` option defines how to modify the URL to have the new target UR Care should be taken when defining replacement expand variables: `$1x` is equivalent to `${1x}`, not `${1}x` (see [Regexp.Expand](https://golang.org/pkg/regexp/#Regexp.Expand)), so use `${1}` syntax. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/middlewares/http/stripprefix.md b/docs/content/middlewares/http/stripprefix.md index 5c985b243..a68e813fc 100644 --- a/docs/content/middlewares/http/stripprefix.md +++ b/docs/content/middlewares/http/stripprefix.md @@ -146,4 +146,4 @@ http: forceSlash = false ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/middlewares/overview.md b/docs/content/middlewares/overview.md index 92be4ecda..5a2971791 100644 --- a/docs/content/middlewares/overview.md +++ b/docs/content/middlewares/overview.md @@ -112,4 +112,4 @@ A list of HTTP middlewares can be found [here](http/overview.md). A list of TCP middlewares can be found [here](tcp/overview.md). -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/migrate/nginx-to-traefik.md b/docs/content/migrate/nginx-to-traefik.md new file mode 100644 index 000000000..a91c862c8 --- /dev/null +++ b/docs/content/migrate/nginx-to-traefik.md @@ -0,0 +1,678 @@ +--- +title: "Migrate from Ingress NGINX Controller to Traefik" +description: "Step-by-step guide to migrate from Kubernetes Ingress NGINX Controller to Traefik with zero downtime and annotation compatibility." +--- + +# Migrate from Ingress NGINX Controller to Traefik + +How to migrate from Ingress NGINX Controller to Traefik with zero downtime. +{: .subtitle } + +--- + +!!! danger "Ingress NGINX Controller Retirement" + + The Kubernetes Ingress NGINX Controller project has announced its retirement in **March 2026**. After this date: + + - No new releases or updates + - No security patches + - No bug fixes + + For more information, see the [official Kubernetes blog announcement](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement). + +## What You Will Achieve + +By completing this migration, your existing Ingress resources will work with Traefik without any modifications. The Traefik Kubernetes Ingress NGINX Provider automatically translates NGINX annotations into Traefik configuration: + +```yaml tab="Your Existing Ingress (No Changes Needed)" +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: myapp + annotations: + # These NGINX annotations are automatically translated by Traefik + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/force-ssl-redirect: "true" + nginx.ingress.kubernetes.io/enable-cors: "true" + nginx.ingress.kubernetes.io/cors-allow-origin: "https://example.com" + nginx.ingress.kubernetes.io/affinity: "cookie" + nginx.ingress.kubernetes.io/session-cookie-name: "route" +spec: + ingressClassName: nginx # ← Traefik will watch this class + rules: + - host: myapp.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: whoami + port: + number: 80 + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: whoami +spec: + replicas: 2 + selector: + matchLabels: + app: whoami + template: + metadata: + labels: + app: whoami + spec: + containers: + - name: whoami + image: traefik/whoami + ports: + - containerPort: 80 + +--- +apiVersion: v1 +kind: Service +metadata: + name: whoami +spec: + selector: + app: whoami + ports: + - protocol: TCP + port: 80 + targetPort: 80 +``` + +For a complete list of supported annotations and behavioral differences, see the [Ingress NGINX Routing Configuration](../reference/routing-configuration/kubernetes/ingress-nginx.md) documentation. + +!!! info "Traefik Version Requirement" + + The Kubernetes Ingress NGINX provider requires **Traefik v3.6.2 or later**. + +--- + +## Prerequisites + +Before starting the migration, ensure you have: + +- **Existing Ingress NGINX Controller** running in your Kubernetes cluster +- **Kubernetes cluster access** with `kubectl` configured +- **Cluster support for running multiple LoadBalancer services** on ports 80/443 simultaneously +- **Helm** +- **Cluster admin permissions** to create RBAC resources +- **Backup of critical configurations** (Ingress resources, ConfigMaps, Secrets) + +!!! tip "Backup Recommendations" + + ```bash + # Export all Ingress resources + kubectl get ingress --all-namespaces -o yaml > ingress-backup.yaml + + # Export NGINX ConfigMaps + kubectl get configmap --all-namespaces -l app.kubernetes.io/name=ingress-nginx -o yaml > nginx-configmaps.yaml + ``` + +--- + +## Migration Strategy Overview + +This migration achieves **zero downtime** by running Traefik alongside NGINX. Both controllers serve the same Ingress resources simultaneously, allowing you to progressively shift traffic before removing NGINX. + +```text +Current: DNS → LoadBalancer → NGINX → Your Services + +Migration: DNS → LoadBalancer → NGINX → Your Services + → LoadBalancer → Traefik → Your Services + +Final: DNS → LoadBalancer → Traefik → Your Services +``` + +**Migration Flow:** + +1. Install Traefik alongside NGINX (both serving traffic in parallel) +2. Add Traefik LoadBalancer to DNS (if you choose DNS option; cf. step 3) +3. Progressively shift traffic from NGINX to Traefik +4. Remove NGINX from DNS, preserve the IngressClass, and uninstall + +--- + +## Step 1: Install Traefik Alongside NGINX + +??? info "Install Ingress NGINX Controller" + + If you have not installed Ingress NGINX Controller yet, you can set up a fresh Ingress NGINX Controller installation following the instructions below: + + ### Install Ingress NGINX Controller + + ```bash + helm upgrade --install ingress-nginx ingress-nginx \ + --repo https://kubernetes.github.io/ingress-nginx \ + --namespace ingress-nginx --create-namespace + ``` +Install Traefik with the Kubernetes Ingress NGINX provider enabled. Both controllers will serve the same Ingress resources simultaneously. + +### Add Traefik Helm Repository + +```bash +helm repo add traefik https://traefik.github.io/charts +helm repo update +``` + +### Install Traefik + +```bash +helm upgrade --install traefik traefik/traefik \ + --namespace traefik --create-namespace \ + --set providers.kubernetesIngressNginx.enabled=true +``` + +Or using a [values file](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/VALUES.md) for more configuration: + +```yaml tab="traefik-values.yaml" +... +providers: + kubernetesIngressNginx: + enabled: true + ... +``` + +```bash +helm upgrade --install traefik traefik/traefik \ + --namespace traefik --create-namespace \ + --values traefik-values.yaml +``` + +### Verify Both Controllers Are Running + +```bash +# Check NGINX pods +kubectl get pods -n ingress-nginx + +# Check Traefik pods +kubectl get pods -n traefik + +# Check both services have LoadBalancer IPs +kubectl get svc -n ingress-nginx ingress-nginx-controller +kubectl get svc -n traefik traefik +``` + +At this point, both NGINX and Traefik are running and can serve the same Ingress resources. Traffic is still flowing only through NGINX since DNS points to the NGINX LoadBalancer. + +--- + +## Step 2: Verify Traefik Is Handling Traffic + +Before adding Traefik to DNS, verify it correctly serves your Ingress resources. + +### Test via Traefik's LoadBalancer IP + +Get Traefik's LoadBalancer IP and use `--resolve` to test without changing DNS: + +```bash +# Get LoadBalancer IPs +NGINX_IP=$(kubectl get svc -n ingress-nginx ingress-nginx-controller -o go-template='{{ $ing := index .status.loadBalancer.ingress 0 }}{{ if $ing.ip }}{{ $ing.ip }}{{ else }}{{ $ing.hostname }}{{ end }}') +TRAEFIK_IP=$(kubectl get svc -n traefik traefik -o go-template='{{ $ing := index .status.loadBalancer.ingress 0 }}{{ if $ing.ip }}{{ $ing.ip }}{{ else }}{{ $ing.hostname }}{{ end }}') +echo -e "Nginx IP: $NGINX_IP\nTraefik IP: $TRAEFIK_IP" + +# Test HTTP for both +FQDN=myapp.example.com +# Observe HTTPS redirections: +curl --connect-to "${FQDN}:80:${NGINX_IP}:80" "http://${FQDN}" -D - +curl --connect-to "${FQDN}:80:${TRAEFIK_IP}:80" "http://${FQDN}" -D - # note X-Forwarded-Server which should be traefik + +# Test HTTPS +curl --connect-to "${FQDN}:443:${NGINX_IP}:443" "https://${FQDN}" +curl --connect-to "${FQDN}:443:${TRAEFIK_IP}:443" "https://${FQDN}" +``` + +!!! warning "TLS Certificates During Migration" + + Both NGINX and Traefik must serve valid TLS certificates for HTTPS tests to succeed. Since Traefik is not publicly exposed during this verification phase, **Let's Encrypt HTTP challenge will not work**. + + Your options for TLS certificates during migration: + + - **Existing certificates via `tls.secretName`** - If you use cert-manager or another external tool, your existing TLS secrets referenced in `spec.tls` will work with both controllers + - **Let's Encrypt DNS challenge** - Configure Traefik's [ACME DNS challenge](../reference/install-configuration/tls/certificate-resolvers/acme.md#dnschallenge) to obtain certificates without public exposure + + Avoid using `curl -k` (skip certificate verification) as this masks TLS configuration issues that could cause problems after migration. + +### Verify Ingress Discovery + +Check Traefik logs to confirm it discovered your Ingress resources: + +```bash +kubectl logs -n traefik deployment/traefik | grep -i "ingress" +``` + +--- + +## Step 3: Shift Traffic to Traefik + +With both controllers running and verified, progressively shift traffic from NGINX to Traefik. + +### Option A: DNS-Based Migration + +Add the Traefik LoadBalancer IP to your DNS records alongside NGINX. This allows both controllers to receive traffic. + +**Get LoadBalancer addresses:** + +```bash +# NGINX LoadBalancer +echo $(kubectl get svc -n ingress-nginx ingress-nginx-controller -o go-template='{{ $ing := index .status.loadBalancer.ingress 0 }}{{ if $ing.ip }}{{ $ing.ip }}{{ else }}{{ $ing.hostname }}{{ end }}') + +# Traefik LoadBalancer +echo $(kubectl get svc -n traefik traefik -o go-template='{{ $ing := index .status.loadBalancer.ingress 0 }}{{ if $ing.ip }}{{ $ing.ip }}{{ else }}{{ $ing.hostname }}{{ end }}') +``` + +**Progressive DNS migration:** + +1. **Add Traefik to DNS** - Add the Traefik LoadBalancer IP to your DNS records (both IPs now receive traffic via round-robin) +2. **Monitor** - Observe traffic patterns on both controllers +3. **Remove NGINX from DNS** - Once confident, remove the NGINX LoadBalancer IP from DNS +4. **Wait for DNS propagation** - Allow time for DNS caches to expire +5. **Uninstall NGINX** - Proceed to [Step 4](#step-4-uninstall-ingress-nginx-controller) + +!!! warning "DNS TTL May Not Be Respected" + + Some ISPs ignore DNS TTL values to reduce traffic costs, caching records longer than specified. After removing NGINX from DNS, keep NGINX running for at least 24-48 hours before uninstalling to avoid dropping traffic from users whose ISPs have stale DNS caches. + +??? info "ExternalDNS Users" + + If you use [ExternalDNS](https://github.com/kubernetes-sigs/external-dns) to automatically manage DNS records based on Ingress status, both NGINX and Traefik will compete to update the Ingress status with their LoadBalancer IPs when `publishService` is enabled. Traefik typically wins because it updates faster, which can cause unexpected traffic shifts. + + **Recommended approach for ExternalDNS:** + + 1. **[Install Traefik](#step-1-install-traefik-alongside-nginx) with `publishService` disabled**: + + ```yaml + # traefik-values.yaml + providers: + kubernetesIngressNginx: + enabled: true + publishService: + enabled: false # Disable to prevent status updates + ``` + + 2. **Test Traefik** using [port-forward](#step-2-verify-traefik-is-handling-traffic) or a separate test hostname + + 3. **Switch DNS via NGINX** - Configure NGINX to publish Traefik's service address: + + ```yaml + # nginx-values.yaml + controller: + publishService: + pathOverride: "traefik/traefik" # Points to Traefik's service + ``` + + This makes NGINX update the Ingress status with Traefik's LoadBalancer IP, causing ExternalDNS to point traffic to Traefik. + + 4. **Verify traffic flows through Traefik** - At this point, you can still rollback by removing the `pathOverride` + + 5. **[Enable `publishService` on Traefik](#step-1-install-traefik-alongside-nginx)** and [uninstall NGINX](#step-5-uninstall-nginx-ingress-controller) + +### Option B: External Load Balancer with Weighted Traffic + +For more control over traffic distribution, use an external load balancer (like Traefik, Cloudflare, AWS ALB, or a dedicated load balancer) in front of both Kubernetes LoadBalancers. + +!!! note "Infrastructure Prerequisite" + + This option assumes you already have an external load balancer in your infrastructure, or are willing to set one up **before** starting the migration. Adding an external load balancer is a significant infrastructure change that should be planned and tested separately from the ingress controller migration. + +**Setup:** + +1. Create an external load balancer pointing to the NGINX Kubernetes LoadBalancer +2. Update DNS to point to the external load balancer +3. Add the Traefik Kubernetes LoadBalancer to the external load balancer with a low weight (e.g., 10%) +4. Gradually increase Traefik's weight while decreasing NGINX's weight +5. Once NGINX receives no traffic, uninstall it + +**Example weight progression:** + +| Phase | NGINX Weight | Traefik Weight | Duration | +|-------|-------------|----------------|----------| +| Initial | 100% | 0% | - | +| Start | 90% | 10% | 1 hour | +| Increase | 50% | 50% | 2 hour | +| Near-complete | 10% | 90% | 4 hour | +| Final | 0% | 100% | - | + +!!! tip "External Load Balancer Options" + + - **Cloudflare Load Balancing** - Traffic steering with health checks + - **AWS Global Accelerator** - Weighted routing across endpoints + - **Google Cloud Load Balancing** - Traffic splitting + - **Traefik / HAProxy / NGINX (external)** - Self-hosted option with weighted backends + - ... + +### LoadBalancer IP Retention + +If you want Traefik to eventually use the same LoadBalancer IP as NGINX (to simplify DNS management), you can transfer the IP after the migration. Since Traefik is already running with its own LoadBalancer, this can be done with zero downtime. + +**Zero-downtime IP transfer process:** + +1. Traefik is already running with its own LoadBalancer IP (from Step 1) +2. Add Traefik's LoadBalancer IP to DNS (traffic now goes to both NGINX and Traefik) +3. Remove NGINX's IP from DNS and wait for propagation +4. Delete NGINX's LoadBalancer service to release the IP +5. Upgrade Traefik to claim the released IP +6. (Optional) Remove Traefik's old IP from DNS once the new IP is active + +This way, traffic is always flowing to Traefik during the IP transfer. + +**Get your current NGINX LoadBalancer IP:** + +```bash +kubectl get svc -n ingress-nginx ingress-nginx-controller -o go-template='{{ $ing := index .status.loadBalancer.ingress 0 }}{{ if $ing.ip }}{{ $ing.ip }}{{ else }}{{ $ing.hostname }}{{ end }}' +``` + +??? note "AWS (Network Load Balancer with Elastic IPs)" + + AWS does not support static IPs for Classic Load Balancers. Use Network Load Balancers (NLB) with Elastic IPs instead. This requires the [AWS Load Balancer Controller](https://kubernetes-sigs.github.io/aws-load-balancer-controller/) to be installed in your cluster. + + **Pre-allocate Elastic IPs (one per availability zone):** + + ```bash + aws ec2 allocate-address --domain vpc --region + # Note the AllocationId (eipalloc-xxx) for each EIP + ``` + + **Update `traefik-values.yaml`:** + + ```yaml + service: + type: LoadBalancer + loadBalancerClass: service.k8s.aws/nlb # Requires AWS Load Balancer Controller + annotations: + service.beta.kubernetes.io/aws-load-balancer-type: "external" + service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip" + service.beta.kubernetes.io/aws-load-balancer-eip-allocations: "eipalloc-xxx,eipalloc-yyy" + ``` + + For more details, see the [AWS Load Balancer Controller annotations documentation](https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/guide/service/annotations/). + +??? note "Azure" + + Azure supports static public IPs for Load Balancers. + + **Identify existing public IP:** + + ```bash + az network public-ip list --resource-group \ + --query "[?ipAddress==''].name" -o tsv + ``` + + **Update `traefik-values.yaml`:** + + ```yaml + service: + type: LoadBalancer + annotations: + # Only needed if the public IP is in a different resource group than the AKS cluster + service.beta.kubernetes.io/azure-load-balancer-resource-group: "" + spec: + loadBalancerIP: "" + ``` + + For more details, see the [Azure AKS static IP documentation](https://learn.microsoft.com/en-us/azure/aks/static-ip). + +??? note "GCP" + + GCP supports static IPs through reserved regional IP addresses. + + **Reserve or identify existing IP:** + + ```bash + # List existing static IPs + gcloud compute addresses list + + # Or reserve a new regional static IP (must be in the same region as your GKE cluster) + gcloud compute addresses create traefik-ip --region + ``` + + **Update `traefik-values.yaml`:** + + ```yaml + service: + type: LoadBalancer + spec: + loadBalancerIP: "" + ``` + + For more details, see the [GKE LoadBalancer Service parameters documentation](https://cloud.google.com/kubernetes-engine/docs/concepts/service-load-balancer-parameters). + +??? note "Other Cloud Providers" + + - **DigitalOcean:** Supports `loadBalancerIP` with floating IPs + - **Linode:** Supports `loadBalancerIP` specification + - **Bare Metal (MetalLB):** Use IP address pools + +**Transfer the IP:** + +Once DNS is pointing to Traefik and your values are configured with the target IP: + +```bash +# Ensure Traefik is already receiving traffic via its current LoadBalancer +kubectl get svc -n traefik traefik + +# Delete NGINX LoadBalancer service to release the IP +kubectl delete svc -n ingress-nginx ingress-nginx-controller + +# Upgrade Traefik to claim the released IP +helm upgrade traefik traefik/traefik \ + --namespace traefik \ + --values traefik-values.yaml + +# Verify Traefik now has the old NGINX IP +kubectl get svc -n traefik traefik +``` + +!!! tip "Zero Downtime During Helm Upgrade" + + The Helm upgrade only restarts the Traefik pod, not the LoadBalancer service. Traefik uses a `RollingUpdate` deployment strategy by default, so the new pod starts before the old one terminates. For additional safety, configure high availability: + + ```yaml + # In traefik-values.yaml + deployment: + replicas: 2 + + # Spread pods across nodes to survive node failures + affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: + app.kubernetes.io/name: traefik + app.kubernetes.io/instance: traefik + topologyKey: kubernetes.io/hostname + + # Ensure at least one pod is always available during disruptions + podDisruptionBudget: + enabled: true + minAvailable: 1 + ``` + + With multiple replicas spread across nodes and a PodDisruptionBudget, at least one pod is always running during upgrades and node maintenance. + +--- + +## Step 4: Uninstall Ingress NGINX Controller + +Once NGINX is no longer receiving traffic, remove it from your cluster. Before uninstalling, you must ensure the `nginx` IngressClass is preserved. Traefik needs it to continue discovering your Ingresses. + +### Preserve the IngressClass + +??? note "If NGINX Was Installed via Helm" + + Add the `helm.sh/resource-policy: keep` annotation to tell Helm to preserve the IngressClass: + + ```bash + # Add the required annotation + helm upgrade ingress-nginx ingress-nginx \ + --repo https://kubernetes.github.io/ingress-nginx \ + --namespace ingress-nginx \ + --reuse-values \ + --set-json 'controller.ingressClassResource.annotations={"helm.sh/resource-policy": "keep"}' + # Check that the annotation is really here + kubectl describe ingressclass nginx + ``` + + The `--reuse-values` flag is critical - it preserves all your existing NGINX configuration. Without it, Helm would reset everything to defaults, potentially breaking your setup. + + !!! info "kubectl annotate/patch/edit does not work" + + Adding the annotation via `kubectl annotate`, `kubectl patch`, or `kubectl edit` will not preserve the IngressClass. Helm stores its release state internally and checks annotations from its internal manifest, not the live cluster state. Only `helm upgrade` updates Helm's internal state. + +??? note "If NGINX Was Installed via GitOps (ArgoCD, Flux)" + + Ensure the `nginx` IngressClass is defined as a standalone resource in your Git repository, separate from the NGINX Helm release: + + ```yaml + # ingressclass.yaml + apiVersion: networking.k8s.io/v1 + kind: IngressClass + metadata: + name: nginx + spec: + controller: k8s.io/ingress-nginx + ``` + +??? note "If NGINX Was Installed Manually" + + Create the IngressClass as a standalone resource: + + ```bash + kubectl apply -f - < -o yaml | grep ingressClassName + ``` + +??? note "Annotation Not Working as Expected" + + Some NGINX annotations have behavioral differences in Traefik. Check the [limitations documentation](../reference/routing-configuration/kubernetes/ingress-nginx.md#limitations) for details. + +??? note "TLS Certificates Not Working" + + Existing TLS configurations continue to work with Traefik: + + - Keep `spec.tls` entries exactly as-is; Traefik terminates TLS using the referenced secrets + - TLS secrets must stay in the same namespace as the Ingress + - NGINX `ssl-redirect` / `force-ssl-redirect` annotations are honored + + ```bash + # Verify TLS secret exists in the same namespace as Ingress + kubectl get secrets -n + + # Check secret format + kubectl get secret -n -o yaml + ``` + +??? note "LoadBalancer IP Not Assigned" + + ```bash + # Check service status + kubectl describe svc -n traefik traefik + + # Check for events + kubectl get events -n traefik --sort-by='.lastTimestamp' + ``` + +--- + +## Next Steps + +**Learn More About Traefik:** + +- [Kubernetes Ingress NGINX Install Configuration](../reference/install-configuration/providers/kubernetes/kubernetes-ingress-nginx.md) - Detailed provider configuration +- [Kubernetes Ingress NGINX Routing Configuration](../reference/routing-configuration/kubernetes/ingress-nginx.md) - Routing rules and annotation support +- [HTTP Middlewares](../reference/routing-configuration/http/middlewares/overview.md) - Extend functionality beyond NGINX annotations +- [TLS Configuration](../reference/routing-configuration/http/tls/overview.md) - Advanced TLS and certificate management + +**Enhance Your Setup:** + +- Enable [metrics](../reference/install-configuration/observability/metrics.md) and [tracing](../reference/install-configuration/observability/tracing.md) +- Configure [access logs](../reference/install-configuration/observability/logs-and-accesslogs.md) for observability +- Explore [Traefik Middlewares](../reference/routing-configuration/http/middlewares/overview.md) for advanced traffic management +- Migrate from Nginx-based config to Traefik [IngressRoute](../reference/routing-configuration/kubernetes/crd/http/ingressroute.md) or [Kubernetes Gateway API](../reference/routing-configuration/kubernetes/gateway-api.md) +- Consider [Traefik Hub](https://traefik.io/traefik-hub/) for enterprise features like AI & API Gateway, API Management, and advanced security + +--- + +## Feedback and Support + +If you encounter issues during migration or have suggestions for improving this guide: + +- **Report Issues:** [GitHub Issues](https://github.com/traefik/traefik/issues) +- **Community Support:** [Traefik Community Forum](https://community.traefik.io/) +- **Enterprise Support:** [Traefik Labs Commercial Support](https://traefik.io/pricing/) + +We welcome contributions to improve this migration guide. See our [contribution guidelines](../contributing/submitting-pull-requests.md) to get started. diff --git a/docs/content/migrate/v2-to-v3.md b/docs/content/migrate/v2-to-v3.md index 175f36921..2c09f86f9 100644 --- a/docs/content/migrate/v2-to-v3.md +++ b/docs/content/migrate/v2-to-v3.md @@ -158,4 +158,4 @@ core: - ✅ All applications functioning correctly - ✅ Performance metrics stable -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/migrate/v3.md b/docs/content/migrate/v3.md index 657dac8d6..d947af7b7 100644 --- a/docs/content/migrate/v3.md +++ b/docs/content/migrate/v3.md @@ -496,3 +496,110 @@ kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/con Starting with `v3.5.4`, and when using OpenTelemetry, the `traefik_tls_certs_not_after_milliseconds` metric is renamed to `traefik_tls_certs_not_after_seconds`. This change aligns the metric name with its real unit precision, which is in seconds. + +## v3.6.0 + +### Kubernetes Gateway API Provider + +Starting with `v3.6.0`, the Kubernetes Gateway API provider only supports version [v1.4.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.4.0) of the specification, +which requires the Gateway API CRDs to be updated. + +**Apply Updated CRDs:** + +```shell +kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/standard-install.yaml +``` + +For the experimental channel: + +```shell +kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/experimental-install.yaml +``` + +### Kubernetes CRD Provider + +To use the new `leasttime` load-balancer algorithm with the Kubernetes CRD provider, you need to update your CRDs. + +**Apply Updated CRDs:** + +```shell +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +``` + +## v3.6.2 + +### Ingress NGINX Provider + +The KubernetesIngressNGINX Provider is no longer experimental in v3.6.2 and can be enabled without the `experimental.kubernetesIngressNGINX` option. + +**Deprecated Configuration:** + +??? example "Experimental kubernetesIngressNGINX option (deprecated)" + + ```yaml tab="File (YAML)" + experimental: + kubernetesIngressNGINX: true + ``` + + ```toml tab="File (TOML)" + [experimental] + kubernetesIngressNGINX=true + ``` + + ```bash tab="CLI" + --experimental.kubernetesIngressNGINX=true + ``` + +**Migration Steps:** + +1. Remove the `kubernetesIngressNGINX` option from the experimental section +2. Configure the provider using the [kubernetesIngressNGINX Provider documentation](../reference/install-configuration/providers/kubernetes/kubernetes-ingress-nginx.md) + +## v3.6.4 + +### Encoded Characters in Request Path + +Starting with `v3.6.4`, for security reasons, Traefik now rejects requests with a path containing a specific set of encoded characters by default. + +When such a request is received, Traefik responds with a `400 Bad Request` status code. + +Here is the list of the encoded characters that are rejected by default, along with the corresponding configuration option to allow them: + +| Encoded Character | Character | Config option to allow the encoded character | +|-------------------|-------------------------|--------------------------------------------------------------------------------------| +| `%2f` or `%2F` | `/` (slash) | `entryPoints.`
`.http.encodedCharacters`
`.allowEncodedSlash` | +| `%5c` or `%5C` | `\` (backslash) | `entryPoints..`
`.http.encodedCharacters`
`.allowEncodedBackSlash` | +| `%00` | `NULL` (null character) | `entryPoints..`
`.http.encodedCharacters`
`.allowEncodedNullCharacter` | +| `%3b` or `%3B` | `;` (semicolon) | `entryPoints..`
`.http.encodedCharacters`
`.allowEncodedSemicolon` | +| `%25` | `%` (percent) | `entryPoints..`
`.http.encodedCharacters`
`.allowEncodedPercent` | +| `%3f` or `%3F` | `?` (question mark) | `entryPoints..`
`.http.encodedCharacters`
`.allowEncodedQuestionMark` | +| `%23` | `#` (hash) | `entryPoints..`
`.http.encodedCharacters`
`.allowEncodedHash` | + +Please check out the entrypoint [encodedCharacters option](../reference/install-configuration/entrypoints.md#opt-http-encodedCharacters) documentation for more details. + +## v3.6.7 + +### Encoded Characters Configuration Default Values + +Since `v3.6.7`, the options for encoded characters now have a `true` default value. +This means that Traefik will not reject requests with a path containing a specific set of encoded characters by default. +It is now up to the users to configure the security hardening of encoded characters. + +Here is the list of the encoded characters that can be configured to `false` to disallow them: + +| Encoded Character | Character | Config options | Default value | +|-------------------|-------------------------|--------------------------------------------------------------------------------------|---------------| +| `%2f` or `%2F` | `/` (slash) | `entryPoints.`
`.http.encodedCharacters`
`.allowEncodedSlash` | `true` | +| `%5c` or `%5C` | `\` (backslash) | `entryPoints..`
`.http.encodedCharacters`
`.allowEncodedBackSlash` | `true` | +| `%00` | `NULL` (null character) | `entryPoints..`
`.http.encodedCharacters`
`.allowEncodedNullCharacter` | `true` | +| `%3b` or `%3B` | `;` (semicolon) | `entryPoints..`
`.http.encodedCharacters`
`.allowEncodedSemicolon` | `true` | +| `%25` | `%` (percent) | `entryPoints..`
`.http.encodedCharacters`
`.allowEncodedPercent` | `true` | +| `%3f` or `%3F` | `?` (question mark) | `entryPoints..`
`.http.encodedCharacters`
`.allowEncodedQuestionMark` | `true` | +| `%23` | `#` (hash) | `entryPoints..`
`.http.encodedCharacters`
`.allowEncodedHash` | `true` | + +Note: This check is not done against query parameters, +but only against the request path as defined +in [RFC3986 section-3](https://datatracker.ietf.org/doc/html/rfc3986#section-3). + +Please check out the entrypoint [encodedCharacters option](../routing/entrypoints.md#encoded-characters) documentation +for more details. diff --git a/docs/content/observability/access-logs.md b/docs/content/observability/access-logs.md index 3091df45e..4b5ce0b36 100644 --- a/docs/content/observability/access-logs.md +++ b/docs/content/observability/access-logs.md @@ -306,7 +306,7 @@ Example utilizing Docker Compose: ```yaml services: traefik: - image: traefik:v3.5 + image: traefik:v3.6 environment: - TZ=US/Alaska command: @@ -780,4 +780,4 @@ accesslog: --accesslog.otlp.grpc.tls.insecureSkipVerify=true ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/observability/logs.md b/docs/content/observability/logs.md index 8f82e9936..8c39edc04 100644 --- a/docs/content/observability/logs.md +++ b/docs/content/observability/logs.md @@ -644,4 +644,4 @@ log: --log.otlp.grpc.tls.insecureSkipVerify=true ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/observe/overview.md b/docs/content/observe/overview.md index e5e822221..c7dc2279a 100644 --- a/docs/content/observe/overview.md +++ b/docs/content/observe/overview.md @@ -77,4 +77,4 @@ additionalArguments: !!! note A router with its own observability configuration will override the global default. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/operations/api.md b/docs/content/operations/api.md index 8e867bdad..f4fcb03e2 100644 --- a/docs/content/operations/api.md +++ b/docs/content/operations/api.md @@ -176,4 +176,4 @@ All the following endpoints must be accessed with a `GET` HTTP request. | `/debug/pprof/symbol` | See the [pprof Symbol](https://golang.org/pkg/net/http/pprof/#Symbol) Go documentation. | | `/debug/pprof/trace` | See the [pprof Trace](https://golang.org/pkg/net/http/pprof/#Trace) Go documentation. | -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/operations/dashboard.md b/docs/content/operations/dashboard.md index c3776a369..6b086a272 100644 --- a/docs/content/operations/dashboard.md +++ b/docs/content/operations/dashboard.md @@ -168,4 +168,4 @@ api: --api.dashboard=false ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/plugins/index.md b/docs/content/plugins/index.md index 9cd3662f8..b3172a9a8 100644 --- a/docs/content/plugins/index.md +++ b/docs/content/plugins/index.md @@ -31,4 +31,4 @@ The experience of implementing a Traefik plugin is comparable to writing a web b To learn more about Traefik plugin creation, please refer to the [developer documentation](https://plugins.traefik.io/create). -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/providers/docker.md b/docs/content/providers/docker.md index 75840998d..24136bd67 100644 --- a/docs/content/providers/docker.md +++ b/docs/content/providers/docker.md @@ -163,7 +163,7 @@ See the [Docker API Access](#docker-api-access) section for more information. ```yaml services: traefik: - image: traefik:v3.5 # The official v3 Traefik docker image + image: traefik:v3.6 # The official v3 Traefik docker image ports: - "80:80" volumes: @@ -702,4 +702,4 @@ providers: --providers.docker.allowEmptyServices=true ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/providers/file.md b/docs/content/providers/file.md index c1f328e32..1fbab2b99 100644 --- a/docs/content/providers/file.md +++ b/docs/content/providers/file.md @@ -292,4 +292,4 @@ To illustrate, it is possible to easily define multiple routers, services, and T {{ end }} ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/providers/kubernetes-crd.md b/docs/content/providers/kubernetes-crd.md index f00b5c68e..18f56f039 100644 --- a/docs/content/providers/kubernetes-crd.md +++ b/docs/content/providers/kubernetes-crd.md @@ -16,7 +16,7 @@ the Traefik engineering team developed a [Custom Resource Definition](https://ku ## Requirements -{!kubernetes-requirements.md!} +{% include-markdown "includes/kubernetes-requirements.md" %} !!! tip "All Steps for a Successful Deployment" @@ -31,10 +31,10 @@ the Traefik engineering team developed a [Custom Resource Definition](https://ku ```bash # Install Traefik Resource Definitions: - kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml + kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml # Install RBAC for Traefik: - kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml + kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml ``` ## Resource Configuration @@ -365,4 +365,4 @@ providers: For additional information, refer to the [full example](../user-guides/crd-acme/index.md) with Let's Encrypt. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/providers/kubernetes-gateway.md b/docs/content/providers/kubernetes-gateway.md index 22158dfb0..7842bb721 100644 --- a/docs/content/providers/kubernetes-gateway.md +++ b/docs/content/providers/kubernetes-gateway.md @@ -8,33 +8,33 @@ description: "Learn how to use the Kubernetes Gateway API as a provider for conf The Kubernetes Gateway provider is a Traefik implementation of the [Gateway API](https://gateway-api.sigs.k8s.io/) specification from the Kubernetes Special Interest Groups (SIGs). -This provider supports Standard version [v1.3.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.3.0) of the Gateway API specification. +This provider supports Standard version [v1.4.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.4.0) of the Gateway API specification. It fully supports all HTTP core and some extended features, as well as the `TCPRoute` and `TLSRoute` resources from the [Experimental channel](https://gateway-api.sigs.k8s.io/concepts/versioning/?h=#release-channels). -For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.3.0/traefik-traefik). +For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.4.0/traefik-traefik). ## Requirements -{!kubernetes-requirements.md!} +{% include-markdown "includes/kubernetes-requirements.md" %} !!! info "Helm Chart" When using the Traefik [Helm Chart](../getting-started/install-traefik.md#use-the-helm-chart), the CRDs (Custom Resource Definitions) and RBAC (Role-Based Access Control) are automatically managed for you. - The only remaining task is to enable the `kubernetesGateway` in the chart [values](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml#L130). + The only remaining task is to enable the `kubernetesGateway` in the chart [values](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml#L323). 1. Install/update the Kubernetes Gateway API CRDs. ```bash # Install Gateway API CRDs from the Standard channel. - kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/standard-install.yaml ``` 2. Install the additional Traefik RBAC required for Gateway API. ```bash # Install Traefik RBACs. - kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml + kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml ``` 3. Deploy Traefik and enable the `kubernetesGateway` provider in the static configuration as detailed below: @@ -275,7 +275,7 @@ providers: ```bash # Install Gateway API CRDs from the Experimental channel. - kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/experimental-install.yaml + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/experimental-install.yaml ``` ### `labelselector` @@ -357,4 +357,4 @@ providers: --providers.kubernetesgateway.throttleDuration=10s ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/providers/kubernetes-ingress.md b/docs/content/providers/kubernetes-ingress.md index ce9276480..bf148867b 100644 --- a/docs/content/providers/kubernetes-ingress.md +++ b/docs/content/providers/kubernetes-ingress.md @@ -13,7 +13,7 @@ it manages access to cluster services by supporting the [Ingress](https://kubern ## Requirements -{!kubernetes-requirements.md!} +{% include-markdown "includes/kubernetes-requirements.md" %} ## Routing Configuration @@ -555,6 +555,6 @@ providers: ### Further To learn more about the various aspects of the Ingress specification that Traefik supports, -many examples of Ingresses definitions are located in the test [examples](https://github.com/traefik/traefik/tree/v3.5/pkg/provider/kubernetes/ingress/fixtures) of the Traefik repository. +many examples of Ingresses definitions are located in the test [examples](https://github.com/traefik/traefik/tree/v3.6/pkg/provider/kubernetes/ingress/fixtures) of the Traefik repository. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/providers/overview.md b/docs/content/providers/overview.md index 7ee8ad75c..8723f58a7 100644 --- a/docs/content/providers/overview.md +++ b/docs/content/providers/overview.md @@ -227,4 +227,4 @@ List of providers that support constraints: - [Kubernetes Ingress](./kubernetes-ingress.md#labelselector) - [Kubernetes Gateway](./kubernetes-gateway.md#labelselector) -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/providers/swarm.md b/docs/content/providers/swarm.md index e82e1d518..26a2c7e97 100644 --- a/docs/content/providers/swarm.md +++ b/docs/content/providers/swarm.md @@ -769,4 +769,4 @@ providers: --providers.swarm.allowEmptyServices=true ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/dynamic-configuration/docker-labels.yml b/docs/content/reference/dynamic-configuration/docker-labels.yml index 3399c2762..b2881a594 100644 --- a/docs/content/reference/dynamic-configuration/docker-labels.yml +++ b/docs/content/reference/dynamic-configuration/docker-labels.yml @@ -199,37 +199,39 @@ - "traefik.http.routers.router1.tls.domains[1].main=foobar" - "traefik.http.routers.router1.tls.domains[1].sans=foobar, foobar" - "traefik.http.routers.router1.tls.options=foobar" -- "traefik.http.services.service02.loadbalancer.healthcheck.followredirects=true" -- "traefik.http.services.service02.loadbalancer.healthcheck.headers.name0=foobar" -- "traefik.http.services.service02.loadbalancer.healthcheck.headers.name1=foobar" -- "traefik.http.services.service02.loadbalancer.healthcheck.hostname=foobar" -- "traefik.http.services.service02.loadbalancer.healthcheck.interval=42s" -- "traefik.http.services.service02.loadbalancer.healthcheck.method=foobar" -- "traefik.http.services.service02.loadbalancer.healthcheck.mode=foobar" -- "traefik.http.services.service02.loadbalancer.healthcheck.path=foobar" -- "traefik.http.services.service02.loadbalancer.healthcheck.port=42" -- "traefik.http.services.service02.loadbalancer.healthcheck.scheme=foobar" -- "traefik.http.services.service02.loadbalancer.healthcheck.status=42" -- "traefik.http.services.service02.loadbalancer.healthcheck.timeout=42s" -- "traefik.http.services.service02.loadbalancer.healthcheck.unhealthyinterval=42s" -- "traefik.http.services.service02.loadbalancer.passhostheader=true" -- "traefik.http.services.service02.loadbalancer.responseforwarding.flushinterval=42s" -- "traefik.http.services.service02.loadbalancer.serverstransport=foobar" -- "traefik.http.services.service02.loadbalancer.sticky=true" -- "traefik.http.services.service02.loadbalancer.sticky.cookie=true" -- "traefik.http.services.service02.loadbalancer.sticky.cookie.domain=foobar" -- "traefik.http.services.service02.loadbalancer.sticky.cookie.httponly=true" -- "traefik.http.services.service02.loadbalancer.sticky.cookie.maxage=42" -- "traefik.http.services.service02.loadbalancer.sticky.cookie.name=foobar" -- "traefik.http.services.service02.loadbalancer.sticky.cookie.path=foobar" -- "traefik.http.services.service02.loadbalancer.sticky.cookie.samesite=foobar" -- "traefik.http.services.service02.loadbalancer.sticky.cookie.secure=true" -- "traefik.http.services.service02.loadbalancer.strategy=foobar" -- "traefik.http.services.service02.loadbalancer.server.port=foobar" -- "traefik.http.services.service02.loadbalancer.server.preservepath=true" -- "traefik.http.services.service02.loadbalancer.server.scheme=foobar" -- "traefik.http.services.service02.loadbalancer.server.url=foobar" -- "traefik.http.services.service02.loadbalancer.server.weight=42" +- "traefik.http.services.service03.loadbalancer.healthcheck.followredirects=true" +- "traefik.http.services.service03.loadbalancer.healthcheck.headers.name0=foobar" +- "traefik.http.services.service03.loadbalancer.healthcheck.headers.name1=foobar" +- "traefik.http.services.service03.loadbalancer.healthcheck.hostname=foobar" +- "traefik.http.services.service03.loadbalancer.healthcheck.interval=42s" +- "traefik.http.services.service03.loadbalancer.healthcheck.method=foobar" +- "traefik.http.services.service03.loadbalancer.healthcheck.mode=foobar" +- "traefik.http.services.service03.loadbalancer.healthcheck.path=foobar" +- "traefik.http.services.service03.loadbalancer.healthcheck.port=42" +- "traefik.http.services.service03.loadbalancer.healthcheck.scheme=foobar" +- "traefik.http.services.service03.loadbalancer.healthcheck.status=42" +- "traefik.http.services.service03.loadbalancer.healthcheck.timeout=42s" +- "traefik.http.services.service03.loadbalancer.healthcheck.unhealthyinterval=42s" +- "traefik.http.services.service03.loadbalancer.passhostheader=true" +- "traefik.http.services.service03.loadbalancer.passivehealthcheck.failurewindow=42s" +- "traefik.http.services.service03.loadbalancer.passivehealthcheck.maxfailedattempts=42" +- "traefik.http.services.service03.loadbalancer.responseforwarding.flushinterval=42s" +- "traefik.http.services.service03.loadbalancer.serverstransport=foobar" +- "traefik.http.services.service03.loadbalancer.sticky=true" +- "traefik.http.services.service03.loadbalancer.sticky.cookie=true" +- "traefik.http.services.service03.loadbalancer.sticky.cookie.domain=foobar" +- "traefik.http.services.service03.loadbalancer.sticky.cookie.httponly=true" +- "traefik.http.services.service03.loadbalancer.sticky.cookie.maxage=42" +- "traefik.http.services.service03.loadbalancer.sticky.cookie.name=foobar" +- "traefik.http.services.service03.loadbalancer.sticky.cookie.path=foobar" +- "traefik.http.services.service03.loadbalancer.sticky.cookie.samesite=foobar" +- "traefik.http.services.service03.loadbalancer.sticky.cookie.secure=true" +- "traefik.http.services.service03.loadbalancer.strategy=foobar" +- "traefik.http.services.service03.loadbalancer.server.port=foobar" +- "traefik.http.services.service03.loadbalancer.server.preservepath=true" +- "traefik.http.services.service03.loadbalancer.server.scheme=foobar" +- "traefik.http.services.service03.loadbalancer.server.url=foobar" +- "traefik.http.services.service03.loadbalancer.server.weight=42" - "traefik.tcp.middlewares.tcpmiddleware01.ipallowlist.sourcerange=foobar, foobar" - "traefik.tcp.middlewares.tcpmiddleware02.ipwhitelist.sourcerange=foobar, foobar" - "traefik.tcp.middlewares.tcpmiddleware03.inflightconn.amount=42" diff --git a/docs/content/reference/dynamic-configuration/file.toml b/docs/content/reference/dynamic-configuration/file.toml index 985f96e21..e0e2139e5 100644 --- a/docs/content/reference/dynamic-configuration/file.toml +++ b/docs/content/reference/dynamic-configuration/file.toml @@ -55,12 +55,23 @@ fallback = "foobar" [http.services.Service01.failover.healthCheck] [http.services.Service02] - [http.services.Service02.loadBalancer] + [http.services.Service02.highestRandomWeight] + + [[http.services.Service02.highestRandomWeight.services]] + name = "foobar" + weight = 42 + + [[http.services.Service02.highestRandomWeight.services]] + name = "foobar" + weight = 42 + [http.services.Service02.highestRandomWeight.healthCheck] + [http.services.Service03] + [http.services.Service03.loadBalancer] strategy = "foobar" passHostHeader = true serversTransport = "foobar" - [http.services.Service02.loadBalancer.sticky] - [http.services.Service02.loadBalancer.sticky.cookie] + [http.services.Service03.loadBalancer.sticky] + [http.services.Service03.loadBalancer.sticky.cookie] name = "foobar" secure = true httpOnly = true @@ -69,16 +80,16 @@ path = "foobar" domain = "foobar" - [[http.services.Service02.loadBalancer.servers]] + [[http.services.Service03.loadBalancer.servers]] url = "foobar" weight = 42 preservePath = true - [[http.services.Service02.loadBalancer.servers]] + [[http.services.Service03.loadBalancer.servers]] url = "foobar" weight = 42 preservePath = true - [http.services.Service02.loadBalancer.healthCheck] + [http.services.Service03.loadBalancer.healthCheck] scheme = "foobar" mode = "foobar" path = "foobar" @@ -90,37 +101,40 @@ timeout = "42s" hostname = "foobar" followRedirects = true - [http.services.Service02.loadBalancer.healthCheck.headers] + [http.services.Service03.loadBalancer.healthCheck.headers] name0 = "foobar" name1 = "foobar" - [http.services.Service02.loadBalancer.responseForwarding] + [http.services.Service03.loadBalancer.passiveHealthCheck] + failureWindow = "42s" + maxFailedAttempts = 42 + [http.services.Service03.loadBalancer.responseForwarding] flushInterval = "42s" - [http.services.Service03] - [http.services.Service03.mirroring] + [http.services.Service04] + [http.services.Service04.mirroring] service = "foobar" mirrorBody = true maxBodySize = 42 - [[http.services.Service03.mirroring.mirrors]] + [[http.services.Service04.mirroring.mirrors]] name = "foobar" percent = 42 - [[http.services.Service03.mirroring.mirrors]] + [[http.services.Service04.mirroring.mirrors]] name = "foobar" percent = 42 - [http.services.Service03.mirroring.healthCheck] - [http.services.Service04] - [http.services.Service04.weighted] + [http.services.Service04.mirroring.healthCheck] + [http.services.Service05] + [http.services.Service05.weighted] - [[http.services.Service04.weighted.services]] + [[http.services.Service05.weighted.services]] name = "foobar" weight = 42 - [[http.services.Service04.weighted.services]] + [[http.services.Service05.weighted.services]] name = "foobar" weight = 42 - [http.services.Service04.weighted.sticky] - [http.services.Service04.weighted.sticky.cookie] + [http.services.Service05.weighted.sticky] + [http.services.Service05.weighted.sticky.cookie] name = "foobar" secure = true httpOnly = true @@ -128,7 +142,7 @@ maxAge = 42 path = "foobar" domain = "foobar" - [http.services.Service04.weighted.healthCheck] + [http.services.Service05.weighted.healthCheck] [http.middlewares] [http.middlewares.Middleware01] [http.middlewares.Middleware01.addPrefix] diff --git a/docs/content/reference/dynamic-configuration/file.yaml b/docs/content/reference/dynamic-configuration/file.yaml index 29822fddb..e2ab16e54 100644 --- a/docs/content/reference/dynamic-configuration/file.yaml +++ b/docs/content/reference/dynamic-configuration/file.yaml @@ -65,6 +65,14 @@ http: fallback: foobar healthCheck: {} Service02: + highestRandomWeight: + services: + - name: foobar + weight: 42 + - name: foobar + weight: 42 + healthCheck: {} + Service03: loadBalancer: sticky: cookie: @@ -98,11 +106,14 @@ http: headers: name0: foobar name1: foobar + passiveHealthCheck: + failureWindow: 42s + maxFailedAttempts: 42 passHostHeader: true responseForwarding: flushInterval: 42s serversTransport: foobar - Service03: + Service04: mirroring: service: foobar mirrorBody: true @@ -113,7 +124,7 @@ http: - name: foobar percent: 42 healthCheck: {} - Service04: + Service05: weighted: services: - name: foobar diff --git a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml index ea2618637..763d981eb 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml @@ -1,9 +1,8 @@ ---- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: ingressroutes.traefik.io spec: group: traefik.io @@ -43,11 +42,31 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/entrypoints/ Default: all. items: type: string type: array + parentRefs: + description: |- + ParentRefs defines references to parent IngressRoute resources for multi-layer routing. + When set, this IngressRoute's routers will be children of the referenced parent IngressRoute's routers. + More info: https://doc.traefik.io/traefik/v3.6/routing/routers/#parentrefs + items: + description: IngressRouteRef is a reference to an IngressRoute resource. + properties: + name: + description: Name defines the name of the referenced IngressRoute + resource. + type: string + namespace: + description: Namespace defines the namespace of the referenced + IngressRoute resource. + type: string + required: + - name + type: object + type: array routes: description: Routes defines the list of routes. items: @@ -64,12 +83,12 @@ spec: match: description: |- Match defines the router's rule. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/ type: string middlewares: description: |- Middlewares defines the list of references to Middleware resources. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/middleware/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/middleware/ items: description: MiddlewareRef is a reference to a Middleware resource. @@ -89,7 +108,7 @@ spec: observability: description: |- Observability defines the observability configuration for a router. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/observability/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/observability/ properties: accessLogs: description: AccessLogs enables access logs for this router. @@ -112,7 +131,7 @@ spec: priority: description: |- Priority defines the router's priority. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/#priority + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/#priority maximum: 9223372036854775000 type: integer services: @@ -227,6 +246,25 @@ spec: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, passHostHeader is true. type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health + checks for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window + during which the failed attempts must occur for + the server to be marked as unhealthy. It also defines + for how long the server will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window + before marking the server as unhealthy. + type: integer + type: object port: anyOf: - type: integer @@ -263,7 +301,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -312,11 +350,13 @@ spec: strategy: description: |- Strategy defines the load balancing strategy between the servers. - Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). RoundRobin value is deprecated and supported for backward compatibility. enum: - wrr - p2c + - hrw + - leasttime - RoundRobin type: string weight: @@ -332,7 +372,7 @@ spec: syntax: description: |- Syntax defines the router's rule syntax. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. type: string required: @@ -342,18 +382,18 @@ spec: tls: description: |- TLS defines the TLS configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/router/#tls + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/router/#tls properties: certResolver: description: |- CertResolver defines the name of the certificate resolver to use. Cert resolvers have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/tls/certificate-resolvers/acme/ + More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/tls/certificate-resolvers/acme/ type: string domains: description: |- Domains defines the list of domains that will be used to issue certificates. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#domains + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#domains items: description: Domain holds a domain name with SANs. properties: @@ -372,17 +412,17 @@ spec: description: |- Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. If not defined, the `default` TLSOption is used. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-options/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-options/ properties: name: description: |- Name defines the name of the referenced TLSOption. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsoption/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/tlsoption/ type: string namespace: description: |- Namespace defines the namespace of the referenced TLSOption. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsoption/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/tlsoption/ type: string required: - name @@ -399,12 +439,12 @@ spec: name: description: |- Name defines the name of the referenced TLSStore. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsstore/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/tlsstore/ type: string namespace: description: |- Namespace defines the namespace of the referenced TLSStore. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsstore/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/tlsstore/ type: string required: - name @@ -424,7 +464,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: ingressroutetcps.traefik.io spec: group: traefik.io @@ -464,7 +504,7 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/entrypoints/ Default: all. items: type: string @@ -477,7 +517,7 @@ spec: match: description: |- Match defines the router's rule. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/ type: string middlewares: description: Middlewares defines the list of references to MiddlewareTCP @@ -501,7 +541,7 @@ spec: priority: description: |- Priority defines the router's priority. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/#priority + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/#priority maximum: 9223372036854775000 type: integer services: @@ -543,7 +583,7 @@ spec: proxyProtocol: description: |- ProxyProtocol defines the PROXY protocol configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/service/#proxy-protocol + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/service/#proxy-protocol Deprecated: ProxyProtocol will not be supported in future APIVersions, please use ServersTransport to configure ProxyProtocol instead. properties: version: @@ -585,7 +625,7 @@ spec: syntax: description: |- Syntax defines the router's rule syntax. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. enum: - v3 @@ -598,18 +638,18 @@ spec: tls: description: |- TLS defines the TLS configuration on a layer 4 / TCP Route. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/router/#tls + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/router/#tls properties: certResolver: description: |- CertResolver defines the name of the certificate resolver to use. Cert resolvers have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/tls/certificate-resolvers/acme/ + More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/tls/certificate-resolvers/acme/ type: string domains: description: |- Domains defines the list of domains that will be used to issue certificates. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/#domains + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/tls/#domains items: description: Domain holds a domain name with SANs. properties: @@ -628,7 +668,7 @@ spec: description: |- Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. If not defined, the `default` TLSOption is used. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/#tls-options + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/tls/#tls-options properties: name: description: Name defines the name of the referenced Traefik @@ -680,7 +720,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: ingressrouteudps.traefik.io spec: group: traefik.io @@ -720,7 +760,7 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/entrypoints/ Default: all. items: type: string @@ -792,7 +832,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: middlewares.traefik.io spec: group: traefik.io @@ -808,7 +848,7 @@ spec: openAPIV3Schema: description: |- Middleware is the CRD implementation of a Traefik Middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/overview/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/overview/ properties: apiVersion: description: |- @@ -834,7 +874,7 @@ spec: description: |- AddPrefix holds the add prefix middleware configuration. This middleware updates the path of a request before forwarding it. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/addprefix/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/addprefix/ properties: prefix: description: |- @@ -849,12 +889,12 @@ spec: description: |- BasicAuth holds the basic auth middleware configuration. This middleware restricts access to your services to known users. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/basicauth/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/basicauth/ properties: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/basicauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/basicauth/#headerfield type: string realm: description: |- @@ -875,7 +915,7 @@ spec: description: |- Buffering holds the buffering middleware configuration. This middleware retries or limits the size of requests that can be forwarded to backends. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/buffering/#maxrequestbodybytes + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/buffering/#maxrequestbodybytes properties: maxRequestBodyBytes: description: |- @@ -907,14 +947,14 @@ spec: description: |- RetryExpression defines the retry conditions. It is a logical combination of functions with operators AND (&&) and OR (||). - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/buffering/#retryexpression + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/buffering/#retryexpression type: string type: object chain: description: |- Chain holds the configuration of the chain middleware. This middleware enables to define reusable combinations of other pieces of middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/chain/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/chain/ properties: middlewares: description: Middlewares is the list of MiddlewareRef which composes @@ -977,7 +1017,7 @@ spec: description: |- Compress holds the compress middleware configuration. This middleware compresses responses before sending them to the client, using gzip, brotli, or zstd compression. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/compress/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/compress/ properties: defaultEncoding: description: DefaultEncoding specifies the default encoding if @@ -1027,12 +1067,12 @@ spec: description: |- DigestAuth holds the digest auth middleware configuration. This middleware restricts access to your services to known users. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/digestauth/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/digestauth/ properties: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/digestauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/digestauth/#headerfield type: string realm: description: |- @@ -1052,7 +1092,7 @@ spec: description: |- ErrorPage holds the custom error middleware configuration. This middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/errorpages/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/errorpages/ properties: query: description: |- @@ -1064,7 +1104,7 @@ spec: service: description: |- Service defines the reference to a Kubernetes Service that will serve the error page. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/errorpages/#service + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/errorpages/#service properties: healthCheck: description: Healthcheck defines health checks for ExternalName @@ -1170,6 +1210,25 @@ spec: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, passHostHeader is true. type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health checks + for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window during + which the failed attempts must occur for the server + to be marked as unhealthy. It also defines for how long + the server will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window before + marking the server as unhealthy. + type: integer + type: object port: anyOf: - type: integer @@ -1206,7 +1265,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -1254,11 +1313,13 @@ spec: strategy: description: |- Strategy defines the load balancing strategy between the servers. - Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). RoundRobin value is deprecated and supported for backward compatibility. enum: - wrr - p2c + - hrw + - leasttime - RoundRobin type: string weight: @@ -1293,7 +1354,7 @@ spec: description: |- ForwardAuth holds the forward auth middleware configuration. This middleware delegates the request authentication to a Service. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/forwardauth/ properties: addAuthCookiesToResponse: description: AddAuthCookiesToResponse defines the list of cookies @@ -1321,7 +1382,7 @@ spec: authResponseHeadersRegex: description: |- AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/#authresponseheadersregex + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/forwardauth/#authresponseheadersregex type: string forwardBody: description: ForwardBody defines whether to send the request body @@ -1330,7 +1391,7 @@ spec: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/forwardauth/#headerfield type: string maxBodySize: description: MaxBodySize defines the maximum body size in bytes @@ -1392,7 +1453,7 @@ spec: description: |- Headers holds the headers middleware configuration. This middleware manages the requests and responses headers. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/headers/#customrequestheaders + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/headers/#customrequestheaders properties: accessControlAllowCredentials: description: AccessControlAllowCredentials defines whether the @@ -1564,7 +1625,7 @@ spec: description: |- InFlightReq holds the in-flight request middleware configuration. This middleware limits the number of requests being processed and served concurrently. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/inflightreq/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/inflightreq/ properties: amount: description: |- @@ -1578,12 +1639,12 @@ spec: SourceCriterion defines what criterion is used to group requests as originating from a common source. If several strategies are defined at the same time, an error will be raised. If none are set, the default is to use the requestHost. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/inflightreq/#sourcecriterion + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/inflightreq/#sourcecriterion properties: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1619,12 +1680,12 @@ spec: description: |- IPAllowList holds the IP allowlist middleware configuration. This middleware limits allowed requests based on the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipallowlist/ properties: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1662,7 +1723,7 @@ spec: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1693,7 +1754,7 @@ spec: description: |- PassTLSClientCert holds the pass TLS client cert middleware configuration. This middleware adds the selected data from the passed client TLS certificate to a header. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/passtlsclientcert/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/passtlsclientcert/ properties: info: description: Info selects the specific client certificate details @@ -1796,13 +1857,13 @@ spec: x-kubernetes-preserve-unknown-fields: true description: |- Plugin defines the middleware plugin configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/overview/#community-middlewares + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/overview/#community-middlewares type: object rateLimit: description: |- RateLimit holds the rate limit configuration. This middleware ensures that services will receive a fair amount of requests, and allows one to define what fair is. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/ratelimit/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/ratelimit/ properties: average: description: |- @@ -1921,7 +1982,7 @@ spec: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1957,7 +2018,7 @@ spec: description: |- RedirectRegex holds the redirect regex middleware configuration. This middleware redirects a request using regex matching and replacement. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/redirectregex/#regex + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/redirectregex/#regex properties: permanent: description: Permanent defines whether the redirection is permanent @@ -1976,11 +2037,12 @@ spec: description: |- RedirectScheme holds the redirect scheme middleware configuration. This middleware redirects requests from a scheme/port to another. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/redirectscheme/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/redirectscheme/ properties: permanent: - description: Permanent defines whether the redirection is permanent - (308). + description: |- + Permanent defines whether the redirection is permanent. + For HTTP GET requests a 301 is returned, otherwise a 308 is returned. type: boolean port: description: Port defines the port of the new URL. @@ -1993,7 +2055,7 @@ spec: description: |- ReplacePath holds the replace path middleware configuration. This middleware replaces the path of the request URL and store the original path in an X-Replaced-Path header. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/replacepath/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/replacepath/ properties: path: description: Path defines the path to use as replacement in the @@ -2004,7 +2066,7 @@ spec: description: |- ReplacePathRegex holds the replace path regex middleware configuration. This middleware replaces the path of a URL using regex matching and replacement. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/replacepathregex/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/replacepathregex/ properties: regex: description: Regex defines the regular expression used to match @@ -2020,7 +2082,7 @@ spec: Retry holds the retry middleware configuration. This middleware reissues requests a given number of times to a backend server if that server does not reply. As soon as the server answers, the middleware stops retrying, regardless of the response status. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/retry/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/retry/ properties: attempts: description: Attempts defines how many times the request should @@ -2044,7 +2106,7 @@ spec: description: |- StripPrefix holds the strip prefix middleware configuration. This middleware removes the specified prefixes from the URL path. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/stripprefix/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/stripprefix/ properties: forceSlash: description: |- @@ -2063,7 +2125,7 @@ spec: description: |- StripPrefixRegex holds the strip prefix regex middleware configuration. This middleware removes the matching prefixes from the URL path. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/stripprefixregex/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/stripprefixregex/ properties: regex: description: Regex defines the regular expression to match the @@ -2084,7 +2146,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: middlewaretcps.traefik.io spec: group: traefik.io @@ -2100,7 +2162,7 @@ spec: openAPIV3Schema: description: |- MiddlewareTCP is the CRD implementation of a Traefik TCP middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/overview/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/overview/ properties: apiVersion: description: |- @@ -2137,7 +2199,7 @@ spec: description: |- IPAllowList defines the IPAllowList middleware configuration. This middleware accepts/refuses connections based on the client IP. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/ipallowlist/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/ipallowlist/ properties: sourceRange: description: SourceRange defines the allowed IPs (or ranges of @@ -2151,7 +2213,7 @@ spec: IPWhiteList defines the IPWhiteList middleware configuration. This middleware accepts/refuses connections based on the client IP. Deprecated: please use IPAllowList instead. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/ipwhitelist/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/ipwhitelist/ properties: sourceRange: description: SourceRange defines the allowed IPs (or ranges of @@ -2172,7 +2234,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: serverstransports.traefik.io spec: group: traefik.io @@ -2190,7 +2252,7 @@ spec: ServersTransport is the CRD implementation of a ServersTransport. If no serversTransport is specified, the default@internal will be used. The default@internal serversTransport is created from the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/serverstransport/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/serverstransport/ properties: apiVersion: description: |- @@ -2341,7 +2403,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: serverstransporttcps.traefik.io spec: group: traefik.io @@ -2359,7 +2421,7 @@ spec: ServersTransportTCP is the CRD implementation of a TCPServersTransport. If no tcpServersTransport is specified, a default one named default@internal will be used. The default@internal tcpServersTransport can be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/serverstransport/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/serverstransport/ properties: apiVersion: description: |- @@ -2497,7 +2559,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: tlsoptions.traefik.io spec: group: traefik.io @@ -2513,7 +2575,7 @@ spec: openAPIV3Schema: description: |- TLSOption is the CRD implementation of a Traefik TLS Option, allowing to configure some parameters of the TLS connection. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#tls-options + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#tls-options properties: apiVersion: description: |- @@ -2538,14 +2600,14 @@ spec: alpnProtocols: description: |- ALPNProtocols defines the list of supported application level protocols for the TLS handshake, in order of preference. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#alpn-protocols + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#alpn-protocols items: type: string type: array cipherSuites: description: |- CipherSuites defines the list of supported cipher suites for TLS versions up to TLS 1.2. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#cipher-suites + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#cipher-suites items: type: string type: array @@ -2573,7 +2635,7 @@ spec: curvePreferences: description: |- CurvePreferences defines the preferred elliptic curves. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#curve-preferences + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#curve-preferences items: type: string type: array @@ -2615,7 +2677,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: tlsstores.traefik.io spec: group: traefik.io @@ -2633,7 +2695,7 @@ spec: TLSStore is the CRD implementation of a Traefik TLS Store. For the time being, only the TLSStore named default is supported. This means that you cannot have two stores that are named default in different Kubernetes namespaces. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#certificates-stores + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#certificates-stores properties: apiVersion: description: |- @@ -2712,7 +2774,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: traefikservices.traefik.io spec: group: traefik.io @@ -2731,7 +2793,7 @@ spec: TraefikService object allows to: - Apply weight to Services on load-balancing - Mirror traffic on services - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/traefikservice/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/traefikservice/ properties: apiVersion: description: |- @@ -2753,6 +2815,244 @@ spec: spec: description: TraefikServiceSpec defines the desired state of a TraefikService. properties: + highestRandomWeight: + description: HighestRandomWeight defines the highest random weight + service configuration. + properties: + services: + description: Services defines the list of Kubernetes Service and/or + TraefikService to load-balance, with weight. + items: + description: Service defines an upstream HTTP service to proxy + traffic to. + properties: + healthCheck: + description: Healthcheck defines health checks for ExternalName + services. + properties: + followRedirects: + description: |- + FollowRedirects defines whether redirects should be followed during the health check calls. + Default: true + type: boolean + headers: + additionalProperties: + type: string + description: Headers defines custom headers to be sent + to the health check endpoint. + type: object + hostname: + description: Hostname defines the value of hostname + in the Host header of the health check request. + type: string + interval: + anyOf: + - type: integer + - type: string + description: |- + Interval defines the frequency of the health check calls for healthy targets. + Default: 30s + x-kubernetes-int-or-string: true + method: + description: Method defines the healthcheck method. + type: string + mode: + description: |- + Mode defines the health check mode. + If defined to grpc, will use the gRPC health check protocol to probe the server. + Default: http + type: string + path: + description: Path defines the server URL path for the + health check endpoint. + type: string + port: + description: Port defines the server URL port for the + health check endpoint. + type: integer + scheme: + description: Scheme replaces the server URL scheme for + the health check endpoint. + type: string + status: + description: Status defines the expected HTTP status + code of the response to the health check request. + type: integer + timeout: + anyOf: + - type: integer + - type: string + description: |- + Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. + Default: 5s + x-kubernetes-int-or-string: true + unhealthyInterval: + anyOf: + - type: integer + - type: string + description: |- + UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. + When UnhealthyInterval is not defined, it defaults to the Interval value. + Default: 30s + x-kubernetes-int-or-string: true + type: object + kind: + description: Kind defines the kind of the Service. + enum: + - Service + - TraefikService + type: string + name: + description: |- + Name defines the name of the referenced Kubernetes Service or TraefikService. + The differentiation between the two is specified in the Kind field. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Kubernetes Service or TraefikService. + type: string + nativeLB: + description: |- + NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean + nodePortLB: + description: |- + NodePortLB controls, when creating the load-balancer, + whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is NodePort. + It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes. + By default, NodePortLB is false. + type: boolean + passHostHeader: + description: |- + PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. + By default, passHostHeader is true. + type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health checks + for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window during + which the failed attempts must occur for the server + to be marked as unhealthy. It also defines for how + long the server will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window + before marking the server as unhealthy. + type: integer + type: object + port: + anyOf: + - type: integer + - type: string + description: |- + Port defines the port of a Kubernetes Service. + This can be a reference to a named port. + x-kubernetes-int-or-string: true + responseForwarding: + description: ResponseForwarding defines how Traefik forwards + the response from the upstream Kubernetes Service to the + client. + properties: + flushInterval: + description: |- + FlushInterval defines the interval, in milliseconds, in between flushes to the client while copying the response body. + A negative value means to flush immediately after each write to the client. + This configuration is ignored when ReverseProxy recognizes a response as a streaming response; + for such responses, writes are flushed to the client immediately. + Default: 100ms + type: string + type: object + scheme: + description: |- + Scheme defines the scheme to use for the request to the upstream Kubernetes Service. + It defaults to https when Kubernetes Service port is 443, http otherwise. + type: string + serversTransport: + description: |- + ServersTransport defines the name of ServersTransport resource to use. + It allows to configure the transport between Traefik and your servers. + Can only be used on a Kubernetes Service. + type: string + sticky: + description: |- + Sticky defines the sticky sessions configuration. + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + properties: + cookie: + description: Cookie defines the sticky cookie configuration. + properties: + domain: + description: |- + Domain defines the host to which the cookie will be sent. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#domaindomain-value + type: string + httpOnly: + description: HTTPOnly defines whether the cookie + can be accessed by client-side APIs, such as JavaScript. + type: boolean + maxAge: + description: |- + MaxAge defines the number of seconds until the cookie expires. + When set to a negative number, the cookie expires immediately. + When set to zero, the cookie never expires. + type: integer + name: + description: Name defines the Cookie name. + type: string + path: + description: |- + Path defines the path that must exist in the requested URL for the browser to send the Cookie header. + When not provided the cookie will be sent on every request to the domain. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#pathpath-value + type: string + sameSite: + description: |- + SameSite defines the same site policy. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + enum: + - none + - lax + - strict + type: string + secure: + description: Secure defines whether the cookie can + only be transmitted over an encrypted connection + (i.e. HTTPS). + type: boolean + type: object + type: object + strategy: + description: |- + Strategy defines the load balancing strategy between the servers. + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). + RoundRobin value is deprecated and supported for backward compatibility. + enum: + - wrr + - p2c + - hrw + - leasttime + - RoundRobin + type: string + weight: + description: |- + Weight defines the weight and should only be specified when Name references a TraefikService object + (and to be precise, one that embeds a Weighted Round Robin). + minimum: 0 + type: integer + required: + - name + type: object + type: array + type: object mirroring: description: Mirroring defines the Mirroring service configuration. properties: @@ -2954,6 +3254,25 @@ spec: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, passHostHeader is true. type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health checks + for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window during + which the failed attempts must occur for the server + to be marked as unhealthy. It also defines for how + long the server will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window + before marking the server as unhealthy. + type: integer + type: object percent: description: |- Percent defines the part of the traffic to mirror. @@ -2995,7 +3314,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -3043,11 +3362,13 @@ spec: strategy: description: |- Strategy defines the load balancing strategy between the servers. - Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). RoundRobin value is deprecated and supported for backward compatibility. enum: - wrr - p2c + - hrw + - leasttime - RoundRobin type: string weight: @@ -3088,6 +3409,25 @@ spec: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, passHostHeader is true. type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health checks + for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window during + which the failed attempts must occur for the server to be + marked as unhealthy. It also defines for how long the server + will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window before + marking the server as unhealthy. + type: integer + type: object port: anyOf: - type: integer @@ -3123,7 +3463,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -3170,11 +3510,13 @@ spec: strategy: description: |- Strategy defines the load balancing strategy between the servers. - Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). RoundRobin value is deprecated and supported for backward compatibility. enum: - wrr - p2c + - hrw + - leasttime - RoundRobin type: string weight: @@ -3300,6 +3642,25 @@ spec: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, passHostHeader is true. type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health checks + for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window during + which the failed attempts must occur for the server + to be marked as unhealthy. It also defines for how + long the server will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window + before marking the server as unhealthy. + type: integer + type: object port: anyOf: - type: integer @@ -3336,7 +3697,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -3384,11 +3745,13 @@ spec: strategy: description: |- Strategy defines the load balancing strategy between the servers. - Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). RoundRobin value is deprecated and supported for backward compatibility. enum: - wrr - p2c + - hrw + - leasttime - RoundRobin type: string weight: @@ -3404,7 +3767,7 @@ spec: sticky: description: |- Sticky defines whether sticky sessions are enabled. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/traefikservice/#stickiness-and-load-balancing + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/traefikservice/#stickiness-and-load-balancing properties: cookie: description: Cookie defines the sticky cookie configuration. diff --git a/docs/content/reference/dynamic-configuration/kubernetes-crd.md b/docs/content/reference/dynamic-configuration/kubernetes-crd.md index fb6ec6f72..aec00bee1 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-crd.md +++ b/docs/content/reference/dynamic-configuration/kubernetes-crd.md @@ -26,4 +26,4 @@ Dynamic configuration with Kubernetes Custom Resource --8<-- "content/reference/dynamic-configuration/kubernetes-crd-rbac.yml" ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml b/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml index c03dc0147..b2ab1e5db 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml @@ -1,4 +1,3 @@ ---- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: diff --git a/docs/content/reference/dynamic-configuration/kubernetes-gateway-resource.yml b/docs/content/reference/dynamic-configuration/kubernetes-gateway-resource.yml index d3b1bef20..4b860c4c5 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-gateway-resource.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-gateway-resource.yml @@ -1,4 +1,3 @@ ---- apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: diff --git a/docs/content/reference/dynamic-configuration/kubernetes-gateway-simple-https.yml b/docs/content/reference/dynamic-configuration/kubernetes-gateway-simple-https.yml index 1f15ae429..886010b0b 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-gateway-simple-https.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-gateway-simple-https.yml @@ -1,4 +1,3 @@ ---- apiVersion: gateway.networking.k8s.io/v1 kind: GatewayClass metadata: diff --git a/docs/content/reference/dynamic-configuration/kubernetes-gateway-traefik-lb-svc.yml b/docs/content/reference/dynamic-configuration/kubernetes-gateway-traefik-lb-svc.yml index 2d09a2a16..ee5394387 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-gateway-traefik-lb-svc.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-gateway-traefik-lb-svc.yml @@ -1,4 +1,3 @@ ---- apiVersion: v1 kind: ServiceAccount metadata: @@ -25,7 +24,7 @@ spec: serviceAccountName: traefik-controller containers: - name: traefik - image: traefik:v3.5 + image: traefik:v3.6 args: - --entryPoints.web.address=:80 - --entryPoints.websecure.address=:443 diff --git a/docs/content/reference/dynamic-configuration/kubernetes-ingress-nginx-rbac.yml b/docs/content/reference/dynamic-configuration/kubernetes-ingress-nginx-rbac.yml new file mode 100644 index 000000000..ca36e2f41 --- /dev/null +++ b/docs/content/reference/dynamic-configuration/kubernetes-ingress-nginx-rbac.yml @@ -0,0 +1,65 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: traefik-ingress-nginx-controller +rules: + - apiGroups: + - "" + resources: + - services + - secrets + verbs: + - list + - watch + # When using the watchNamespaceSelector option, + # Traefik requires permissions to list and watch namespaces. + - apiGroups: + - "" + resources: + - namespaces + verbs: + - list + - watch + # The pods right is needed to inject k8s.pod.uid and k8s.pod.name OTel attributes. + # When OTel tracing/logs/metrics are not enabled, this rule is not needed. + - apiGroups: + - "" + resources: + - pods + verbs: + - get + - apiGroups: + - discovery.k8s.io + resources: + - endpointslices + verbs: + - list + - watch + - apiGroups: + - networking.k8s.io + resources: + - ingresses + - ingressclasses + verbs: + - list + - watch + - apiGroups: + - networking.k8s.io + resources: + - ingresses/status + verbs: + - update + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: traefik-ingress-nginx-controller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: traefik-ingress-nginx-controller +subjects: + - kind: ServiceAccount + name: traefik-ingress-nginx-controller + namespace: default diff --git a/docs/content/reference/dynamic-configuration/kubernetes-knative-rbac.yml b/docs/content/reference/dynamic-configuration/kubernetes-knative-rbac.yml new file mode 100644 index 000000000..ea721ec12 --- /dev/null +++ b/docs/content/reference/dynamic-configuration/kubernetes-knative-rbac.yml @@ -0,0 +1,49 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: knative-networking-role +rules: + - apiGroups: + - "" + resources: + - services + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch + - apiGroups: + - networking.internal.knative.dev + resources: + - ingresses + verbs: + - get + - list + - watch + - apiGroups: + - networking.internal.knative.dev + resources: + - ingresses/status + verbs: + - update + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: gateway-controller +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: knative-networking-role +subjects: + - kind: ServiceAccount + name: traefik-controller + namespace: default diff --git a/docs/content/reference/dynamic-configuration/kubernetes-whoami-svc.yml b/docs/content/reference/dynamic-configuration/kubernetes-whoami-svc.yml index 32471e4c7..2a1b33be4 100644 --- a/docs/content/reference/dynamic-configuration/kubernetes-whoami-svc.yml +++ b/docs/content/reference/dynamic-configuration/kubernetes-whoami-svc.yml @@ -1,4 +1,3 @@ ---- apiVersion: apps/v1 kind: Deployment metadata: diff --git a/docs/content/reference/dynamic-configuration/kv-ref.md b/docs/content/reference/dynamic-configuration/kv-ref.md index d73df2bfa..a9d2984b4 100644 --- a/docs/content/reference/dynamic-configuration/kv-ref.md +++ b/docs/content/reference/dynamic-configuration/kv-ref.md @@ -274,56 +274,63 @@ THIS FILE MUST NOT BE EDITED BY HAND | `traefik/http/services/Service01/failover/fallback` | `foobar` | | `traefik/http/services/Service01/failover/healthCheck` | `` | | `traefik/http/services/Service01/failover/service` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/followRedirects` | `true` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/headers/name0` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/headers/name1` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/hostname` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/interval` | `42s` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/method` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/mode` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/path` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/port` | `42` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/scheme` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/status` | `42` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/timeout` | `42s` | -| `traefik/http/services/Service02/loadBalancer/healthCheck/unhealthyInterval` | `42s` | -| `traefik/http/services/Service02/loadBalancer/passHostHeader` | `true` | -| `traefik/http/services/Service02/loadBalancer/responseForwarding/flushInterval` | `42s` | -| `traefik/http/services/Service02/loadBalancer/servers/0/preservePath` | `true` | -| `traefik/http/services/Service02/loadBalancer/servers/0/url` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/servers/0/weight` | `42` | -| `traefik/http/services/Service02/loadBalancer/servers/1/preservePath` | `true` | -| `traefik/http/services/Service02/loadBalancer/servers/1/url` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/servers/1/weight` | `42` | -| `traefik/http/services/Service02/loadBalancer/serversTransport` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/sticky/cookie/domain` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/sticky/cookie/httpOnly` | `true` | -| `traefik/http/services/Service02/loadBalancer/sticky/cookie/maxAge` | `42` | -| `traefik/http/services/Service02/loadBalancer/sticky/cookie/name` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/sticky/cookie/path` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/sticky/cookie/sameSite` | `foobar` | -| `traefik/http/services/Service02/loadBalancer/sticky/cookie/secure` | `true` | -| `traefik/http/services/Service02/loadBalancer/strategy` | `foobar` | -| `traefik/http/services/Service03/mirroring/healthCheck` | `` | -| `traefik/http/services/Service03/mirroring/maxBodySize` | `42` | -| `traefik/http/services/Service03/mirroring/mirrorBody` | `true` | -| `traefik/http/services/Service03/mirroring/mirrors/0/name` | `foobar` | -| `traefik/http/services/Service03/mirroring/mirrors/0/percent` | `42` | -| `traefik/http/services/Service03/mirroring/mirrors/1/name` | `foobar` | -| `traefik/http/services/Service03/mirroring/mirrors/1/percent` | `42` | -| `traefik/http/services/Service03/mirroring/service` | `foobar` | -| `traefik/http/services/Service04/weighted/healthCheck` | `` | -| `traefik/http/services/Service04/weighted/services/0/name` | `foobar` | -| `traefik/http/services/Service04/weighted/services/0/weight` | `42` | -| `traefik/http/services/Service04/weighted/services/1/name` | `foobar` | -| `traefik/http/services/Service04/weighted/services/1/weight` | `42` | -| `traefik/http/services/Service04/weighted/sticky/cookie/domain` | `foobar` | -| `traefik/http/services/Service04/weighted/sticky/cookie/httpOnly` | `true` | -| `traefik/http/services/Service04/weighted/sticky/cookie/maxAge` | `42` | -| `traefik/http/services/Service04/weighted/sticky/cookie/name` | `foobar` | -| `traefik/http/services/Service04/weighted/sticky/cookie/path` | `foobar` | -| `traefik/http/services/Service04/weighted/sticky/cookie/sameSite` | `foobar` | -| `traefik/http/services/Service04/weighted/sticky/cookie/secure` | `true` | +| `traefik/http/services/Service02/highestRandomWeight/healthCheck` | `` | +| `traefik/http/services/Service02/highestRandomWeight/services/0/name` | `foobar` | +| `traefik/http/services/Service02/highestRandomWeight/services/0/weight` | `42` | +| `traefik/http/services/Service02/highestRandomWeight/services/1/name` | `foobar` | +| `traefik/http/services/Service02/highestRandomWeight/services/1/weight` | `42` | +| `traefik/http/services/Service03/loadBalancer/healthCheck/followRedirects` | `true` | +| `traefik/http/services/Service03/loadBalancer/healthCheck/headers/name0` | `foobar` | +| `traefik/http/services/Service03/loadBalancer/healthCheck/headers/name1` | `foobar` | +| `traefik/http/services/Service03/loadBalancer/healthCheck/hostname` | `foobar` | +| `traefik/http/services/Service03/loadBalancer/healthCheck/interval` | `42s` | +| `traefik/http/services/Service03/loadBalancer/healthCheck/method` | `foobar` | +| `traefik/http/services/Service03/loadBalancer/healthCheck/mode` | `foobar` | +| `traefik/http/services/Service03/loadBalancer/healthCheck/path` | `foobar` | +| `traefik/http/services/Service03/loadBalancer/healthCheck/port` | `42` | +| `traefik/http/services/Service03/loadBalancer/healthCheck/scheme` | `foobar` | +| `traefik/http/services/Service03/loadBalancer/healthCheck/status` | `42` | +| `traefik/http/services/Service03/loadBalancer/healthCheck/timeout` | `42s` | +| `traefik/http/services/Service03/loadBalancer/healthCheck/unhealthyInterval` | `42s` | +| `traefik/http/services/Service03/loadBalancer/passHostHeader` | `true` | +| `traefik/http/services/Service03/loadBalancer/passiveHealthCheck/failureWindow` | `42s` | +| `traefik/http/services/Service03/loadBalancer/passiveHealthCheck/maxFailedAttempts` | `42` | +| `traefik/http/services/Service03/loadBalancer/responseForwarding/flushInterval` | `42s` | +| `traefik/http/services/Service03/loadBalancer/servers/0/preservePath` | `true` | +| `traefik/http/services/Service03/loadBalancer/servers/0/url` | `foobar` | +| `traefik/http/services/Service03/loadBalancer/servers/0/weight` | `42` | +| `traefik/http/services/Service03/loadBalancer/servers/1/preservePath` | `true` | +| `traefik/http/services/Service03/loadBalancer/servers/1/url` | `foobar` | +| `traefik/http/services/Service03/loadBalancer/servers/1/weight` | `42` | +| `traefik/http/services/Service03/loadBalancer/serversTransport` | `foobar` | +| `traefik/http/services/Service03/loadBalancer/sticky/cookie/domain` | `foobar` | +| `traefik/http/services/Service03/loadBalancer/sticky/cookie/httpOnly` | `true` | +| `traefik/http/services/Service03/loadBalancer/sticky/cookie/maxAge` | `42` | +| `traefik/http/services/Service03/loadBalancer/sticky/cookie/name` | `foobar` | +| `traefik/http/services/Service03/loadBalancer/sticky/cookie/path` | `foobar` | +| `traefik/http/services/Service03/loadBalancer/sticky/cookie/sameSite` | `foobar` | +| `traefik/http/services/Service03/loadBalancer/sticky/cookie/secure` | `true` | +| `traefik/http/services/Service03/loadBalancer/strategy` | `foobar` | +| `traefik/http/services/Service04/mirroring/healthCheck` | `` | +| `traefik/http/services/Service04/mirroring/maxBodySize` | `42` | +| `traefik/http/services/Service04/mirroring/mirrorBody` | `true` | +| `traefik/http/services/Service04/mirroring/mirrors/0/name` | `foobar` | +| `traefik/http/services/Service04/mirroring/mirrors/0/percent` | `42` | +| `traefik/http/services/Service04/mirroring/mirrors/1/name` | `foobar` | +| `traefik/http/services/Service04/mirroring/mirrors/1/percent` | `42` | +| `traefik/http/services/Service04/mirroring/service` | `foobar` | +| `traefik/http/services/Service05/weighted/healthCheck` | `` | +| `traefik/http/services/Service05/weighted/services/0/name` | `foobar` | +| `traefik/http/services/Service05/weighted/services/0/weight` | `42` | +| `traefik/http/services/Service05/weighted/services/1/name` | `foobar` | +| `traefik/http/services/Service05/weighted/services/1/weight` | `42` | +| `traefik/http/services/Service05/weighted/sticky/cookie/domain` | `foobar` | +| `traefik/http/services/Service05/weighted/sticky/cookie/httpOnly` | `true` | +| `traefik/http/services/Service05/weighted/sticky/cookie/maxAge` | `42` | +| `traefik/http/services/Service05/weighted/sticky/cookie/name` | `foobar` | +| `traefik/http/services/Service05/weighted/sticky/cookie/path` | `foobar` | +| `traefik/http/services/Service05/weighted/sticky/cookie/sameSite` | `foobar` | +| `traefik/http/services/Service05/weighted/sticky/cookie/secure` | `true` | | `traefik/tcp/middlewares/TCPMiddleware01/ipAllowList/sourceRange/0` | `foobar` | | `traefik/tcp/middlewares/TCPMiddleware01/ipAllowList/sourceRange/1` | `foobar` | | `traefik/tcp/middlewares/TCPMiddleware02/ipWhiteList/sourceRange/0` | `foobar` | @@ -365,7 +372,6 @@ THIS FILE MUST NOT BE EDITED BY HAND | `traefik/tcp/routers/TCPRouter1/tls/passthrough` | `true` | | `traefik/tcp/serversTransports/TCPServersTransport0/dialKeepAlive` | `42s` | | `traefik/tcp/serversTransports/TCPServersTransport0/dialTimeout` | `42s` | -| `traefik/tcp/serversTransports/TCPServersTransport0/proxyProtocol/version` | `42` | | `traefik/tcp/serversTransports/TCPServersTransport0/terminationDelay` | `42s` | | `traefik/tcp/serversTransports/TCPServersTransport0/tls/certificates/0/certFile` | `foobar` | | `traefik/tcp/serversTransports/TCPServersTransport0/tls/certificates/0/keyFile` | `foobar` | @@ -381,7 +387,6 @@ THIS FILE MUST NOT BE EDITED BY HAND | `traefik/tcp/serversTransports/TCPServersTransport0/tls/spiffe/trustDomain` | `foobar` | | `traefik/tcp/serversTransports/TCPServersTransport1/dialKeepAlive` | `42s` | | `traefik/tcp/serversTransports/TCPServersTransport1/dialTimeout` | `42s` | -| `traefik/tcp/serversTransports/TCPServersTransport1/proxyProtocol/version` | `42` | | `traefik/tcp/serversTransports/TCPServersTransport1/terminationDelay` | `42s` | | `traefik/tcp/serversTransports/TCPServersTransport1/tls/certificates/0/certFile` | `foobar` | | `traefik/tcp/serversTransports/TCPServersTransport1/tls/certificates/0/keyFile` | `foobar` | diff --git a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml index 7c40866ea..3debb5926 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutes.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: ingressroutes.traefik.io spec: group: traefik.io @@ -43,11 +43,31 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/entrypoints/ Default: all. items: type: string type: array + parentRefs: + description: |- + ParentRefs defines references to parent IngressRoute resources for multi-layer routing. + When set, this IngressRoute's routers will be children of the referenced parent IngressRoute's routers. + More info: https://doc.traefik.io/traefik/v3.6/routing/routers/#parentrefs + items: + description: IngressRouteRef is a reference to an IngressRoute resource. + properties: + name: + description: Name defines the name of the referenced IngressRoute + resource. + type: string + namespace: + description: Namespace defines the namespace of the referenced + IngressRoute resource. + type: string + required: + - name + type: object + type: array routes: description: Routes defines the list of routes. items: @@ -64,12 +84,12 @@ spec: match: description: |- Match defines the router's rule. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/ type: string middlewares: description: |- Middlewares defines the list of references to Middleware resources. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/middleware/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/middleware/ items: description: MiddlewareRef is a reference to a Middleware resource. @@ -89,7 +109,7 @@ spec: observability: description: |- Observability defines the observability configuration for a router. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/observability/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/observability/ properties: accessLogs: description: AccessLogs enables access logs for this router. @@ -112,7 +132,7 @@ spec: priority: description: |- Priority defines the router's priority. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/#priority + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/#priority maximum: 9223372036854775000 type: integer services: @@ -227,6 +247,25 @@ spec: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, passHostHeader is true. type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health + checks for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window + during which the failed attempts must occur for + the server to be marked as unhealthy. It also defines + for how long the server will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window + before marking the server as unhealthy. + type: integer + type: object port: anyOf: - type: integer @@ -263,7 +302,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -312,11 +351,13 @@ spec: strategy: description: |- Strategy defines the load balancing strategy between the servers. - Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). RoundRobin value is deprecated and supported for backward compatibility. enum: - wrr - p2c + - hrw + - leasttime - RoundRobin type: string weight: @@ -332,7 +373,7 @@ spec: syntax: description: |- Syntax defines the router's rule syntax. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. type: string required: @@ -342,18 +383,18 @@ spec: tls: description: |- TLS defines the TLS configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/router/#tls + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/router/#tls properties: certResolver: description: |- CertResolver defines the name of the certificate resolver to use. Cert resolvers have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/tls/certificate-resolvers/acme/ + More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/tls/certificate-resolvers/acme/ type: string domains: description: |- Domains defines the list of domains that will be used to issue certificates. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#domains + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#domains items: description: Domain holds a domain name with SANs. properties: @@ -372,17 +413,17 @@ spec: description: |- Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. If not defined, the `default` TLSOption is used. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-options/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-options/ properties: name: description: |- Name defines the name of the referenced TLSOption. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsoption/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/tlsoption/ type: string namespace: description: |- Namespace defines the namespace of the referenced TLSOption. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsoption/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/tlsoption/ type: string required: - name @@ -399,12 +440,12 @@ spec: name: description: |- Name defines the name of the referenced TLSStore. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsstore/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/tlsstore/ type: string namespace: description: |- Namespace defines the namespace of the referenced TLSStore. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsstore/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/tlsstore/ type: string required: - name diff --git a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutetcps.yaml b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutetcps.yaml index 37a02f2fb..c7006f6d0 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_ingressroutetcps.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_ingressroutetcps.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: ingressroutetcps.traefik.io spec: group: traefik.io @@ -43,7 +43,7 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/entrypoints/ Default: all. items: type: string @@ -56,7 +56,7 @@ spec: match: description: |- Match defines the router's rule. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/ type: string middlewares: description: Middlewares defines the list of references to MiddlewareTCP @@ -80,7 +80,7 @@ spec: priority: description: |- Priority defines the router's priority. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/#priority + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/#priority maximum: 9223372036854775000 type: integer services: @@ -122,7 +122,7 @@ spec: proxyProtocol: description: |- ProxyProtocol defines the PROXY protocol configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/service/#proxy-protocol + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/service/#proxy-protocol Deprecated: ProxyProtocol will not be supported in future APIVersions, please use ServersTransport to configure ProxyProtocol instead. properties: version: @@ -164,7 +164,7 @@ spec: syntax: description: |- Syntax defines the router's rule syntax. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. enum: - v3 @@ -177,18 +177,18 @@ spec: tls: description: |- TLS defines the TLS configuration on a layer 4 / TCP Route. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/router/#tls + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/router/#tls properties: certResolver: description: |- CertResolver defines the name of the certificate resolver to use. Cert resolvers have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/tls/certificate-resolvers/acme/ + More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/tls/certificate-resolvers/acme/ type: string domains: description: |- Domains defines the list of domains that will be used to issue certificates. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/#domains + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/tls/#domains items: description: Domain holds a domain name with SANs. properties: @@ -207,7 +207,7 @@ spec: description: |- Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. If not defined, the `default` TLSOption is used. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/#tls-options + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/tls/#tls-options properties: name: description: Name defines the name of the referenced Traefik diff --git a/docs/content/reference/dynamic-configuration/traefik.io_ingressrouteudps.yaml b/docs/content/reference/dynamic-configuration/traefik.io_ingressrouteudps.yaml index dc08ce018..d5e8d0857 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_ingressrouteudps.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_ingressrouteudps.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: ingressrouteudps.traefik.io spec: group: traefik.io @@ -43,7 +43,7 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/entrypoints/ Default: all. items: type: string diff --git a/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml b/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml index 5321da0b0..f0999cf22 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_middlewares.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: middlewares.traefik.io spec: group: traefik.io @@ -19,7 +19,7 @@ spec: openAPIV3Schema: description: |- Middleware is the CRD implementation of a Traefik Middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/overview/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/overview/ properties: apiVersion: description: |- @@ -45,7 +45,7 @@ spec: description: |- AddPrefix holds the add prefix middleware configuration. This middleware updates the path of a request before forwarding it. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/addprefix/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/addprefix/ properties: prefix: description: |- @@ -60,12 +60,12 @@ spec: description: |- BasicAuth holds the basic auth middleware configuration. This middleware restricts access to your services to known users. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/basicauth/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/basicauth/ properties: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/basicauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/basicauth/#headerfield type: string realm: description: |- @@ -86,7 +86,7 @@ spec: description: |- Buffering holds the buffering middleware configuration. This middleware retries or limits the size of requests that can be forwarded to backends. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/buffering/#maxrequestbodybytes + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/buffering/#maxrequestbodybytes properties: maxRequestBodyBytes: description: |- @@ -118,14 +118,14 @@ spec: description: |- RetryExpression defines the retry conditions. It is a logical combination of functions with operators AND (&&) and OR (||). - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/buffering/#retryexpression + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/buffering/#retryexpression type: string type: object chain: description: |- Chain holds the configuration of the chain middleware. This middleware enables to define reusable combinations of other pieces of middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/chain/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/chain/ properties: middlewares: description: Middlewares is the list of MiddlewareRef which composes @@ -188,7 +188,7 @@ spec: description: |- Compress holds the compress middleware configuration. This middleware compresses responses before sending them to the client, using gzip, brotli, or zstd compression. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/compress/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/compress/ properties: defaultEncoding: description: DefaultEncoding specifies the default encoding if @@ -238,12 +238,12 @@ spec: description: |- DigestAuth holds the digest auth middleware configuration. This middleware restricts access to your services to known users. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/digestauth/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/digestauth/ properties: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/digestauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/digestauth/#headerfield type: string realm: description: |- @@ -263,7 +263,7 @@ spec: description: |- ErrorPage holds the custom error middleware configuration. This middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/errorpages/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/errorpages/ properties: query: description: |- @@ -275,7 +275,7 @@ spec: service: description: |- Service defines the reference to a Kubernetes Service that will serve the error page. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/errorpages/#service + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/errorpages/#service properties: healthCheck: description: Healthcheck defines health checks for ExternalName @@ -381,6 +381,25 @@ spec: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, passHostHeader is true. type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health checks + for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window during + which the failed attempts must occur for the server + to be marked as unhealthy. It also defines for how long + the server will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window before + marking the server as unhealthy. + type: integer + type: object port: anyOf: - type: integer @@ -417,7 +436,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -465,11 +484,13 @@ spec: strategy: description: |- Strategy defines the load balancing strategy between the servers. - Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). RoundRobin value is deprecated and supported for backward compatibility. enum: - wrr - p2c + - hrw + - leasttime - RoundRobin type: string weight: @@ -504,7 +525,7 @@ spec: description: |- ForwardAuth holds the forward auth middleware configuration. This middleware delegates the request authentication to a Service. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/forwardauth/ properties: addAuthCookiesToResponse: description: AddAuthCookiesToResponse defines the list of cookies @@ -532,7 +553,7 @@ spec: authResponseHeadersRegex: description: |- AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/#authresponseheadersregex + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/forwardauth/#authresponseheadersregex type: string forwardBody: description: ForwardBody defines whether to send the request body @@ -541,7 +562,7 @@ spec: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/forwardauth/#headerfield type: string maxBodySize: description: MaxBodySize defines the maximum body size in bytes @@ -603,7 +624,7 @@ spec: description: |- Headers holds the headers middleware configuration. This middleware manages the requests and responses headers. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/headers/#customrequestheaders + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/headers/#customrequestheaders properties: accessControlAllowCredentials: description: AccessControlAllowCredentials defines whether the @@ -775,7 +796,7 @@ spec: description: |- InFlightReq holds the in-flight request middleware configuration. This middleware limits the number of requests being processed and served concurrently. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/inflightreq/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/inflightreq/ properties: amount: description: |- @@ -789,12 +810,12 @@ spec: SourceCriterion defines what criterion is used to group requests as originating from a common source. If several strategies are defined at the same time, an error will be raised. If none are set, the default is to use the requestHost. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/inflightreq/#sourcecriterion + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/inflightreq/#sourcecriterion properties: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -830,12 +851,12 @@ spec: description: |- IPAllowList holds the IP allowlist middleware configuration. This middleware limits allowed requests based on the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipallowlist/ properties: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -873,7 +894,7 @@ spec: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -904,7 +925,7 @@ spec: description: |- PassTLSClientCert holds the pass TLS client cert middleware configuration. This middleware adds the selected data from the passed client TLS certificate to a header. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/passtlsclientcert/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/passtlsclientcert/ properties: info: description: Info selects the specific client certificate details @@ -1007,13 +1028,13 @@ spec: x-kubernetes-preserve-unknown-fields: true description: |- Plugin defines the middleware plugin configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/overview/#community-middlewares + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/overview/#community-middlewares type: object rateLimit: description: |- RateLimit holds the rate limit configuration. This middleware ensures that services will receive a fair amount of requests, and allows one to define what fair is. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/ratelimit/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/ratelimit/ properties: average: description: |- @@ -1132,7 +1153,7 @@ spec: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1168,7 +1189,7 @@ spec: description: |- RedirectRegex holds the redirect regex middleware configuration. This middleware redirects a request using regex matching and replacement. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/redirectregex/#regex + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/redirectregex/#regex properties: permanent: description: Permanent defines whether the redirection is permanent @@ -1187,11 +1208,12 @@ spec: description: |- RedirectScheme holds the redirect scheme middleware configuration. This middleware redirects requests from a scheme/port to another. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/redirectscheme/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/redirectscheme/ properties: permanent: - description: Permanent defines whether the redirection is permanent - (308). + description: |- + Permanent defines whether the redirection is permanent. + For HTTP GET requests a 301 is returned, otherwise a 308 is returned. type: boolean port: description: Port defines the port of the new URL. @@ -1204,7 +1226,7 @@ spec: description: |- ReplacePath holds the replace path middleware configuration. This middleware replaces the path of the request URL and store the original path in an X-Replaced-Path header. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/replacepath/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/replacepath/ properties: path: description: Path defines the path to use as replacement in the @@ -1215,7 +1237,7 @@ spec: description: |- ReplacePathRegex holds the replace path regex middleware configuration. This middleware replaces the path of a URL using regex matching and replacement. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/replacepathregex/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/replacepathregex/ properties: regex: description: Regex defines the regular expression used to match @@ -1231,7 +1253,7 @@ spec: Retry holds the retry middleware configuration. This middleware reissues requests a given number of times to a backend server if that server does not reply. As soon as the server answers, the middleware stops retrying, regardless of the response status. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/retry/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/retry/ properties: attempts: description: Attempts defines how many times the request should @@ -1255,7 +1277,7 @@ spec: description: |- StripPrefix holds the strip prefix middleware configuration. This middleware removes the specified prefixes from the URL path. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/stripprefix/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/stripprefix/ properties: forceSlash: description: |- @@ -1274,7 +1296,7 @@ spec: description: |- StripPrefixRegex holds the strip prefix regex middleware configuration. This middleware removes the matching prefixes from the URL path. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/stripprefixregex/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/stripprefixregex/ properties: regex: description: Regex defines the regular expression to match the diff --git a/docs/content/reference/dynamic-configuration/traefik.io_middlewaretcps.yaml b/docs/content/reference/dynamic-configuration/traefik.io_middlewaretcps.yaml index 5f7604923..b2a19f9d5 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_middlewaretcps.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_middlewaretcps.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: middlewaretcps.traefik.io spec: group: traefik.io @@ -19,7 +19,7 @@ spec: openAPIV3Schema: description: |- MiddlewareTCP is the CRD implementation of a Traefik TCP middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/overview/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/overview/ properties: apiVersion: description: |- @@ -56,7 +56,7 @@ spec: description: |- IPAllowList defines the IPAllowList middleware configuration. This middleware accepts/refuses connections based on the client IP. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/ipallowlist/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/ipallowlist/ properties: sourceRange: description: SourceRange defines the allowed IPs (or ranges of @@ -70,7 +70,7 @@ spec: IPWhiteList defines the IPWhiteList middleware configuration. This middleware accepts/refuses connections based on the client IP. Deprecated: please use IPAllowList instead. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/ipwhitelist/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/ipwhitelist/ properties: sourceRange: description: SourceRange defines the allowed IPs (or ranges of diff --git a/docs/content/reference/dynamic-configuration/traefik.io_serverstransports.yaml b/docs/content/reference/dynamic-configuration/traefik.io_serverstransports.yaml index 0828291ee..d5ec61903 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_serverstransports.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_serverstransports.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: serverstransports.traefik.io spec: group: traefik.io @@ -21,7 +21,7 @@ spec: ServersTransport is the CRD implementation of a ServersTransport. If no serversTransport is specified, the default@internal will be used. The default@internal serversTransport is created from the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/serverstransport/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/serverstransport/ properties: apiVersion: description: |- diff --git a/docs/content/reference/dynamic-configuration/traefik.io_serverstransporttcps.yaml b/docs/content/reference/dynamic-configuration/traefik.io_serverstransporttcps.yaml index f8be2b1d9..79469823b 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_serverstransporttcps.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_serverstransporttcps.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: serverstransporttcps.traefik.io spec: group: traefik.io @@ -21,7 +21,7 @@ spec: ServersTransportTCP is the CRD implementation of a TCPServersTransport. If no tcpServersTransport is specified, a default one named default@internal will be used. The default@internal tcpServersTransport can be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/serverstransport/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/serverstransport/ properties: apiVersion: description: |- diff --git a/docs/content/reference/dynamic-configuration/traefik.io_tlsoptions.yaml b/docs/content/reference/dynamic-configuration/traefik.io_tlsoptions.yaml index c32974fae..5f37cb023 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_tlsoptions.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_tlsoptions.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: tlsoptions.traefik.io spec: group: traefik.io @@ -19,7 +19,7 @@ spec: openAPIV3Schema: description: |- TLSOption is the CRD implementation of a Traefik TLS Option, allowing to configure some parameters of the TLS connection. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#tls-options + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#tls-options properties: apiVersion: description: |- @@ -44,14 +44,14 @@ spec: alpnProtocols: description: |- ALPNProtocols defines the list of supported application level protocols for the TLS handshake, in order of preference. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#alpn-protocols + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#alpn-protocols items: type: string type: array cipherSuites: description: |- CipherSuites defines the list of supported cipher suites for TLS versions up to TLS 1.2. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#cipher-suites + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#cipher-suites items: type: string type: array @@ -79,7 +79,7 @@ spec: curvePreferences: description: |- CurvePreferences defines the preferred elliptic curves. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#curve-preferences + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#curve-preferences items: type: string type: array diff --git a/docs/content/reference/dynamic-configuration/traefik.io_tlsstores.yaml b/docs/content/reference/dynamic-configuration/traefik.io_tlsstores.yaml index 779c93908..7fbf65443 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_tlsstores.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_tlsstores.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: tlsstores.traefik.io spec: group: traefik.io @@ -21,7 +21,7 @@ spec: TLSStore is the CRD implementation of a Traefik TLS Store. For the time being, only the TLSStore named default is supported. This means that you cannot have two stores that are named default in different Kubernetes namespaces. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#certificates-stores + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#certificates-stores properties: apiVersion: description: |- diff --git a/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml b/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml index 77e97156b..a51ddc0a4 100644 --- a/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml +++ b/docs/content/reference/dynamic-configuration/traefik.io_traefikservices.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: traefikservices.traefik.io spec: group: traefik.io @@ -22,7 +22,7 @@ spec: TraefikService object allows to: - Apply weight to Services on load-balancing - Mirror traffic on services - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/traefikservice/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/traefikservice/ properties: apiVersion: description: |- @@ -44,6 +44,244 @@ spec: spec: description: TraefikServiceSpec defines the desired state of a TraefikService. properties: + highestRandomWeight: + description: HighestRandomWeight defines the highest random weight + service configuration. + properties: + services: + description: Services defines the list of Kubernetes Service and/or + TraefikService to load-balance, with weight. + items: + description: Service defines an upstream HTTP service to proxy + traffic to. + properties: + healthCheck: + description: Healthcheck defines health checks for ExternalName + services. + properties: + followRedirects: + description: |- + FollowRedirects defines whether redirects should be followed during the health check calls. + Default: true + type: boolean + headers: + additionalProperties: + type: string + description: Headers defines custom headers to be sent + to the health check endpoint. + type: object + hostname: + description: Hostname defines the value of hostname + in the Host header of the health check request. + type: string + interval: + anyOf: + - type: integer + - type: string + description: |- + Interval defines the frequency of the health check calls for healthy targets. + Default: 30s + x-kubernetes-int-or-string: true + method: + description: Method defines the healthcheck method. + type: string + mode: + description: |- + Mode defines the health check mode. + If defined to grpc, will use the gRPC health check protocol to probe the server. + Default: http + type: string + path: + description: Path defines the server URL path for the + health check endpoint. + type: string + port: + description: Port defines the server URL port for the + health check endpoint. + type: integer + scheme: + description: Scheme replaces the server URL scheme for + the health check endpoint. + type: string + status: + description: Status defines the expected HTTP status + code of the response to the health check request. + type: integer + timeout: + anyOf: + - type: integer + - type: string + description: |- + Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. + Default: 5s + x-kubernetes-int-or-string: true + unhealthyInterval: + anyOf: + - type: integer + - type: string + description: |- + UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. + When UnhealthyInterval is not defined, it defaults to the Interval value. + Default: 30s + x-kubernetes-int-or-string: true + type: object + kind: + description: Kind defines the kind of the Service. + enum: + - Service + - TraefikService + type: string + name: + description: |- + Name defines the name of the referenced Kubernetes Service or TraefikService. + The differentiation between the two is specified in the Kind field. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Kubernetes Service or TraefikService. + type: string + nativeLB: + description: |- + NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean + nodePortLB: + description: |- + NodePortLB controls, when creating the load-balancer, + whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is NodePort. + It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes. + By default, NodePortLB is false. + type: boolean + passHostHeader: + description: |- + PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. + By default, passHostHeader is true. + type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health checks + for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window during + which the failed attempts must occur for the server + to be marked as unhealthy. It also defines for how + long the server will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window + before marking the server as unhealthy. + type: integer + type: object + port: + anyOf: + - type: integer + - type: string + description: |- + Port defines the port of a Kubernetes Service. + This can be a reference to a named port. + x-kubernetes-int-or-string: true + responseForwarding: + description: ResponseForwarding defines how Traefik forwards + the response from the upstream Kubernetes Service to the + client. + properties: + flushInterval: + description: |- + FlushInterval defines the interval, in milliseconds, in between flushes to the client while copying the response body. + A negative value means to flush immediately after each write to the client. + This configuration is ignored when ReverseProxy recognizes a response as a streaming response; + for such responses, writes are flushed to the client immediately. + Default: 100ms + type: string + type: object + scheme: + description: |- + Scheme defines the scheme to use for the request to the upstream Kubernetes Service. + It defaults to https when Kubernetes Service port is 443, http otherwise. + type: string + serversTransport: + description: |- + ServersTransport defines the name of ServersTransport resource to use. + It allows to configure the transport between Traefik and your servers. + Can only be used on a Kubernetes Service. + type: string + sticky: + description: |- + Sticky defines the sticky sessions configuration. + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + properties: + cookie: + description: Cookie defines the sticky cookie configuration. + properties: + domain: + description: |- + Domain defines the host to which the cookie will be sent. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#domaindomain-value + type: string + httpOnly: + description: HTTPOnly defines whether the cookie + can be accessed by client-side APIs, such as JavaScript. + type: boolean + maxAge: + description: |- + MaxAge defines the number of seconds until the cookie expires. + When set to a negative number, the cookie expires immediately. + When set to zero, the cookie never expires. + type: integer + name: + description: Name defines the Cookie name. + type: string + path: + description: |- + Path defines the path that must exist in the requested URL for the browser to send the Cookie header. + When not provided the cookie will be sent on every request to the domain. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#pathpath-value + type: string + sameSite: + description: |- + SameSite defines the same site policy. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + enum: + - none + - lax + - strict + type: string + secure: + description: Secure defines whether the cookie can + only be transmitted over an encrypted connection + (i.e. HTTPS). + type: boolean + type: object + type: object + strategy: + description: |- + Strategy defines the load balancing strategy between the servers. + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). + RoundRobin value is deprecated and supported for backward compatibility. + enum: + - wrr + - p2c + - hrw + - leasttime + - RoundRobin + type: string + weight: + description: |- + Weight defines the weight and should only be specified when Name references a TraefikService object + (and to be precise, one that embeds a Weighted Round Robin). + minimum: 0 + type: integer + required: + - name + type: object + type: array + type: object mirroring: description: Mirroring defines the Mirroring service configuration. properties: @@ -245,6 +483,25 @@ spec: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, passHostHeader is true. type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health checks + for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window during + which the failed attempts must occur for the server + to be marked as unhealthy. It also defines for how + long the server will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window + before marking the server as unhealthy. + type: integer + type: object percent: description: |- Percent defines the part of the traffic to mirror. @@ -286,7 +543,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -334,11 +591,13 @@ spec: strategy: description: |- Strategy defines the load balancing strategy between the servers. - Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). RoundRobin value is deprecated and supported for backward compatibility. enum: - wrr - p2c + - hrw + - leasttime - RoundRobin type: string weight: @@ -379,6 +638,25 @@ spec: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, passHostHeader is true. type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health checks + for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window during + which the failed attempts must occur for the server to be + marked as unhealthy. It also defines for how long the server + will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window before + marking the server as unhealthy. + type: integer + type: object port: anyOf: - type: integer @@ -414,7 +692,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -461,11 +739,13 @@ spec: strategy: description: |- Strategy defines the load balancing strategy between the servers. - Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). RoundRobin value is deprecated and supported for backward compatibility. enum: - wrr - p2c + - hrw + - leasttime - RoundRobin type: string weight: @@ -591,6 +871,25 @@ spec: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, passHostHeader is true. type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health checks + for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window during + which the failed attempts must occur for the server + to be marked as unhealthy. It also defines for how + long the server will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window + before marking the server as unhealthy. + type: integer + type: object port: anyOf: - type: integer @@ -627,7 +926,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -675,11 +974,13 @@ spec: strategy: description: |- Strategy defines the load balancing strategy between the servers. - Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). RoundRobin value is deprecated and supported for backward compatibility. enum: - wrr - p2c + - hrw + - leasttime - RoundRobin type: string weight: @@ -695,7 +996,7 @@ spec: sticky: description: |- Sticky defines whether sticky sessions are enabled. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/traefikservice/#stickiness-and-load-balancing + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/traefikservice/#stickiness-and-load-balancing properties: cookie: description: Cookie defines the sticky cookie configuration. diff --git a/docs/content/reference/install-configuration/api-dashboard.md b/docs/content/reference/install-configuration/api-dashboard.md index 9f03454ca..42b954d01 100644 --- a/docs/content/reference/install-configuration/api-dashboard.md +++ b/docs/content/reference/install-configuration/api-dashboard.md @@ -3,13 +3,27 @@ title: "Traefik API & Dashboard Documentation" description: "Traefik Proxy exposes information through API handlers and showcase them on the Dashboard. Learn about the security, configuration, and endpoints of the APIs and Dashboard. Read the technical documentation." --- -The dashboard is the central place that shows you the current active routes handled by Traefik. +Traefik exposes a number of information through API endpoints, such as the configuration of your routers, services, middlewares, etc. + +The dashboard, which is the central place that displays the current active routes handled by Traefik, fetches the data from this API.
Dashboard - Providers
The dashboard in action
+## Security + +Enabling the API and the dashboard in production is not recommended, because it will expose all configuration elements, +including sensitive data, for which access should be reserved to administrators. + +In production, it should be at least secured by authentication and authorizations. + +!!! info + + It's recommended to NOT publicly exposing the API's port, keeping it restricted to internal networks + (as in the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege), applied to networks). + ## Configuration Example Enable the dashboard: @@ -150,7 +164,7 @@ http: The API and the dashboard can be configured: - In the Helm Chart: You can find the options to customize the Traefik installation -enabing the dashboard [here](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml#L155). +enabling the dashboard [here](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml#L155). - In the Traefik Static Configuration as described below. | Field | Description | Default | Required | @@ -187,6 +201,7 @@ All the following endpoints must be accessed with a `GET` HTTP request. | `/api/entrypoints` | Lists all the entry points information. | | `/api/entrypoints/{name}` | Returns the information of the entry point specified by `name`. | | `/api/overview` | Returns statistic information about HTTP, TCP and about enabled features and providers. | +| `/api/support-dump` | Returns an archive that contains the anonymized static configuration and the runtime configuration. | | `/api/rawdata` | Returns information about dynamic configurations, errors, status and dependency relations. | | `/api/version` | Returns information about Traefik version. | | `/debug/vars` | See the [expvar](https://golang.org/pkg/expvar/) Go documentation. | @@ -203,14 +218,16 @@ All the following endpoints must be accessed with a `GET` HTTP request. ## Dashboard -The dashboard is available at the same location as the API, but by default on the path `/dashboard/`. +The dashboard is available by default on the path `/dashboard/`. !!! note - The trailing slash `/` in `/dashboard/` is mandatory. This limitation can be mitigated using the the [RedirectRegex Middleware](../../middlewares/http/redirectregex.md). - - There is also a redirect from the path `/` to `/dashboard/`, but you should not rely on this behavior, as it is subject to change and may complicate routing rules. + - There is also a redirect from the path `/` to `/dashboard/`. -To securely access the dashboard, you need to define a routing configuration within Traefik. This involves setting up a router attached to the service `api@internal`, which allows you to: +As mentioned above in the [Security](#security) section, it is important to secure access to both the dashboard and the API. +You need to define a routing configuration within Traefik. +This involves setting up a router attached to the service `api@internal`, which allows you to: - Implement security features using [middlewares](../../middlewares/overview.md), such as authentication ([basicAuth](../../middlewares/http/basicauth.md), [digestAuth](../../middlewares/http/digestauth.md), [forwardAuth](../../middlewares/http/forwardauth.md)) or [allowlisting](../../middlewares/http/ipallowlist.md). @@ -238,4 +255,4 @@ rule = "PathPrefix(`/api`) || PathPrefix(`/dashboard`)" rule = "Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))" ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/install-configuration/configuration-options.md b/docs/content/reference/install-configuration/configuration-options.md index 596c69283..3fd9908b0 100644 --- a/docs/content/reference/install-configuration/configuration-options.md +++ b/docs/content/reference/install-configuration/configuration-options.md @@ -51,6 +51,7 @@ THIS FILE MUST NOT BE EDITED BY HAND | certificatesresolvers._name_.acme.certificatesduration | Certificates' duration in hours. | 2160 | | certificatesresolvers._name_.acme.clientresponseheadertimeout | Timeout for receiving the response headers when communicating with the ACME server. | 30 | | certificatesresolvers._name_.acme.clienttimeout | Timeout for a complete HTTP transaction with the ACME server. | 120 | +| certificatesresolvers._name_.acme.disablecommonname | Disable the common name in the CSR. | false | | certificatesresolvers._name_.acme.dnschallenge | Activate DNS-01 Challenge. | false | | certificatesresolvers._name_.acme.dnschallenge.delaybeforecheck | (Deprecated) Assume DNS propagates after a delay in seconds rather than finding and querying nameservers. | 0 | | certificatesresolvers._name_.acme.dnschallenge.disablepropagationcheck | (Deprecated) Disable the DNS propagation checks before notifying ACME that the DNS challenge is ready. [not recommended] | false | @@ -72,7 +73,8 @@ THIS FILE MUST NOT BE EDITED BY HAND | certificatesresolvers._name_.acme.preferredchain | Preferred chain to use. | | | certificatesresolvers._name_.acme.profile | Certificate profile to use. | | | certificatesresolvers._name_.acme.storage | Storage to use. | acme.json | -| certificatesresolvers._name_.acme.tlschallenge | Activate TLS-ALPN-01 Challenge. | true | +| certificatesresolvers._name_.acme.tlschallenge | Activate TLS-ALPN-01 Challenge. | false | +| certificatesresolvers._name_.acme.tlschallenge.delay | Delay between the creation of the challenge and the validation. | 0 | | certificatesresolvers._name_.tailscale | Enables Tailscale certificate resolution. | true | | core.defaultrulesyntax | Defines the rule parser default syntax (v2 or v3) | v3 | | entrypoints._name_ | Entry points definition. | false | @@ -83,6 +85,13 @@ THIS FILE MUST NOT BE EDITED BY HAND | entrypoints._name_.forwardedheaders.insecure | Trust all forwarded headers. | false | | entrypoints._name_.forwardedheaders.trustedips | Trust only forwarded headers from selected IPs. | | | entrypoints._name_.http | HTTP configuration. | | +| entrypoints._name_.http.encodedcharacters.allowencodedbackslash | Defines whether requests with encoded back slash characters in the path are allowed. | true | +| entrypoints._name_.http.encodedcharacters.allowencodedhash | Defines whether requests with encoded hash characters in the path are allowed. | true | +| entrypoints._name_.http.encodedcharacters.allowencodednullcharacter | Defines whether requests with encoded null characters in the path are allowed. | true | +| entrypoints._name_.http.encodedcharacters.allowencodedpercent | Defines whether requests with encoded percent characters in the path are allowed. | true | +| entrypoints._name_.http.encodedcharacters.allowencodedquestionmark | Defines whether requests with encoded question mark characters in the path are allowed. | true | +| entrypoints._name_.http.encodedcharacters.allowencodedsemicolon | Defines whether requests with encoded semicolon characters in the path are allowed. | true | +| entrypoints._name_.http.encodedcharacters.allowencodedslash | Defines whether requests with encoded slash characters in the path are allowed. | true | | entrypoints._name_.http.encodequerysemicolons | Defines whether request query semicolons should be URLEncoded. | false | | entrypoints._name_.http.maxheaderbytes | Maximum size of request headers in bytes. | 1048576 | | entrypoints._name_.http.middlewares | Default middlewares for the routers linked to the entry point. | | @@ -98,6 +107,8 @@ THIS FILE MUST NOT BE EDITED BY HAND | entrypoints._name_.http.tls.domains[0].sans | Subject alternative names. | | | entrypoints._name_.http.tls.options | Default TLS options for the routers linked to the entry point. | | | entrypoints._name_.http2.maxconcurrentstreams | Specifies the number of concurrent streams per connection that each client is allowed to initiate. | 250 | +| entrypoints._name_.http2.maxdecoderheadertablesize | Specifies the maximum size of the HTTP2 HPACK header table on the decoding (receiving from client) side. | 4096 | +| entrypoints._name_.http2.maxencoderheadertablesize | Specifies the maximum size of the HTTP2 HPACK header table on the encoding (sending to client) side. | 4096 | | entrypoints._name_.http3 | HTTP/3 configuration. | false | | entrypoints._name_.http3.advertisedport | UDP port to advertise, on which HTTP/3 is available. | 0 | | entrypoints._name_.observability.accesslogs | Enables access-logs for this entryPoint. | true | @@ -119,6 +130,7 @@ THIS FILE MUST NOT BE EDITED BY HAND | experimental.abortonpluginfailure | Defines whether all plugins must be loaded successfully for Traefik to start. | false | | experimental.fastproxy | Enables the FastProxy implementation. | false | | experimental.fastproxy.debug | Enable debug mode for the FastProxy implementation. | false | +| experimental.knative | Allow the Knative provider usage. | false | | experimental.kubernetesgateway | (Deprecated) Allow the Kubernetes gateway api provider usage. | false | | experimental.kubernetesingressnginx | Allow the Kubernetes Ingress NGINX provider usage. | false | | experimental.localplugins._name_ | Local plugins configuration. | false | @@ -126,14 +138,14 @@ THIS FILE MUST NOT BE EDITED BY HAND | experimental.localplugins._name_.settings | Plugin's settings (works only for wasm plugins). | | | experimental.localplugins._name_.settings.envs | Environment variables to forward to the wasm guest. | | | experimental.localplugins._name_.settings.mounts | Directory to mount to the wasm guest. | | -| experimental.localplugins._name_.settings.useunsafe | Allow the plugin to use unsafe package. | false | +| experimental.localplugins._name_.settings.useunsafe | Allow the plugin to use unsafe and syscall packages. | false | | experimental.otlplogs | Enables the OpenTelemetry logs integration. | false | | experimental.plugins._name_.hash | plugin's hash to validate' | | | experimental.plugins._name_.modulename | plugin's module name. | | | experimental.plugins._name_.settings | Plugin's settings (works only for wasm plugins). | | | experimental.plugins._name_.settings.envs | Environment variables to forward to the wasm guest. | | | experimental.plugins._name_.settings.mounts | Directory to mount to the wasm guest. | | -| experimental.plugins._name_.settings.useunsafe | Allow the plugin to use unsafe package. | false | +| experimental.plugins._name_.settings.useunsafe | Allow the plugin to use unsafe and syscall packages. | false | | experimental.plugins._name_.version | plugin's version. | | | global.checknewversion | Periodically check if a new version has been released. | true | | global.sendanonymoususage | Periodically send anonymous usage statistics. If the option is not specified, it will be disabled by default. | false | @@ -320,6 +332,21 @@ THIS FILE MUST NOT BE EDITED BY HAND | providers.http.tls.cert | TLS cert | | | providers.http.tls.insecureskipverify | TLS insecure skip verify | false | | providers.http.tls.key | TLS key | | +| providers.knative | Enables Knative provider. | false | +| providers.knative.certauthfilepath | Kubernetes certificate authority file path (not needed for in-cluster client). | | +| providers.knative.endpoint | Kubernetes server endpoint (required for external cluster client). | | +| providers.knative.labelselector | Kubernetes label selector to use. | | +| providers.knative.namespaces | Kubernetes namespaces. | | +| providers.knative.privateentrypoints | Entrypoint names used to expose the Ingress privately. If empty local Ingresses are skipped. | | +| providers.knative.privateservice | Kubernetes service used to expose the networking controller privately. | | +| providers.knative.privateservice.name | Name of the Kubernetes service. | | +| providers.knative.privateservice.namespace | Namespace of the Kubernetes service. | | +| providers.knative.publicentrypoints | Entrypoint names used to expose the Ingress publicly. If empty an Ingress is exposed on all entrypoints. | | +| providers.knative.publicservice | Kubernetes service used to expose the networking controller publicly. | | +| providers.knative.publicservice.name | Name of the Kubernetes service. | | +| providers.knative.publicservice.namespace | Namespace of the Kubernetes service. | | +| providers.knative.throttleduration | Ingress refresh throttle duration | 0 | +| providers.knative.token | Kubernetes bearer token (not needed for in-cluster client). | | | providers.kubernetescrd | Enables Kubernetes CRD provider. | false | | providers.kubernetescrd.allowcrossnamespace | Allow cross namespace resource reference. | false | | providers.kubernetescrd.allowemptyservices | Allow the creation of services without endpoints. | false | diff --git a/docs/content/reference/install-configuration/entrypoints.md b/docs/content/reference/install-configuration/entrypoints.md index 9a41e3bc3..6b1809932 100644 --- a/docs/content/reference/install-configuration/entrypoints.md +++ b/docs/content/reference/install-configuration/entrypoints.md @@ -84,8 +84,8 @@ additionalArguments: ## Configuration Options -| Field | Description | Default | Required | -|:----------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------|:---------| +| Field | Description | Default | Required | +|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------------|:---------| | `address` | Define the port, and optionally the hostname, on which to listen for incoming connections and packets.
It also defines the protocol to use (TCP or UDP).
If no protocol is specified, the default is TCP. The format is:`[host]:port[/tcp\|/udp] | - | Yes | | `asDefault` | Mark the `entryPoint` to be in the list of default `entryPoints`.
`entryPoints`in this list are used (by default) on HTTP and TCP routers that do not define their own `entryPoints` option.
More information [here](#asdefault). | false | No | | `forwardedHeaders.trustedIPs` | Set the IPs or CIDR from where Traefik trusts the forwarded headers information (`X-Forwarded-*`). | - | No | @@ -94,14 +94,25 @@ additionalArguments: | `http.redirections.`
`entryPoint.scheme`
| The target scheme to use for (permanent) redirection of all incoming requests. | https | No | | `http.redirections.`
`entryPoint.permanent`
| Enable permanent redirecting of all incoming requests on an entry point to another one changing the scheme.
The target element, it can be an entry point name (ex: `websecure`), or a port (`:443`). | false | No | | `http.redirections.`
`entryPoint.priority`
| Default priority applied to the routers attached to the `entryPoint`. | MaxInt32-1 (2147483646) | No | +| `http.encodedCharacters` | Defines which encoded characters are allowed in the request path. More information [here](#encoded-characters). | false | No | +| `http.encodedCharacters.`
`allowEncodedSlash`
| Defines whether requests with encoded slash characters in the path are allowed. | true | No | +| `http.encodedCharacters.`
`allowEncodedBackSlash`
| Defines whether requests with encoded back slash characters in the path are allowed. | true | No | +| `http.encodedCharacters.`
`allowEncodedNullCharacter`
| Defines whether requests with encoded null characters in the path are allowed. | true | No | +| `http.encodedCharacters.`
`allowEncodedSemicolon`
| Defines whether requests with encoded semicolon characters in the path are allowed. | true | No | +| `http.encodedCharacters.`
`allowEncodedPercent`
| Defines whether requests with encoded percent characters in the path are allowed. | true | No | +| `http.encodedCharacters.`
`allowEncodedQuestionMark`
| Defines whether requests with encoded question mark characters in the path are allowed. | true | No | +| `http.encodedCharacters.`
`allowEncodedHash`
| Defines whether requests with encoded hash characters in the path are allowed. | true | No | | `http.encodeQuerySemicolons` | Enable query semicolons encoding.
Use this option to avoid non-encoded semicolons to be interpreted as query parameter separators by Traefik.
When using this option, the non-encoded semicolons characters in query will be transmitted encoded to the backend.
More information [here](#encodequerysemicolons). | false | No | | `http.sanitizePath` | Defines whether to enable the request path sanitization.
More information [here](#sanitizepath). | false | No | +| `http.maxHeaderBytes` | Set the maximum size of request headers in bytes. | 1048576 | No | | `http.middlewares` | Set the list of middlewares that are prepended by default to the list of middlewares of each router associated to the named entry point.
More information [here](#httpmiddlewares). | - | No | | `http.tls` | Enable TLS on every router attached to the `entryPoint`.
If no certificate are set, a default self-signed certificate is generated by Traefik.
We recommend to not use self signed certificates in production. | - | No | | `http.tls.options` | Apply TLS options on every router attached to the `entryPoint`.
The TLS options can be overidden per router.
More information in the [dedicated section](../../routing/providers/kubernetes-crd.md#kind-tlsoption). | - | No | | `http.tls.certResolver` | Apply a certificate resolver on every router attached to the `entryPoint`.
The TLS options can be overidden per router.
More information in the [dedicated section](../install-configuration/tls/certificate-resolvers/overview.md). | - | No | | `http2.maxConcurrentStreams` | Set the number of concurrent streams per connection that each client is allowed to initiate.
The value must be greater than zero. | 250 | No | -| `http3` | Enable HTTP/3 protocol on the `entryPoint`.
HTTP/3 requires a TCP `entryPoint`. as HTTP/3 always starts as a TCP connection that then gets upgraded to UDP. In most scenarios, this `entryPoint` is the same as the one used for TLS traffic.
More information [here](#http3). | - | No | +| `http2.maxDecoderHeaderTableSize` | Set the maximum size of the decoder header compression table. This controls the maximum size of the header cache that the server is willing to maintain so the client does not need to repeatedly send the same header across requests in the same http2 connection.
This value is only a maximum, the other end of the connection can use a lower size. | 4096 | No | +| `http2.maxEncoderHeaderTableSize` | Set the maximum size of the encoder header compression table. This controls the maximum size of the header cache that the server is willing to maintain when sending headers to the client, allowing the server to reduce the amount of duplicate headers it is sending in responses.
This value is only a maximum, the other end of the connection can use a lower size. | 4096 | No | +| `http3` | Enable HTTP/3 protocol on the `entryPoint`.
HTTP/3 requires a TCP `entryPoint`. as HTTP/3 always starts as a TCP connection that then gets upgraded to UDP. In most scenarios, this `entryPoint` is the same as the one used for TLS traffic.
More information [here](#http3). | - | No | | `http3.advertisedPort` | Set the UDP port to advertise as the HTTP/3 authority.
It defaults to the entryPoint's address port.
It can be used to override the authority in the `alt-svc` header, for example if the public facing port is different from where Traefik is listening. | - | No | | `observability.accessLogs` | Defines whether a router attached to this EntryPoint produces access-logs by default. Nonetheless, a router defining its own observability configuration will opt-out from this default. | true | No | | `observability.metrics` | Defines whether a router attached to this EntryPoint produces metrics by default. Nonetheless, a router defining its own observability configuration will opt-out from this default. | true | No | @@ -206,6 +217,33 @@ it can lead to unsafe routing when the `sanitizePath` option is set to `false`. | false | /./foo/../bar// | /./foo/../bar// | | true | /./foo/../bar// | /bar/ | +### Encoded Characters + +You can configure Traefik to control the handling of encoded characters in request paths for security purposes. +By default, Traefik do not reject requests with path containing certain encoded characters that could be used in path traversal or other security attacks. + +!!! info + + This check is not done against the request query parameters, + but only against the request path as defined in [RFC3986 section-3](https://datatracker.ietf.org/doc/html/rfc3986#section-3). + +!!! info "Security Considerations" + + When your backend is not fully compliant with [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) and notably decode encoded reserved characters in the requets path, + it is recommended to set these options to `false` to avoid split-view situation and helps prevent path traversal attacks or other malicious attempts to bypass security controls. + +Here is the list of the encoded characters that are rejected by default: + +| Encoded Character | Character | +|------------------------------------------------------------------------------------|-------------------------| +| `%2f` or `%2F` | `/` (slash) | +| `%5c` or `%5C` | `\` (backslash) | +| `%00` | `NULL` (null character) | +| `%3b` or `%3B` | `;` (semicolon) | +| `%25` | `%` (percent) | +| `%3f` or `%3F` | `?` (question mark) | +| `%23` | `#` (hash) | + ### HTTP3 As HTTP/3 actually uses UDP, when Traefik is configured with a TCP `entryPoint` diff --git a/docs/content/reference/install-configuration/observability/logs-and-accesslogs.md b/docs/content/reference/install-configuration/observability/logs-and-accesslogs.md index 7fab52d14..147826fc0 100644 --- a/docs/content/reference/install-configuration/observability/logs-and-accesslogs.md +++ b/docs/content/reference/install-configuration/observability/logs-and-accesslogs.md @@ -397,7 +397,7 @@ Example utilizing Docker Compose: ```yaml services: traefik: - image: traefik:v3.5 + image: traefik:v3.6 environment: - TZ=US/Alaska command: @@ -409,4 +409,4 @@ services: - /var/run/docker.sock:/var/run/docker.sock ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/install-configuration/providers/docker.md b/docs/content/reference/install-configuration/providers/docker.md index eff2a9297..47354ae91 100644 --- a/docs/content/reference/install-configuration/providers/docker.md +++ b/docs/content/reference/install-configuration/providers/docker.md @@ -420,11 +420,11 @@ You can specify which Docker API Endpoint to use with the directive [`endpoint`] - [Traefik and Docker: A Discussion with Docker Captain, Bret Fisher](https://blog.traefik.io/traefik-and-docker-a-discussion-with-docker-captain-bret-fisher-7f0b9a54ff88) - [KubeCon EU 2018 Keynote, Running with Scissors, from Liz Rice](https://www.youtube.com/watch?v=ltrV-Qmh3oY) - [Don't expose the Docker socket (not even to a container)](https://www.lvh.io/posts/dont-expose-the-docker-socket-not-even-to-a-container/) - - [A thread on Stack Overflow about sharing the `/var/run/docker.sock` file](https://news.ycombinator.com/item?id=17983623) + - [A thread on Hacker News about sharing the `/var/run/docker.sock` file](https://news.ycombinator.com/item?id=17983623) - [To DinD or not to DinD](https://blog.loof.fr/2018/01/to-dind-or-not-do-dind.html) - [Traefik issue GH-4174 about security with Docker socket](https://github.com/traefik/traefik/issues/4174) - [Inspecting Docker Activity with Socat](https://developers.redhat.com/blog/2015/02/25/inspecting-docker-activity-with-socat/) - [Letting Traefik run on Worker Nodes](https://blog.mikesir87.io/2018/07/letting-traefik-run-on-worker-nodes/) - [Docker Socket Proxy from Tecnativa](https://github.com/Tecnativa/docker-socket-proxy) -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/install-configuration/providers/kubernetes/knative.md b/docs/content/reference/install-configuration/providers/kubernetes/knative.md new file mode 100644 index 000000000..810bf2335 --- /dev/null +++ b/docs/content/reference/install-configuration/providers/kubernetes/knative.md @@ -0,0 +1,142 @@ +--- +title: "Traefik Knative Documentation" +description: "Learn how to use the Knative as a provider for configuration discovery in Traefik Proxy. Read the technical documentation." +--- + +# Traefik & Knative + +The Traefik Knative provider integrates with Knative Serving to provide advanced traffic management and routing capabilities for serverless applications. + +[Knative](https://knative.dev) is a Kubernetes-based platform that enables serverless workloads with features like scale-to-zero, +automatic scaling, and revision management. + +The provider watches Knative `Ingress` resources and automatically configures Traefik routing rules, +enabling seamless integration between Traefik's networking capabilities and Knative's serverless platform. + +## Requirements + +{% include-markdown "includes/kubernetes-requirements.md" %} + +1. Install/update the Knative CRDs. + + ```bash + kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.19.0/serving-crds.yaml + ``` + +2. Install the Knative Serving core components. + + ```bash + kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.19.0/serving-core.yaml + ``` + +3. Update the config-network configuration to use the Traefik ingress class. + + ```bash + kubectl patch configmap/config-network \ + -n knative-serving \ + --type merge \ + -p '{"data":{"ingress.class":"traefik.ingress.networking.knative.dev"}}' + ``` + +4. Add a custom domain to your Knative configuration (Optional). + + ```bash + kubectl patch configmap config-domain \ + -n knative-serving \ + --type='merge' \ + -p='{"data":{"example.com":""}}' + ``` + +5. Install/update the Traefik [RBAC](../../../dynamic-configuration/kubernetes-knative-rbac.yml). + + ```bash + kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/reference/dynamic-configuration/kubernetes-knative-rbac.yml + ``` + +## Configuration Example + +As this provider is an experimental feature, it needs to be enabled in the experimental and in the provider sections of the configuration. +You can enable the Knative provider as detailed below: + +```yaml tab="File (YAML)" +experimental: + knative: true + +providers: + knative: {} +``` + +```toml tab="File (TOML)" +[experimental.knative] + +[providers.knative] +``` + +```bash tab="CLI" +--experimental.knative=true +--providers.knative=true +``` + +The Knative provider uses the Knative API to retrieve its routing configuration. +The provider then watches for incoming Knative events and derives the corresponding dynamic configuration from it. + +## Configuration Options + + + +| Field | Description | Default | Required | +|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| +| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | +| providers.knative.endpoint | Server endpoint URL.
More information [here](#endpoint). | | +| providers.knative.token | Bearer token used for the Kubernetes client configuration. | | +| providers.knative.certauthfilepath | Path to the certificate authority file.
Used for the Kubernetes client configuration. | | +| providers.knative.namespaces | Array of namespaces to watch.
If left empty, watch all namespaces. | | +| providers.knative.labelselector | Allow filtering Knative Ingress objects using label selectors. | | +| providers.knative.throttleduration | Minimum amount of time to wait between two Kubernetes events before producing a new configuration.
This prevents a Kubernetes cluster that updates many times per second from continuously changing your Traefik configuration.
If empty, every event is caught. | 0 | +| providers.knative.privateentrypoints | Entrypoint names used to expose the Ingress privately. If empty local Ingresses are skipped. | | +| providers.knative.privateservice | Kubernetes service used to expose the networking controller privately. | | +| providers.knative.privateservice.name | Name of the private Kubernetes service. | | +| providers.knative.privateservice.namespace | Namespace of the private Kubernetes service. | | +| providers.knative.publicentrypoints | Entrypoint names used to expose the Ingress publicly. If empty an Ingress is exposed on all entrypoints. | | +| providers.knative.publicservice | Kubernetes service used to expose the networking controller publicly. | | +| providers.knative.publicservice.name | Name of the public Kubernetes service. | | +| providers.knative.publicservice.namespace | Namespace of the public Kubernetes service. | | + + + +### `endpoint` + +The Kubernetes server endpoint URL. + +When deployed into Kubernetes, Traefik reads the environment variables `KUBERNETES_SERVICE_HOST` and `KUBERNETES_SERVICE_PORT` or `KUBECONFIG` to construct the endpoint. + +The access token is looked up in `/var/run/secrets/kubernetes.io/serviceaccount/token` and the SSL CA certificate in `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt`. +Both are mounted automatically when deployed inside Kubernetes. + +The endpoint may be specified to override the environment variable values inside a cluster. + +When the environment variables are not found, Traefik tries to connect to the Knative API server with an external-cluster client. +In this case, the endpoint is required. +Specifically, it may be set to the URL used by `kubectl proxy` to connect to a Knative cluster using the granted authentication and authorization of the associated kubeconfig. + +```yaml tab="File (YAML)" +providers: + knative: + endpoint: "http://localhost:8080" + # ... +``` + +```toml tab="File (TOML)" +[providers.knative] + endpoint = "http://localhost:8080" + # ... +``` + +```bash tab="CLI" +--providers.knative.endpoint=http://localhost:8080 +``` +## Routing Configuration + +See the dedicated section in [routing](../../../routing-configuration/kubernetes/knative.md). + +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-crd.md b/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-crd.md index 50ea7ff2d..d89e28bd2 100644 --- a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-crd.md +++ b/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-crd.md @@ -20,10 +20,10 @@ When you install Traefik without using the Helm Chart, or when you are upgrading ```bash # Install Traefik Resource Definitions: -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml # Install RBAC for Traefik: -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml ``` ## Configuration Example @@ -130,4 +130,4 @@ See the dedicated section in [routing](../../../../routing/providers/kubernetes- For additional information, refer to the [full example](../../../../user-guides/crd-acme/index.md) with Let's Encrypt. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-gateway.md b/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-gateway.md index bdbf04428..2a59a8300 100644 --- a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-gateway.md +++ b/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-gateway.md @@ -8,33 +8,33 @@ description: "Learn how to use the Kubernetes Gateway API as a provider for conf The Kubernetes Gateway provider is a Traefik implementation of the [Gateway API](https://gateway-api.sigs.k8s.io/) specification from the Kubernetes Special Interest Groups (SIGs). -This provider supports Standard version [v1.3.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.3.0) of the Gateway API specification. +This provider supports Standard version [v1.4.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.4.0) of the Gateway API specification. -It fully supports all HTTP core and some extended features, as well as the `TCPRoute` and `TLSRoute` resources from the [Experimental channel](https://gateway-api.sigs.k8s.io/concepts/versioning/?h=#release-channels). +It fully supports all `HTTPRoute` core and some extended features, like `BackendTLSPolicy`, and `GRPCRoute` resources from the [Standard channel](https://gateway-api.sigs.k8s.io/concepts/versioning/?h=#release-channels), as well as `TCPRoute`, and `TLSRoute` resources from the [Experimental channel](https://gateway-api.sigs.k8s.io/concepts/versioning/?h=#release-channels). -For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.3.0/traefik-traefik). +For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.4.0/traefik-traefik). !!! info "Using The Helm Chart" When using the Traefik [Helm Chart](../../../../getting-started/install-traefik.md#use-the-helm-chart), the CRDs (Custom Resource Definitions) and RBAC (Role-Based Access Control) are automatically managed for you. - The only remaining task is to enable the `kubernetesGateway` in the chart [values](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml#L130). + The only remaining task is to enable the `kubernetesGateway` in the chart [values](https://github.com/traefik/traefik-helm-chart/blob/master/traefik/values.yaml#L323). ## Requirements -{!kubernetes-requirements.md!} +{% include-markdown "includes/kubernetes-requirements.md" %} 1. Install/update the Kubernetes Gateway API CRDs. ```bash # Install Gateway API CRDs from the Standard channel. - kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml + kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/standard-install.yaml ``` 2. Install/update the Traefik [RBAC](../../../dynamic-configuration/kubernetes-gateway-rbac.yml). ```bash # Install Traefik RBACs. - kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml + kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/reference/dynamic-configuration/kubernetes-gateway-rbac.yml ``` ## Configuration Example @@ -137,4 +137,4 @@ See the dedicated section in [routing](../../../../routing/providers/kubernetes- and the dedicated [routing section](../../../../routing/providers/kubernetes-gateway.md) in the Traefik documentation. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-ingress-nginx.md b/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-ingress-nginx.md index 2d9e93c1e..aa42c8acb 100644 --- a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-ingress-nginx.md +++ b/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-ingress-nginx.md @@ -3,67 +3,134 @@ title: "Traefik Kubernetes Ingress NGINX Documentation" description: "Understand the requirements, routing configuration, and how to set up the Kubernetes Ingress NGINX provider. Read the technical documentation." --- -# Traefik & Ingresses with NGINX Annotations +# Traefik & Ingresses with NGINX Annotations -The experimental Traefik Kubernetes Ingress NGINX provider is a Kubernetes Ingress controller; i.e, -it manages access to cluster services by supporting the [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) specification. -It also supports some of the [ingress-nginx](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/) annotations on ingresses to customize their behavior. +This provider is a Kubernetes Ingress controller that manages access to cluster services by supporting the [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/) specification. +It also supports many of the [ingress-nginx](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/) annotations on Ingresses, enabling teams to migrate from NGINX Ingress Controller to Traefik with minimal configuration changes. -!!! warning "Ingress Discovery" +!!! warning "NGINX Ingress Controller Retirement" - The Kubernetes Ingress NGINX provider is discovering by default all Ingresses in the cluster, - which may lead to duplicated routers if you are also using the Kubernetes Ingress provider. - We recommend to use IngressClass for the Ingresses you want to be handled by this provider, - or to use the `watchNamespace` or `watchNamespaceSelector` options to limit the discovery of Ingresses to a specific namespace or set of namespaces. + The Kubernetes NGINX Ingress Controller project has announced its retirement in **March 2026** and will no longer receive updates or security patches. + Traefik provides a migration path by supporting NGINX annotations, allowing you to transition your workloads without rewriting all your Ingress configurations. + + **→ See the [NGINX to Traefik Migration Guide](../../../../migrate/nginx-to-traefik.md) for step-by-step instructions.** + + For more information about the NGINX Ingress Controller retirement, see the [official Kubernetes blog announcement](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement). + +## Requirements + +When you install Traefik without using the Helm Chart, +ensure that you add/update the [RBAC](https://kubernetes.io/docs/reference/access-authn-authz/rbac/) for the Traefik Kubernetes Ingress NGINX provider. + +!!! note "Additional RBAC for Namespace Selector" + + When using the `watchNamespaceSelector` option, Traefik requires permissions to list and watch namespaces. + These permissions are included in the RBAC configuration below. + +```bash +# Install RBAC for Traefik Ingress NGINX provider: +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/reference/dynamic-configuration/kubernetes-ingress-nginx-rbac.yml +``` + +## Ingress Discovery + +This provider discovers all Ingresses in the cluster by default, which may lead to duplicated routers if you are also using the standard Kubernetes Ingress provider. + +**Best Practices:** + +- Use IngressClass to specify which Ingresses should be handled by this provider +- Configure `watchNamespace` to limit discovery to specific namespaces +- Use `watchNamespaceSelector` to target Ingresses based on namespace labels ## Configuration Example -As this provider is an experimental feature, it needs to be enabled in the experimental and in the provider sections of the configuration. You can enable the Kubernetes Ingress NGINX provider as detailed below: ```yaml tab="File (YAML)" -experimental: - kubernetesIngressNGINX: true - providers: - kubernetesIngressNGINX: {} + kubernetesIngressNGINX: + # Namespace discovery + watchNamespace: "default" + # OR use namespace selector (mutually exclusive with watchNamespace) + # watchNamespaceSelector: "environment=production" + + # IngressClass configuration + ingressClass: "nginx" + controllerClass: "k8s.io/ingress-nginx" + watchIngressWithoutClass: false + ingressClassByName: false ``` ```toml tab="File (TOML)" -[experimental.kubernetesIngressNGINX] - [providers.kubernetesIngressNGINX] + # Namespace discovery + watchNamespace = "default" + # OR use namespace selector (mutually exclusive with watchNamespace) + # watchNamespaceSelector = "environment=production" + + # IngressClass configuration + ingressClass = "nginx" + controllerClass = "k8s.io/ingress-nginx" + watchIngressWithoutClass = false + ingressClassByName = false ``` ```bash tab="CLI" ---experimental.kubernetesingressnginx=true --providers.kubernetesingressnginx=true +--providers.kubernetesingressnginx.watchnamespace=default +--providers.kubernetesingressnginx.ingressclass=nginx +--providers.kubernetesingressnginx.controllerclass=k8s.io/ingress-nginx +--providers.kubernetesingressnginx.watchingresswithoutclass=false +--providers.kubernetesingressnginx.ingressclassbyname=false ``` -The provider then watches for incoming ingresses events, such as the example below, -and derives the corresponding dynamic configuration from it, -which in turn creates the resulting routers, services, handlers, etc. +```yaml tab="Helm Chart Values" +providers: + kubernetesIngressNginx: + # -- Enable Kubernetes Ingress NGINX provider + enabled: true + + # Namespace discovery + # -- Namespace the controller watches for updates to Kubernetes objects + # When using rbac.namespaced, it will watch helm release namespace and namespaces listed in this array + namespaces: + - default + # OR use namespace selector (mutually exclusive with namespaces) + # namespaceSelector: "environment=production" + + # IngressClass configuration + # -- Name of the ingress class this controller satisfies + ingressClass: "nginx" + # -- Ingress Class Controller value this controller satisfies + controllerClass: "k8s.io/ingress-nginx" + # -- Define if Ingress Controller should also watch for Ingresses without an IngressClass or the annotation specified + watchIngressWithoutClass: false + # -- Define if Ingress Controller should watch for Ingress Class by Name together with Controller Class + ingressClassByName: false +``` + +This provider watches for incoming Ingress events and automatically translates NGINX annotations into Traefik's dynamic configuration, creating the corresponding routers, services, middlewares, and other components needed to route traffic to your cluster services. ## Configuration Options -| Field | Description | Default | Required | -|:------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| -| `providers.providersThrottleDuration` | Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | -| `providers.kubernetesIngressNGINX.endpoint` | Server endpoint URL.
More information [here](#endpoint). | "" | No | -| `providers.kubernetesIngressNGINX.token` | Bearer token used for the Kubernetes client configuration. | "" | No | -| `providers.kubernetesIngressNGINX.certAuthFilePath` | Path to the certificate authority file.
Used for the Kubernetes client configuration. | "" | No | -| `providers.kubernetesIngressNGINX.throttleDuration` | Minimum amount of time to wait between two Kubernetes events before producing a new configuration.
This prevents a Kubernetes cluster that updates many times per second from continuously changing your Traefik configuration.
If empty, every event is caught. | 0s | No | -| `providers.kubernetesIngressNGINX.watchNamespace` | Namespace the controller watches for updates to Kubernetes objects. All namespaces are watched if this parameter is left empty. | "" | No | -| `providers.kubernetesIngressNGINX.watchNamespaceSelector` | Selector selects namespaces the controller watches for updates to Kubernetes objects. | "" | No | -| `providers.kubernetesIngressNGINX.ingressClass` | Name of the ingress class this controller satisfies. | "" | No | -| `providers.kubernetesIngressNGINX.controllerClass` | Ingress Class Controller value this controller satisfies. | "" | No | -| `providers.kubernetesIngressNGINX.watchIngressWithoutClass` | Define if Ingress Controller should also watch for Ingresses without an IngressClass or the annotation specified. | false | No | -| `providers.kubernetesIngressNGINX.ingressClassByName` | Define if Ingress Controller should watch for Ingress Class by Name together with Controller Class. | false | No | -| `providers.kubernetesIngressNGINX.publishService` | Service fronting the Ingress controller. Takes the form namespace/name. | "" | No | -| `providers.kubernetesIngressNGINX.publishStatusAddress` | Customized address (or addresses, separated by comma) to set as the load-balancer status of Ingress objects this controller satisfies. | "" | No | -| `providers.kubernetesIngressNGINX.defaultBackendService` | Service used to serve HTTP requests not matching any known server name (catch-all). Takes the form 'namespace/name'. | "" | No | -| `providers.kubernetesIngressNGINX.disableSvcExternalName` | Disable support for Services of type ExternalName. | false | No | +| Field | Description | Default | Required | +|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------|:--------|:---------| +| `providers.providers`
`ThrottleDuration`
| Minimum amount of time to wait for, after a configuration reload, before taking into account any new configuration refresh event.
If multiple events occur within this time, only the most recent one is taken into account, and all others are discarded.
**This option cannot be set per provider, but the throttling algorithm applies to each of them independently.** | 2s | No | +| `providers.`
`kubernetesIngressNGINX.`
`endpoint`
| Server endpoint URL.
More information [here](#endpoint). | "" | No | +| `providers.`
`kubernetesIngressNGINX.`
`token`
| Bearer token used for the Kubernetes client configuration. | "" | No | +| `providers.`
`kubernetesIngressNGINX.`
`certAuthFilePath`
| Path to the certificate authority file.
Used for the Kubernetes client configuration. | "" | No | +| `providers.`
`kubernetesIngressNGINX.`
`throttleDuration`
| Minimum amount of time to wait between two Kubernetes events before producing a new configuration.
This prevents a Kubernetes cluster that updates many times per second from continuously changing your Traefik configuration.
If empty, every event is caught. | 0s | No | +| `providers.`
`kubernetesIngressNGINX.`
`watchNamespace`
| Namespace the controller watches for updates to Kubernetes objects. All namespaces are watched if this parameter is left empty. | "" | No | +| `providers.`
`kubernetesIngressNGINX.`
`watchNamespaceSelector`
| Selector selects namespaces the controller watches for updates to Kubernetes objects. | "" | No | +| `providers.`
`kubernetesIngressNGINX.`
`ingressClass`
| Name of the ingress class this controller satisfies. | "nginx" | No | +| `providers.`
`kubernetesIngressNGINX.`
`controllerClass`
| Ingress Class Controller value this controller satisfies. | "" | No | +| `providers.`
`kubernetesIngressNGINX.`
`watchIngressWithoutClass`
| Define if Ingress Controller should also watch for Ingresses without an IngressClass or the annotation specified. | false | No | +| `providers.`
`kubernetesIngressNGINX.`
`ingressClassByName`
| Define if Ingress Controller should watch for Ingress Class by Name together with Controller Class. | false | No | +| `providers.`
`kubernetesIngressNGINX.`
`publishService`
| Service fronting the Ingress controller. Takes the form `namespace/name`. | "" | No | +| `providers.`
`kubernetesIngressNGINX.`
`publishStatusAddress`
| Customized address (or addresses, separated by comma) to set as the load-balancer status of Ingress objects this controller satisfies. | "" | No | +| `providers.`
`kubernetesIngressNGINX.`
`defaultBackendService`
| Service used to serve HTTP requests not matching any known server name (catch-all). Takes the form 'namespace/name'. | "" | No | +| `providers.`
`kubernetesIngressNGINX.`
`disableSvcExternalName`
| Disable support for Services of type ExternalName. | false | No | @@ -109,4 +176,4 @@ providers: See the dedicated section in [routing](../../../routing-configuration/kubernetes/ingress-nginx.md). -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-ingress.md b/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-ingress.md index 5ac53b269..b9df965b7 100644 --- a/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-ingress.md +++ b/docs/content/reference/install-configuration/providers/kubernetes/kubernetes-ingress.md @@ -60,7 +60,6 @@ which in turn creates the resulting routers, services, handlers, etc. | `providers.kubernetesIngress.`
`ingressEndpoint.publishedService`
| The Kubernetes service to copy status from.
More information [here](#ingressendpointpublishedservice). | "" | No | | `providers.kubernetesIngress.throttleDuration` | Minimum amount of time to wait between two Kubernetes events before producing a new configuration.
This prevents a Kubernetes cluster that updates many times per second from continuously changing your Traefik configuration.
If empty, every event is caught. | 0s | No | | `providers.kubernetesIngress.allowEmptyServices` | Allows creating a route to reach a service that has no endpoint available.
It allows Traefik to handle the requests and responses targeting this service (applying middleware or observability operations) before returning a `503` HTTP Status. | false | No | -| `providers.kubernetesIngress.allowCrossNamespace` | Allows the `Ingress` to reference resources in namespaces other than theirs. | false | No | | `providers.kubernetesIngress.allowExternalNameServices` | Allows the `Ingress` to reference ExternalName services. | false | No | | `providers.kubernetesIngress.nativeLBByDefault` | Allow using the Kubernetes Service load balancing between the pods instead of the one provided by Traefik for every `Ingress` by default.
It can br overridden in the [`ServerTransport`](../../../../routing/services/index.md#serverstransport). | false | No | | `providers.kubernetesIngress.disableClusterScopeResources` | Prevent from discovering cluster scope resources (`IngressClass` and `Nodes`).
By doing so, it alleviates the requirement of giving Traefik the rights to look up for cluster resources.
Furthermore, Traefik will not handle Ingresses with IngressClass references, therefore such Ingresses will be ignored (please note that annotations are not affected by this option).
This will also prevent from using the `NodePortLB` options on services. | false | No | @@ -116,6 +115,7 @@ depending on the service type: - **ClusterIP:** The ExternalIPs of the service will be propagated to the ingress status. - **NodePort:** The ExternalIP addresses of the nodes in the cluster will be propagated to the ingress status. - **LoadBalancer:** The IPs from the service's `loadBalancer.status` field (which contains the endpoints provided by the load balancer) will be propagated to the ingress status. +- **ExternalName:** The hostname from the service's `spec.externalName` field will be propagated to the ingress status. When using third-party tools such as External-DNS, this option enables the copying of external service IPs to the ingress resources. @@ -149,4 +149,4 @@ many examples of Ingresses definitions are located in the test [examples](https://github.com/traefik/traefik/tree/v3.1/pkg/provider/kubernetes/ingress/fixtures) of the Traefik repository. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/install-configuration/providers/others/file.md b/docs/content/reference/install-configuration/providers/others/file.md index 737c7a65c..1f3304d84 100644 --- a/docs/content/reference/install-configuration/providers/others/file.md +++ b/docs/content/reference/install-configuration/providers/others/file.md @@ -121,4 +121,4 @@ http: As it is very difficult to listen to all file system notifications, Traefik uses [fsnotify](https://github.com/fsnotify/fsnotify). If using a directory with a mounted directory does not fix your issue, please check your file system compatibility with fsnotify. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/install-configuration/providers/overview.md b/docs/content/reference/install-configuration/providers/overview.md index 5253f57a0..cbdbffb4d 100644 --- a/docs/content/reference/install-configuration/providers/overview.md +++ b/docs/content/reference/install-configuration/providers/overview.md @@ -160,4 +160,4 @@ List of providers that support constraints: - [Consul Catalog](./hashicorp/consul-catalog.md#constraints) - [Nomad](./hashicorp/nomad.md#constraints) -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/install-configuration/providers/swarm.md b/docs/content/reference/install-configuration/providers/swarm.md index 8f876dfd2..e1d57e14b 100644 --- a/docs/content/reference/install-configuration/providers/swarm.md +++ b/docs/content/reference/install-configuration/providers/swarm.md @@ -464,4 +464,4 @@ It allows different implementation levels of the [AAA (Authentication, Authoriza - [Letting Traefik run on Worker Nodes](https://blog.mikesir87.io/2018/07/letting-traefik-run-on-worker-nodes/) - [Docker Socket Proxy from Tecnativa](https://github.com/Tecnativa/docker-socket-proxy) -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/install-configuration/tls/certificate-resolvers/acme.md b/docs/content/reference/install-configuration/tls/certificate-resolvers/acme.md index f923e6c72..dbb12e8a8 100644 --- a/docs/content/reference/install-configuration/tls/certificate-resolvers/acme.md +++ b/docs/content/reference/install-configuration/tls/certificate-resolvers/acme.md @@ -73,30 +73,37 @@ certificatesResolvers: ACME certificate resolvers have the following configuration options: -| Field | Description | Default | Required | -|:--------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------|:---------| -| `acme.email` | Email address used for registration. | "" | Yes | -| `acme.caServer` | CA server to use. | https://acme-v02.api.letsencrypt.org/directory | No | -| `acme.preferredChain` | Preferred chain to use. If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used. | "" | No | -| `acme.keyType` | KeyType to use. | "RSA4096" | No | -| `acme.eab` | Enable external account binding. | | No | -| `acme.eab.kid` | Key identifier from External CA. | "" | No | -| `acme.eab.hmacEncoded` | HMAC key from External CA, should be in Base64 URL Encoding without padding format. | "" | No | -| `acme.certificatesDuration` | The certificates' duration in hours, exclusively used to determine renewal dates. | 2160 | No | -| `acme.clientTimeout` | Timeout for HTTP Client used to communicate with the ACME server. | 2m | No | -| `acme.clientResponseHeaderTimeout` | Timeout for response headers for HTTP Client used to communicate with the ACME server. | 30s | No | -| `acme.dnsChallenge` | Enable DNS-01 challenge. More information [here](#dnschallenge). | - | No | -| `acme.dnsChallenge.provider` | DNS provider to use. | "" | No | -| `acme.dnsChallenge.resolvers` | DNS servers to resolve the FQDN authority. | [] | No | -| `acme.dnsChallenge.propagation.delayBeforeChecks` | By default, the provider will verify the TXT DNS challenge record before letting ACME verify. If `delayBeforeCheck` is greater than zero, this check is delayed for the configured duration in seconds. This is Useful if internal networks block external DNS queries. | 0s | No | -| `acme.dnsChallenge.propagation.disableChecks` | Disables the challenge TXT record propagation checks, before notifying ACME that the DNS challenge is ready. Please note that disabling checks can prevent the challenge from succeeding. | false | No | -| `acme.dnsChallenge.propagation.requireAllRNS` | Enables the challenge TXT record to be propagated to all recursive nameservers. If you have disabled authoritative nameservers checks (with `propagation.disableANSChecks`), it is recommended to check all recursive nameservers instead. | false | No | -| `acme.dnsChallenge.propagation.disableANSChecks` | Disables the challenge TXT record propagation checks against authoritative nameservers. This option will skip the propagation check against the nameservers of the authority (SOA). It should be used only if the nameservers of the authority are not reachable. | false | No | -| `acme.httpChallenge` | Enable HTTP-01 challenge. More information [here](#httpchallenge). | | No | -| `acme.httpChallenge.entryPoint` | EntryPoint to use for the HTTP-01 challenges. Must be reachable by Let's Encrypt through port 80 | "" | Yes | -| `acme.httpChallenge.delay` | The delay between the creation of the challenge and the validation. A value lower than or equal to zero means no delay. | 0 | No | -| `acme.tlsChallenge` | Enable TLS-ALPN-01 challenge. Traefik must be reachable by Let's Encrypt through port 443. More information [here](#tlschallenge). | - | No | -| `acme.storage` | File path used for certificates storage. | "acme.json" | Yes | +| Field | Description | Default | Required | +|:------|:------------|:--------|:---------| +| `acme.email` | Email address used for registration. | "" | Yes | +| `acme.caServer` | CA server to use. | https://acme-v02.api.letsencrypt.org/directory | No | +| `acme.preferredChain` | Preferred chain to use. If the CA offers multiple certificate chains, prefer the chain with an issuer matching this Subject Common Name. If no match, the default offered chain will be used. | "" | No | +| `acme.keyType` | KeyType to use. | "RSA4096" | No | +| `acme.disableCommonName` | Disable common name inside CSR and certificates. | false | No | +| `acme.profile` | Certificate profile to use. | "" | No | +| `acme.caCertificates` | Specify the paths to PEM encoded CA Certificates that can be used to authenticate an ACME server with an HTTPS certificate not issued by a CA in the system-wide trusted root list. | [] | No | +| `acme.caSystemCertPool` | Defines if the certificates pool must use a copy of the system cert pool. | false | No | +| `acme.caServerName` | Specify the CA server name that can be used to authenticate an ACME server with an HTTPS certificate not issued by a CA in the system-wide trusted root list. | "" | No | +| `acme.emailAddresses` | CSR email addresses to use. | "" | No | +| `acme.eab` | Enable external account binding. | | No | +| `acme.eab.kid` | Key identifier from External CA. | "" | No | +| `acme.eab.hmacEncoded` | HMAC key from External CA, should be in Base64 URL Encoding without padding format. | "" | No | +| `acme.certificatesDuration` | The certificates' duration in hours, exclusively used to determine renewal dates. | 2160 | No | +| `acme.clientTimeout` | Timeout for HTTP Client used to communicate with the ACME server. | 2m | No | +| `acme.clientResponseHeaderTimeout` | Timeout for response headers for HTTP Client used to communicate with the ACME server. | 30s | No | +| `acme.dnsChallenge` | Enable DNS-01 challenge. More information [here](#dnschallenge). | - | No | +| `acme.dnsChallenge.provider` | DNS provider to use. | "" | No | +| `acme.dnsChallenge.resolvers` | DNS servers to resolve the FQDN authority. | [] | No | +| `acme.dnsChallenge.propagation.delayBeforeChecks` | By default, the provider will verify the TXT DNS challenge record before letting ACME verify. If `delayBeforeCheck` is greater than zero, this check is delayed for the configured duration in seconds. This is Useful if internal networks block external DNS queries. | 0s | No | +| `acme.dnsChallenge.propagation.disableChecks` | Disables the challenge TXT record propagation checks, before notifying ACME that the DNS challenge is ready. Please note that disabling checks can prevent the challenge from succeeding. | false | No | +| `acme.dnsChallenge.propagation.requireAllRNS` | Enables the challenge TXT record to be propagated to all recursive nameservers. If you have disabled authoritative nameservers checks (with `propagation.disableANSChecks`), it is recommended to check all recursive nameservers instead. | false | No | +| `acme.dnsChallenge.propagation.disableANSChecks` | Disables the challenge TXT record propagation checks against authoritative nameservers. This option will skip the propagation check against the nameservers of the authority (SOA). It should be used only if the nameservers of the authority are not reachable. | false | No | +| `acme.httpChallenge` | Enable HTTP-01 challenge. More information [here](#httpchallenge). | | No | +| `acme.httpChallenge.entryPoint` | EntryPoint to use for the HTTP-01 challenges. Must be reachable by Let's Encrypt through port 80 | "" | Yes | +| `acme.httpChallenge.delay` | The delay between the creation of the challenge and the validation. A value lower than or equal to zero means no delay. | 0 | No | +| `acme.tlsChallenge` | Enable TLS-ALPN-01 challenge. Traefik must be reachable by Let's Encrypt through port 443. More information [here](#tlschallenge). | - | No | +| `acme.tlschallenge.delay` | The delay between the creation of the challenge and the validation. A value lower than or equal to zero means no delay. | 0 | No | +| `acme.storage` | File path used for certificates storage. | "acme.json" | Yes | ## Automatic Certificate Renewal @@ -323,4 +330,4 @@ If Let's Encrypt is not reachable, the following certificates will apply: !!! important For new (sub)domains which need Let's Encrypt authentication, the default Traefik certificate will be used until Traefik is restarted. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/install-configuration/tls/certificate-resolvers/overview.md b/docs/content/reference/install-configuration/tls/certificate-resolvers/overview.md index 5652f0982..eccf4a66a 100644 --- a/docs/content/reference/install-configuration/tls/certificate-resolvers/overview.md +++ b/docs/content/reference/install-configuration/tls/certificate-resolvers/overview.md @@ -17,4 +17,4 @@ The Certificates resolvers are defined in the static configuration. Defining a certificate resolver does not imply that routers are going to use it automatically. Each router or entrypoint that is meant to use the resolver must explicitly reference it. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/load-balancing/service.md b/docs/content/reference/routing-configuration/http/load-balancing/service.md index 38f26d077..7b009e2e3 100644 --- a/docs/content/reference/routing-configuration/http/load-balancing/service.md +++ b/docs/content/reference/routing-configuration/http/load-balancing/service.md @@ -30,6 +30,9 @@ http: path: "/health" interval: "10s" timeout: "3s" + passiveHealthcheck: + failureWindow: "3s" + maxFailedAttempts: "3" passHostHeader: true serversTransport: "customTransport@file" responseForwarding: @@ -49,6 +52,10 @@ http: path = "/health" interval = "10s" timeout = "3s" + + [http.services.my-service.loadBalancer.passiveHealthcheck] + failureWindow = "3s" + maxFailedAttempts = "3" passHostHeader = true serversTransport = "customTransport@file" @@ -66,6 +73,8 @@ labels: - "traefik.http.services.my-service.loadBalancer.healthcheck.path=/health" - "traefik.http.services.my-service.loadBalancer.healthcheck.interval=10s" - "traefik.http.services.my-service.loadBalancer.healthcheck.timeout=3s" + - "traefik.http.services.my-service.loadBalancer.passiveHealthcheck.failureWindow=3s" + - "traefik.http.services.my-service.loadBalancer.passiveHealthcheck.maxFailedAttempts=3" - "traefik.http.services.my-service.loadBalancer.passHostHeader=true" - "traefik.http.services.my-service.loadBalancer.serversTransport=customTransport@file" - "traefik.http.services.my-service.loadBalancer.responseForwarding.flushInterval=150ms" @@ -81,6 +90,8 @@ labels: "traefik.http.services.my-service.loadBalancer.healthcheck.path=/health", "traefik.http.services.my-service.loadBalancer.healthcheck.interval=10s", "traefik.http.services.my-service.loadBalancer.healthcheck.timeout=3s", + "traefik.http.services.my-service.loadBalancer.passiveHealthcheck.failureWindow=3s", + "traefik.http.services.my-service.loadBalancer.passiveHealthcheck.maxFailedAttempts=3", "traefik.http.services.my-service.loadBalancer.passHostHeader=true", "traefik.http.services.my-service.loadBalancer.serversTransport=customTransport@file", "traefik.http.services.my-service.loadBalancer.responseForwarding.flushInterval=150ms" @@ -95,6 +106,7 @@ labels: | `servers` | Represents individual backend instances for your service | Yes | | `sticky` | Defines a `Set-Cookie` header is set on the initial response to let the client know which server handles the first response. | No | | `healthcheck` | Configures health check to remove unhealthy servers from the load balancing rotation. | No | +| `passiveHealthcheck` | Configures the passive health check to remove unhealthy servers from the load balancing rotation. | No | | `passHostHeader` | Allows forwarding of the client Host header to server. By default, `passHostHeader` is true. | No | | `serversTransport` | Allows to reference an [HTTP ServersTransport](./serverstransport.md) configuration for the communication between Traefik and your servers. If no `serversTransport` is specified, the `default@internal` will be used. | No | | `responseForwarding` | Configures how Traefik forwards the response from the backend server to the client. | No | @@ -114,7 +126,9 @@ Servers represent individual backend instances for your service. The [service lo #### Health Check -The `healthcheck` option configures health check to remove unhealthy servers from the load balancing rotation. Traefik will consider HTTP(s) servers healthy as long as they return a status code to the health check request (carried out every interval) between `2XX` and `3XX`, or matching the configured status. For gRPC servers, Traefik will consider them healthy as long as they return SERVING to [gRPC health check v1 requests](https://github.com/grpc/grpc/blob/master/doc/health-checking.md). +The `healthcheck` option configures health check to remove unhealthy servers from the load balancing rotation. +Traefik will consider HTTP(s) servers healthy as long as they return a status code to the health check request (carried out every interval) between `2XX` and `3XX`, or matching the configured status. +For gRPC servers, Traefik will consider them healthy as long as they return SERVING to [gRPC health check v1 requests](https://github.com/grpc/grpc/blob/master/doc/health-checking.md). To propagate status changes (e.g. all servers of this service are down) upwards, HealthCheck must also be enabled on the parent(s) of this service. @@ -143,39 +157,39 @@ On subsequent requests, to keep the session alive with the same server, the clie ##### Stickiness on multiple levels - When chaining or mixing load-balancers (e.g. a load-balancer of servers is one of the "children" of a load-balancer of services), for stickiness to work all the way, the option needs to be specified at all required levels. Which means the client needs to send a cookie with as many key/value pairs as there are sticky levels. +When chaining or mixing load-balancers (e.g. a load-balancer of servers is one of the "children" of a load-balancer of services), for stickiness to work all the way, the option needs to be specified at all required levels. Which means the client needs to send a cookie with as many key/value pairs as there are sticky levels. ##### Stickiness & Unhealthy Servers - If the server specified in the cookie becomes unhealthy, the request will be forwarded to a new server (and the cookie will keep track of the new server). +If the server specified in the cookie becomes unhealthy, the request will be forwarded to a new server (and the cookie will keep track of the new server). ##### Cookie Name - The default cookie name is an abbreviation of a sha1 (ex: `_1d52e`). +The default cookie name is an abbreviation of a sha1 (ex: `_1d52e`). ##### MaxAge - By default, the affinity cookie will never expire as the `MaxAge` option is set to zero. +By default, the affinity cookie will never expire as the `MaxAge` option is set to zero. + +This option indicates the number of seconds until the cookie expires. +When set to a negative number, the cookie expires immediately. - This option indicates the number of seconds until the cookie expires. - When set to a negative number, the cookie expires immediately. - ##### Secure & HTTPOnly & SameSite flags - By default, the affinity cookie is created without those flags. - One however can change that through configuration. +By default, the affinity cookie is created without those flags. +One however can change that through configuration. - `SameSite` can be `none`, `lax`, `strict` or empty. +`SameSite` can be `none`, `lax`, `strict` or empty. ##### Domain - The Domain attribute of a cookie specifies the domain for which the cookie is valid. - - By setting the Domain attribute, the cookie can be shared across subdomains (for example, a cookie set for example.com would be accessible to www.example.com, api.example.com, etc.). This is particularly useful in cases where sticky sessions span multiple subdomains, ensuring that the session is maintained even when the client interacts with different parts of the infrastructure. +The Domain attribute of a cookie specifies the domain for which the cookie is valid. + +By setting the Domain attribute, the cookie can be shared across subdomains (for example, a cookie set for example.com would be accessible to www.example.com, api.example.com, etc.). This is particularly useful in cases where sticky sessions span multiple subdomains, ensuring that the session is maintained even when the client interacts with different parts of the infrastructure. ??? example "Adding Stickiness -- Using the [File Provider](../../../install-configuration/providers/others/file.md)" - ```yaml tab="YAML" + ```yaml tab="Structured (YAML)" ## Dynamic configuration http: services: @@ -185,7 +199,7 @@ On subsequent requests, to keep the session alive with the same server, the clie cookie: {} ``` - ```toml tab="TOML" + ```toml tab="Structured (TOML)" ## Dynamic configuration [http.services] [http.services.my-service] @@ -194,7 +208,7 @@ On subsequent requests, to keep the session alive with the same server, the clie ??? example "Adding Stickiness with custom Options -- Using the [File Provider](../../../install-configuration/providers/others/file.md)" - ```yaml tab="YAML" + ```yaml tab="Structured (YAML)" ## Dynamic configuration http: services: @@ -208,7 +222,7 @@ On subsequent requests, to keep the session alive with the same server, the clie httpOnly: true ``` - ```toml tab="TOML" + ```toml tab="Structured (TOML)" ## Dynamic configuration [http.services] [http.services.my-service] @@ -222,7 +236,7 @@ On subsequent requests, to keep the session alive with the same server, the clie ??? example "Setting Stickiness on all the required levels -- Using the [File Provider](../../../install-configuration/providers/others/file.md)" - ```yaml tab="YAML" + ```yaml tab="Structured (YAML)" ## Dynamic configuration http: services: @@ -256,7 +270,7 @@ On subsequent requests, to keep the session alive with the same server, the clie - url: http://127.0.0.1:8084 ``` - ```toml tab="TOML" + ```toml tab="Structured (TOML)" ## Dynamic configuration [http.services] [http.services.wrr1] @@ -288,11 +302,29 @@ On subsequent requests, to keep the session alive with the same server, the clie url = "http://127.0.0.1:8084" ``` - To keep a session open with the same server, the client would then need to specify the two levels within the cookie for each request, e.g. with curl: +To keep a session open with the same server, the client would then need to specify the two levels within the cookie for each request, e.g. with curl: - ``` - curl -b "lvl1=whoami1; lvl2=http://127.0.0.1:8081" http://localhost:8000 - ``` +```bash +curl -b "lvl1=whoami1; lvl2=http://127.0.0.1:8081" http://localhost:8000 +``` + +#### Passive Health Check + +The `passiveHealthcheck` option configures passive health check to remove unhealthy servers from the load balancing rotation. + +Passive health checks rely on real traffic to assess server health. +Traefik forwards requests as usual and evaluates each response or timeout, +incrementing a failure counter whenever a request fails. +If the number of successive failures within a specified time window exceeds the configured threshold, +Traefik will automatically stop routing traffic to that server until it recovers. +A server will be considered healthy again after the configured failure window has passed. + +Below are the available options for the passive health check mechanism: + +| Field | Description | Default | Required | +|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|----------| +| `failureWindow` | Defines the time window during which the failed attempts must occur for the server to be marked as unhealthy. It also defines for how long the server will be considered unhealthy. | 10s | No | +| `maxFailedAttempts` | Defines the number of consecutive failed attempts allowed within the failure window before marking the server as unhealthy. | 1 | No | ## Weighted Round Robin (WRR) @@ -421,13 +453,14 @@ http: [[http.services.appv2.loadBalancer.servers]] url = "http://private-ip-server-2/" ``` + ## P2C Power of two choices algorithm is a load balancing strategy that selects two servers at random and chooses the one with the least number of active requests. ??? example "P2C Load Balancing -- Using the [File Provider](../../../install-configuration/providers/others/file.md)" - ```yaml tab="YAML" + ```yaml tab="Structured (YAML)" ## Dynamic configuration http: services: @@ -440,7 +473,7 @@ Power of two choices algorithm is a load balancing strategy that selects two ser - url: "http://private-ip-server-3/" ``` - ```toml tab="TOML" + ```toml tab="Structured (TOML) " ## Dynamic configuration [http.services] [http.services.my-service.loadBalancer] @@ -453,6 +486,48 @@ Power of two choices algorithm is a load balancing strategy that selects two ser url = "http://private-ip-server-3/" ``` +## Least-Time + +The Least-Time load balancing algorithm selects the server with the lowest average response time (Time To First Byte - TTFB), +combined with the fewest active connections, weighted by server capacity. +This strategy is ideal for heterogeneous backend environments where servers have varying performance characteristics, +different hardware capabilities, or varying network latency. + +The algorithm continuously measures each backend's response time and tracks active connection counts. +When routing a request, +it calculates a score for each healthy server using the formula: `(avg_response_time × (1 + active_connections)) / weight`. +The server with the lowest score receives the request. +When multiple servers have identical scores, +Weighted Round Robin (WRR) with Earliest Deadline First (EDF) scheduling is used as a tie-breaker to ensure fair distribution. + +??? example "Basic Least-Time Load Balancing -- Using the [File Provider](../../../install-configuration/providers/others/file.md)" + + ```yaml tab="Structured (YAML)" + ## Dynamic configuration + http: + services: + my-service: + loadBalancer: + strategy: "leasttime" + servers: + - url: "http://private-ip-server-1/" + - url: "http://private-ip-server-2/" + - url: "http://private-ip-server-3/" + ``` + + ```toml tab="Structured (TOML)" + ## Dynamic configuration + [http.services] + [http.services.my-service.loadBalancer] + strategy = "leasttime" + [[http.services.my-service.loadBalancer.servers]] + url = "http://private-ip-server-1/" + [[http.services.my-service.loadBalancer.servers]] + url = "http://private-ip-server-2/" + [[http.services.my-service.loadBalancer.servers]] + url = "http://private-ip-server-3/" + ``` + ## Mirroring The mirroring is able to mirror requests sent to a service to other services. Please note that by default the whole request is buffered in memory while it is being mirrored. See the `maxBodySize` option in the example below for how to modify this behaviour. You can also omit the request body by setting the `mirrorBody` option to false. diff --git a/docs/content/reference/routing-configuration/http/middlewares/apikey.md b/docs/content/reference/routing-configuration/http/middlewares/apikey.md index 454c141a1..a5b2c44b2 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/apikey.md +++ b/docs/content/reference/routing-configuration/http/middlewares/apikey.md @@ -53,4 +53,4 @@ stringData: | `secretNonBase64Encoded` | Defines whether the secret sent by the client is base64 encoded. | false | No | | `secretValues` | Contain the hash of the API keys.
Supported hashing algorithms are Bcrypt, SHA1 and MD5.
The hash should be generated using `htpasswd`.
Can reference a Kubernetes Secret using the URN format: `urn:k8s:secret:[name]:[valueKey]` | [] | Yes | -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/middlewares/basicauth.md b/docs/content/reference/routing-configuration/http/middlewares/basicauth.md index be372250b..641170051 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/basicauth.md +++ b/docs/content/reference/routing-configuration/http/middlewares/basicauth.md @@ -92,4 +92,4 @@ The option `users` supports Kubernetes secrets. Please note that these keys are not hashed or encrypted in any way, and therefore is less secure than other methods. You can find more information on the [Kubernetes Basic Authentication Secret Documentation](https://kubernetes.io/docs/concepts/configuration/secret/#basic-authentication-secret) -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/middlewares/compress.md b/docs/content/reference/routing-configuration/http/middlewares/compress.md index f67c62c9a..31cf2a4eb 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/compress.md +++ b/docs/content/reference/routing-configuration/http/middlewares/compress.md @@ -53,7 +53,7 @@ spec: |:-----------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| | `excludedContentTypes` | List of content types to compare the `Content-Type` header of the incoming requests and responses before compressing.
The responses with content types defined in `excludedContentTypes` are not compressed.
Content types are compared in a case-insensitive, whitespace-ignored manner.
**The `excludedContentTypes` and `includedContentTypes` options are mutually exclusive.** | "" | No | | `defaultEncoding` | specifies the default encoding if the `Accept-Encoding` header is not in the request or contains a wildcard (`*`). | "" | No | -| `encodings` | Specifies the list of supported compression encodings. At least one encoding value must be specified, and valid entries are `zstd` (Zstandard), `br` (Brotli), and `gzip` (Gzip). The order of the list also sets the priority, the top entry has the highest priority. | zstd, br, gzip | No | +| `encodings` | Specifies the list of supported compression encodings. At least one encoding value must be specified, and valid entries are `zstd` (Zstandard), `br` (Brotli), and `gzip` (Gzip). The order of the list also sets the priority, the top entry has the highest priority. | gzip, br, zstd | No | | `includedContentTypes` | List of content types to compare the `Content-Type` header of the responses before compressing.
The responses with content types defined in `includedContentTypes` are compressed.
Content types are compared in a case-insensitive, whitespace-ignored manner.
**The `excludedContentTypes` and `includedContentTypes` options are mutually exclusive.** | "" | No | | `minResponseBodyBytes` | `Minimum amount of bytes a response body must have to be compressed.
Responses smaller than the specified values will **not** be compressed. | 1024 | No | diff --git a/docs/content/reference/routing-configuration/http/middlewares/digestauth.md b/docs/content/reference/routing-configuration/http/middlewares/digestauth.md index 974ef48ce..8e5354393 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/digestauth.md +++ b/docs/content/reference/routing-configuration/http/middlewares/digestauth.md @@ -82,4 +82,4 @@ On Kubernetes, you don’t use the `users` or `usersFile` fields. Instead, you r - `kubernetes.io/basic-auth secret`: This secret type contains two keys—`username` and `password`—but is generally suited for a smaller number of users. Please note that these keys are not hashed or encrypted in any way, and therefore is less secure than the other method. - Opaque secret with a users field: Here, the secret contains a single string field (often called `users`) where each line represents a user. This approach allows you to store multiple users in one secret. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/middlewares/forwardauth.md b/docs/content/reference/routing-configuration/http/middlewares/forwardauth.md index 5df864c26..30bc78ec7 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/forwardauth.md +++ b/docs/content/reference/routing-configuration/http/middlewares/forwardauth.md @@ -62,7 +62,7 @@ spec: | `authRequestHeaders` | List of the headers to copy from the request to the authentication server.
It allows filtering headers that should not be passed to the authentication server.
If not set or empty, then all request headers are passed. | [] | No | | `addAuthCookiesToResponse` | List of cookies to copy from the authentication server to the response, replacing any existing conflicting cookie from the forwarded response.
Please note that all backend cookies matching the configured list will not be added to the response. | [] | No | | `forwardBody` | Sets the `forwardBody` option to `true` to send the Body. As body is read inside Traefik before forwarding, this breaks streaming. | false | No | -| `maxBodySize` | Set the `maxBodySize` to limit the body size in bytes. If body is bigger than this, it returns a 401 (unauthorized). | -1 | No | +| `maxBodySize` | Set the `maxBodySize` to limit the body size in bytes. If body is bigger than this, it returns a 401 (unauthorized). If left unset, the request body size is unrestricted which can have performance or security implications. < br/>More information [here](#maxbodysize).| -1 | No | | `headerField` | Defines a header field to store the authenticated user. | "" | No | | `preserveLocationHeader` | Defines whether to forward the Location header to the client as is or prefix it with the domain name of the authentication server. | false | No | | `preserveRequestMethod` | Defines whether to preserve the original request method while forwarding the request to the authentication server. | false | No | @@ -81,6 +81,40 @@ The start of string (`^`) and end of string (`$`) anchors should be used to ensu Regular expressions and replacements can be tested using online tools such as [Go Playground](https://play.golang.org/p/mWU9p-wk2ru) or the [Regex101](https://regex101.com/r/58sIgx/2). +### maxBodySize + +The `maxBodySize` option controls the maximum size of request bodies that will be forwarded to the authentication server. + +**⚠️ Important Security Consideration** + +By default, `maxBodySize` is not set (value: -1), which means request body size is unlimited. This can have significant security and performance implications: + +- **Security Risk**: Attackers can send extremely large request bodies, potentially causing DoS attacks or memory exhaustion +- **Performance Impact**: Large request bodies consume memory and processing resources, affecting overall system performance +- **Resource Consumption**: Unlimited body size can lead to unexpected resource usage patterns + +**Recommended Configuration** + +It is strongly recommended to set an appropriate `maxBodySize` value for your use case: + +```yaml +# For most web applications (1MB limit) +maxBodySize: 1048576 # 1MB in bytes + +# For API endpoints expecting larger payloads (10MB limit) +maxBodySize: 10485760 # 10MB in bytes + +# For file upload authentication (100MB limit) +maxBodySize: 104857600 # 100MB in bytes +``` + +**Guidelines for Setting maxBodySize** + +- **Web Forms**: 1-5MB is typically sufficient for most form submissions +- **API Endpoints**: Consider your largest expected JSON/XML payload + buffer +- **File Uploads**: Set based on your maximum expected file size +- **High-Traffic Services**: Use smaller limits to prevent resource exhaustion + ## Forward-Request Headers The following request properties are provided to the forward-auth target endpoint as `X-Forwarded-` headers. @@ -93,4 +127,4 @@ The following request properties are provided to the forward-auth target endpoin | Request URI | `X-Forwarded-Uri` | | Source IP-Address | `X-Forwarded-For` | -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/middlewares/headers.md b/docs/content/reference/routing-configuration/http/middlewares/headers.md index 5d468363e..f1774d85e 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/headers.md +++ b/docs/content/reference/routing-configuration/http/middlewares/headers.md @@ -323,4 +323,4 @@ It allows all origins that contain any match of a regular expression in the `acc When defining a regular expression within YAML, any escaped character needs to be escaped twice: `example\.com` needs to be written as `example\\.com`. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/middlewares/hmac.md b/docs/content/reference/routing-configuration/http/middlewares/hmac.md index 2eabab306..0bc37da90 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/hmac.md +++ b/docs/content/reference/routing-configuration/http/middlewares/hmac.md @@ -204,4 +204,4 @@ Only SHA-256 and SHA-512 checksums are supported for checksum computation. To disable this feature and only perform authentication, set the `validateDigest` option to `false` in the middleware configuration. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/middlewares/jwt.md b/docs/content/reference/routing-configuration/http/middlewares/jwt.md index 2aebcdf86..5dc9f5479 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/jwt.md +++ b/docs/content/reference/routing-configuration/http/middlewares/jwt.md @@ -230,4 +230,4 @@ The reference to a Kubernetes secret takes the form of a URN: urn:k8s:secret:[name]:[valueKey] ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/middlewares/ldap.md b/docs/content/reference/routing-configuration/http/middlewares/ldap.md index 9a0efc6f2..027d6ee02 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/ldap.md +++ b/docs/content/reference/routing-configuration/http/middlewares/ldap.md @@ -102,4 +102,4 @@ and a `bindPassword`, then the middleware runs in search mode. In this mode, a s issued to the LDAP server before trying to bind. If result of this search returns only 1 record, it tries to issue a bind request with this record, otherwise it aborts a `401 Unauthorized` status code. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/middlewares/oauth2-client-credentials.md b/docs/content/reference/routing-configuration/http/middlewares/oauth2-client-credentials.md index 80eecea88..40de4f315 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/oauth2-client-credentials.md +++ b/docs/content/reference/routing-configuration/http/middlewares/oauth2-client-credentials.md @@ -252,4 +252,4 @@ The following Redis modes are supported: For more information about Redis, we recommend the [official Redis documentation](https://redis.io/docs/ "Link to official Redis documentation"). -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/middlewares/oauth2-token-introspection.md b/docs/content/reference/routing-configuration/http/middlewares/oauth2-token-introspection.md index 8633abcbb..d98ad4e43 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/oauth2-token-introspection.md +++ b/docs/content/reference/routing-configuration/http/middlewares/oauth2-token-introspection.md @@ -206,4 +206,4 @@ stringData: -----END EC PRIVATE KEY----- ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/middlewares/oidc.md b/docs/content/reference/routing-configuration/http/middlewares/oidc.md index 120e79bf9..0e0dfe1ea 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/oidc.md +++ b/docs/content/reference/routing-configuration/http/middlewares/oidc.md @@ -427,4 +427,4 @@ This means that a new CSRF token will be generated and sent to the client whenev When a request is sent and uses a non-safe method (see [RFC7231#section-4.2.1](https://datatracker.ietf.org/doc/html/rfc7231.html#section-4.2.1)), the CSRF token value (extracted from the cookie) have to be sent to the server in the header configured with the [headerName option](#configuration-options). -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/middlewares/opa.md b/docs/content/reference/routing-configuration/http/middlewares/opa.md index 4dabfe455..298c8ef04 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/opa.md +++ b/docs/content/reference/routing-configuration/http/middlewares/opa.md @@ -69,4 +69,4 @@ spec: | `allow` | The `allow` option sets the expression to evaluate that determines if the request should be authorized. | "" | No (one of `allow` or `forwardHeaders` must be set) | | `forwardHeaders` | The `forwardHeaders` option sets the HTTP headers to add to requests and populates them with the result of the given expression. | "" | No (one of `allow` or `forwardHeaders` must be set) | -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/middlewares/overview.md b/docs/content/reference/routing-configuration/http/middlewares/overview.md index 40ddb6015..5a287c70b 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/overview.md +++ b/docs/content/reference/routing-configuration/http/middlewares/overview.md @@ -48,4 +48,4 @@ Middlewares that use the same protocol can be combined into chains to fit every Please take a look at the community-contributed plugins in the [plugin catalog](https://plugins.traefik.io/plugins). -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/middlewares/redirectregex.md b/docs/content/reference/routing-configuration/http/middlewares/redirectregex.md index 942f2e833..187cb0286 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/redirectregex.md +++ b/docs/content/reference/routing-configuration/http/middlewares/redirectregex.md @@ -85,4 +85,4 @@ The `replacement` option defines how to modify the URL to have the new target UR Care should be taken when defining replacement expand variables: `$1x` is equivalent to `${1x}`, not `${1}x` (see [Regexp.Expand](https://golang.org/pkg/regexp/#Regexp.Expand)), so use `${1}` syntax. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/middlewares/stripprefix.md b/docs/content/reference/routing-configuration/http/middlewares/stripprefix.md index b3e6ebfd8..90590489c 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/stripprefix.md +++ b/docs/content/reference/routing-configuration/http/middlewares/stripprefix.md @@ -63,4 +63,4 @@ spec: |:-----------------------------|:--------------------------------------------------------------|:--------|:---------| | `prefixes` | List of prefixes to strip from the request URL.
If your backend is serving assets (for example, images or JavaScript files), it can use the `X-Forwarded-Prefix` header to construct relative URLs. | [] | No | -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/middlewares/waf.md b/docs/content/reference/routing-configuration/http/middlewares/waf.md index e5e864db7..33fe47b21 100644 --- a/docs/content/reference/routing-configuration/http/middlewares/waf.md +++ b/docs/content/reference/routing-configuration/http/middlewares/waf.md @@ -61,4 +61,4 @@ spec: | `directives` | List of WAF rules to enforce. | | Yes | | `crsEnabled` | Enable [CRS rulesets](https://github.com/corazawaf/coraza-coreruleset/tree/main/rules/%40owasp_crs).
Once the ruleset is enabled, it can be used in the middleware. | false | False | -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/routing/multi-layer-routing.md b/docs/content/reference/routing-configuration/http/routing/multi-layer-routing.md new file mode 100644 index 000000000..7d6f9f01e --- /dev/null +++ b/docs/content/reference/routing-configuration/http/routing/multi-layer-routing.md @@ -0,0 +1,188 @@ +--- +title: "Multi-Layer Routing" +description: "Learn how to use Traefik's multi-layer routing to create hierarchical router relationships where parent routers can apply middleware before child routers make routing decisions." +--- + +# Multi-Layer Routing + +Hierarchical Router Relationships for Advanced Routing Scenarios. + +## Overview + +Multi-layer routing enables you to create hierarchical relationships between routers, +where parent routers can process requests through middleware before child routers make final routing decisions. + +This feature allows middleware at the parent level to modify requests (adding headers, performing authentication, etc.) that influence how child routers evaluate their rules and route traffic to services. + +Multi-layer routing is particularly useful for progressive request enrichment, where each layer adds context to the request, enabling increasingly specific routing decisions: + +- **Authentication-Based Routing**: Parent router authenticates requests and adds user context (roles, permissions) as headers, child routers route based on these headers +- **Staged Middleware Application**: Apply common middleware (rate limiting, CORS) at parent level (for a given domain/path), but specific middleware at child level + +!!! info "Provider Support" + + Multi-layer routing is supported by the following providers: + + - **File provider** (YAML, TOML, JSON) + - **KV stores** (Consul, etcd, Redis, ZooKeeper) + - **Kubernetes CRD** (IngressRoute) + + Multi-layer routing is not available for other providers (Docker, Kubernetes Ingress, Gateway API, etc.). + + +## How It Works + +``` +Request → EntryPoint → Parent Router → Middleware → Child Router A → Service A + ↓ → Child Router B → Service B + Modify Request + (e.g., add headers) +``` + +1. **Request arrives** at an entrypoint +2. **Parent router matches** based on its rule (e.g., ```Host(`example.com`)```) +3. **Parent middleware executes**, potentially modifying the request +4. **One child router matches** based on its rule (which may use modified request attributes) +5. **Request is forwarded** to the matching child router's service + +## Building a Router Hierarchy + +### Root Routers + +- Have no `parentRefs` (top of the hierarchy) +- **Can** have `tls`, `observability`, and `entryPoints` configuration +- Can be either parent routers (with children) or standalone routers (with service) +- **Can** have models applied (non-root routers cannot have models) + +### Intermediate Routers + +- Reference their parent router(s) via `parentRefs` +- Have one or more child routers +- **Must not** have a `service` defined +- **Must not** have `entryPoints`, `tls`, or `observability` configuration + +### Leaf Routers + +- Reference their parent router(s) via `parentRefs` +- **Must** have a `service` defined +- **Must not** have `entryPoints`, `tls`, or `observability` configuration + +## Configuration Example + +??? example "Authentication-Based Routing" + + ```yaml tab="File (YAML)" + ## Dynamic configuration + http: + routers: + # Parent router with authentication + api-parent: + rule: "PathPrefix(`/api`)" + middlewares: + - auth-middleware + entryPoints: + - websecure + tls: {} + # Note: No service defined - this is a parent router + + # Child router for admin users + api-admin: + rule: "HeadersRegexp(`X-User-Role`, `admin`)" + service: admin-service + parentRefs: + - api-parent + + # Child router for regular users + api-user: + rule: "HeadersRegexp(`X-User-Role`, `user`)" + service: user-service + parentRefs: + - api-parent + + middlewares: + auth-middleware: + forwardAuth: + address: "http://auth-service:8080/auth" + authResponseHeaders: + - X-User-Role + - X-User-Name + + services: + admin-service: + loadBalancer: + servers: + - url: "http://admin-backend:8080" + + user-service: + loadBalancer: + servers: + - url: "http://user-backend:8080" + ``` + + ```toml tab="File (TOML)" + ## Dynamic configuration + [http.routers] + # Parent router with authentication + [http.routers.api-parent] + rule = "PathPrefix(`/api`)" + middlewares = ["auth-middleware"] + entryPoints = ["websecure"] + [http.routers.api-parent.tls] + # Note: No service defined - this is a parent router + + # Child router for admin users + [http.routers.api-admin] + rule = "HeadersRegexp(`X-User-Role`, `admin`)" + service = "admin-service" + parentRefs = ["api-parent"] + + # Child router for regular users + [http.routers.api-user] + rule = "HeadersRegexp(`X-User-Role`, `user`)" + service = "user-service" + parentRefs = ["api-parent"] + + [http.middlewares] + [http.middlewares.auth-middleware.forwardAuth] + address = "http://auth-service:8080/auth" + authResponseHeaders = ["X-User-Role", "X-User-Name"] + + [http.services] + [http.services.admin-service.loadBalancer] + [[http.services.admin-service.loadBalancer.servers]] + url = "http://admin-backend:8080" + + [http.services.user-service.loadBalancer] + [[http.services.user-service.loadBalancer.servers]] + url = "http://user-backend:8080" + ``` + + ```txt tab="KV (Consul/etcd/Redis/ZK)" + | Key | Value | + |------------------------------------------------------------------------|---------------------------------| + | `traefik/http/routers/api-parent/rule` | `PathPrefix(\`/api\`)` | + | `traefik/http/routers/api-parent/middlewares/0` | `auth-middleware` | + | `traefik/http/routers/api-parent/entrypoints/0` | `websecure` | + | `traefik/http/routers/api-parent/tls` | `true` | + | `traefik/http/routers/api-admin/rule` | `HeadersRegexp(\`X-User-Role\`, \`admin\`)` | + | `traefik/http/routers/api-admin/service` | `admin-service` | + | `traefik/http/routers/api-admin/parentrefs/0` | `api-parent` | + | `traefik/http/routers/api-user/rule` | `HeadersRegexp(\`X-User-Role\`, \`user\`)` | + | `traefik/http/routers/api-user/service` | `user-service` | + | `traefik/http/routers/api-user/parentrefs/0` | `api-parent` | + | `traefik/http/middlewares/auth-middleware/forwardauth/address` | `http://auth-service:8080/auth` | + | `traefik/http/middlewares/auth-middleware/forwardauth/authresponseheaders/0` | `X-User-Role` | + | `traefik/http/middlewares/auth-middleware/forwardauth/authresponseheaders/1` | `X-User-Name` | + | `traefik/http/services/admin-service/loadbalancer/servers/0/url` | `http://admin-backend:8080` | + | `traefik/http/services/user-service/loadbalancer/servers/0/url` | `http://user-backend:8080` | + ``` + + **How it works:** + + 1. Request to `/api/endpoint` matches `api-parent` router + 2. `auth-middleware` (ForwardAuth) validates the request and adds `X-User-Role` header + 3. Modified request is evaluated by child routers + 4. If `X-User-Role: admin`, `api-admin` router matches and forwards to `admin-service` + 5. If `X-User-Role: user`, `api-user` router matches and forwards to `user-service` + +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/routing/router.md b/docs/content/reference/routing-configuration/http/routing/router.md index 2e9a7eb56..6d322e121 100644 --- a/docs/content/reference/routing-configuration/http/routing/router.md +++ b/docs/content/reference/routing-configuration/http/routing/router.md @@ -32,6 +32,9 @@ http: metrics: true accessLogs: true tracing: true + parentRefs: + - "parent-router-1" + - "parent-router-2" service: my-service ``` @@ -43,6 +46,7 @@ http: priority = 10 middlewares = ["auth", "ratelimit"] service = "my-service" + parentRefs = ["parent-router-1", "parent-router-2"] [http.routers.my-router.tls] certResolver = "letsencrypt" @@ -88,15 +92,15 @@ labels: "traefik.http.routers.my-router.tls.domains[0].sans=www.example.com", "traefik.http.routers.my-router.observability.metrics=true", "traefik.http.routers.my-router.observability.accessLogs=true", - "traefik.http.routers.my-router.observability.tracing=true" + "traefik.http.routers.my-router.observability.tracing=true", ] } ``` ## Configuration Options -| Field | Description | Default | Required | -|----------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------|----------| +| Field | Description | Default | Required | +|----------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------|----------| | `entryPoints` | The list of entry points to which the router is attached. If not specified, HTTP routers are attached to all entry points. | All entry points | No | | `rule` | Rules are a set of matchers configured with values, that determine if a particular request matches specific criteria. If the rule is verified, the router becomes active, calls middlewares, and then forwards the request to the service. See [Rules & Priority](./rules-and-priority.md) for details. | | Yes | | `priority` | To avoid path overlap, routes are sorted, by default, in descending order using rules length. The priority is directly equal to the length of the rule, and so the longest length has the highest priority. A value of `0` for the priority is ignored. See [Rules & Priority](./rules-and-priority.md) for details. | Rule length | No | @@ -106,6 +110,7 @@ labels: | `tls.options` | The name of the TLS options to use for configuring TLS parameters (cipher suites, min/max TLS version, client authentication, etc.). See [TLS Options](../tls/tls-options.md) for detailed configuration. | `default` | No | | `tls.domains` | List of domains and Subject Alternative Names (SANs) for explicit certificate domain specification. When using ACME certificate resolvers, domains are automatically extracted from router rules, making this option optional. | | No | | `observability` | Observability configuration for the router. Allows fine-grained control over access logs, metrics, and tracing per router. See [Observability](./observability.md) for details. | Inherited from entry points | No | +| `parentRefs` | References to parent router names for multi-layer routing. When specified, this router becomes a child router that processes requests after parent routers have applied their middlewares. See [Multi-Layer Routing](../routing/multi-layer-routing.md) for details. | | No | | `service` | The name of the service that will handle the matched requests. Services can be load balancer services, weighted round robin, mirroring, or failover services. See [Service](../load-balancing/service.md) for details. | | Yes | ## Router Naming @@ -113,4 +118,4 @@ labels: - The character `@` is not authorized in the router name - In provider-specific configurations (Docker, Kubernetes), router names are often auto-generated based on service names and rules -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/tls/overview.md b/docs/content/reference/routing-configuration/http/tls/overview.md index c1e1a6892..7e05bf185 100644 --- a/docs/content/reference/routing-configuration/http/tls/overview.md +++ b/docs/content/reference/routing-configuration/http/tls/overview.md @@ -100,4 +100,4 @@ This provides fine-grained control over certificate generation and takes precede Every domain must have A/AAAA records pointing to Traefik. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/tls/tls-certificates.md b/docs/content/reference/routing-configuration/http/tls/tls-certificates.md index 155dd1e4c..cb2f1a4ce 100644 --- a/docs/content/reference/routing-configuration/http/tls/tls-certificates.md +++ b/docs/content/reference/routing-configuration/http/tls/tls-certificates.md @@ -155,4 +155,4 @@ labels: } ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/http/tls/tls-options.md b/docs/content/reference/routing-configuration/http/tls/tls-options.md index 939edf91e..0339ba70c 100644 --- a/docs/content/reference/routing-configuration/http/tls/tls-options.md +++ b/docs/content/reference/routing-configuration/http/tls/tls-options.md @@ -259,4 +259,4 @@ spec: disableSessionTickets: true ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/kubernetes/crd/http/ingressroute.md b/docs/content/reference/routing-configuration/kubernetes/crd/http/ingressroute.md index 4328ebfd1..fc7ae06b2 100644 --- a/docs/content/reference/routing-configuration/kubernetes/crd/http/ingressroute.md +++ b/docs/content/reference/routing-configuration/kubernetes/crd/http/ingressroute.md @@ -23,6 +23,9 @@ metadata: spec: entryPoints: - web + parentRefs: + - name: parent-gateway + namespace: default # Optional - defaults to same namespace routes: - kind: Rule # Rule on the Host @@ -54,7 +57,7 @@ spec: httpOnly: true name: cookie secure: true - strategy: RoundRobin + strategy: wrr weight: 10 tls: # Generate a TLS certificate using a certificate resolver @@ -74,9 +77,12 @@ spec: ## Configuration Options -| Field | Description | Default | Required | -|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| +| Field | Description | Default | Required | +|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------|:---------| | `entryPoints` | List of [entry points](../../../../install-configuration/entrypoints.md) names.
If not specified, HTTP routers will accept requests from all EntryPoints in the list of default EntryPoints. | | No | +| `parentRefs` | List of references to parent IngressRoute resources for multi-layer routing. When specified, this IngressRoute's routers become children of the referenced parent IngressRoute's routers. See [Multi-Layer Routing](#multi-layer-routing-with-ingressroutes) section for details. | | No | +| `parentRefs[n].name` | Name of the referenced parent IngressRoute resource. | | Yes | +| `parentRefs[n].namespace` | Namespace of the referenced parent IngressRoute resource.
If not specified, defaults to the same namespace as the child IngressRoute.
Cross-namespace references require `allowCrossNamespace` provider option to be enabled. | | No | | `routes` | List of routes. | | Yes | | `routes[n].kind` | Kind of router matching, only `Rule` is allowed yet. | "Rule" | No | | `routes[n].match` | Defines the [rule](../../../http/routing/rules-and-priority.md#rules) corresponding to an underlying router. | | Yes | @@ -213,6 +219,162 @@ TLS options references, a conflict occurs, such as in the example below. ... ``` -If that happens, both mappings are discarded, and the host name +If that happens, both mappings are discarded, and the host name (`example.net` in the example) for these routers gets associated with the default TLS options instead. + +### Multi-Layer Routing with IngressRoutes + +Multi-layer routing allows creating hierarchical relationships between IngressRoutes, +where parent IngressRoutes can apply middleware before child IngressRoutes make routing decisions. + +This is particularly useful for authentication-based routing, +where a parent IngressRoute authenticates requests and adds context (e.g., user roles as headers), +and child IngressRoutes route based on that context. + +When a child IngressRoute references a parent IngressRoute with multiple routes, +**all** parent routers then become parents of **all** child routers. + +!!! info "Comprehensive Multi-Layer Routing Documentation" + + For detailed information about multi-layer routing concepts, validation rules, and use cases, see the dedicated [Multi-Layer Routing](../../../../routing-configuration/http/routing/multi-layer-routing.md) page. + +#### Configuration Requirements + +### Root IngressRoutes + +- Have no `parentRefs` (top of the hierarchy) +- **Can** have `entryPoints`, `tls`, and `observability` configuration +- Can be either parent IngressRoutes (with children) or standalone IngressRoutes (with service) + +### Intermediate IngressRoutes + +- Reference their parent IngressRoute(s) via `parentRefs` +- Have one or more child IngressRoutes +- **Must not** have a `service` defined +- **Must not** have `entryPoints`, `tls`, or `observability` configuration + +### Leaf IngressRoutes + +- Reference their parent IngressRoute(s) via `parentRefs` +- **Must** have a `service` defined +- **Must not** have `entryPoints`, `tls`, or `observability` configuration + +!!! warning "Cross-Namespace References" + + Cross-namespace parent references require the `allowCrossNamespace` provider option to be enabled. + If disabled, child IngressRoute creation will be skipped with an error logged. + +#### Example: Authentication-Based Routing + +??? example "Parent IngressRoute with ForwardAuth and Child IngressRoutes" + + ```yaml tab="Parent IngressRoute" + apiVersion: traefik.io/v1alpha1 + kind: IngressRoute + metadata: + name: api-parent + namespace: default + spec: + entryPoints: + - websecure + tls: + certResolver: letsencrypt + routes: + # Parent route with authentication - no services + - match: Host(`api.example.com`) && PathPrefix(`/api`) + kind: Rule + middlewares: + - name: auth-middleware + namespace: default + --- + apiVersion: traefik.io/v1alpha1 + kind: Middleware + metadata: + name: auth-middleware + namespace: default + spec: + forwardAuth: + address: "http://auth-service.default.svc.cluster.local:8080/auth" + authResponseHeaders: + - X-User-Role + - X-User-Name + ``` + + ```yaml tab="Child IngressRoutes" + # Child IngressRoute for admin users + apiVersion: traefik.io/v1alpha1 + kind: IngressRoute + metadata: + name: api-admin + namespace: default + spec: + parentRefs: + - name: api-parent + namespace: default # Optional - defaults to same namespace + routes: + - match: HeadersRegexp(`X-User-Role`, `admin`) + kind: Rule + services: + - name: admin-service + port: 80 + --- + # Child IngressRoute for regular users + apiVersion: traefik.io/v1alpha1 + kind: IngressRoute + metadata: + name: api-user + namespace: default + spec: + parentRefs: + - name: api-parent + routes: + - match: HeadersRegexp(`X-User-Role`, `user`) + kind: Rule + services: + - name: user-service + port: 80 + ``` + + ```yaml tab="Services" + apiVersion: v1 + kind: Service + metadata: + name: auth-service + namespace: default + spec: + ports: + - port: 8080 + selector: + app: auth-service + --- + apiVersion: v1 + kind: Service + metadata: + name: admin-service + namespace: default + spec: + ports: + - port: 80 + selector: + app: admin-backend + --- + apiVersion: v1 + kind: Service + metadata: + name: user-service + namespace: default + spec: + ports: + - port: 80 + selector: + app: user-backend + ``` + + **How it works:** + + 1. Request to `https://api.example.com/api/endpoint` matches the parent router + 2. `auth-middleware` (ForwardAuth) validates the request with `auth-service` + 3. `auth-service` returns 200 OK with `X-User-Role` header (e.g., `admin` or `user`) + 4. Child routers evaluate rules against the modified request (with `X-User-Role` header) + 5. Request is routed to `admin-service` or `user-service` based on the role diff --git a/docs/content/reference/routing-configuration/kubernetes/crd/http/service.md b/docs/content/reference/routing-configuration/kubernetes/crd/http/service.md index b69515b25..429b654c1 100644 --- a/docs/content/reference/routing-configuration/kubernetes/crd/http/service.md +++ b/docs/content/reference/routing-configuration/kubernetes/crd/http/service.md @@ -47,7 +47,7 @@ spec: httpOnly: true name: cookie secure: true - strategy: RoundRobin + strategy: wrr ``` ```yaml tab="TraefikService" @@ -75,41 +75,41 @@ spec: httpOnly: true name: cookie secure: true - strategy: RoundRobin + strategy: wrr ``` ## Configuration Options -| Field | Description | Default | Required | -|:---------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------|:---------| -| `kind` | Kind of the service targeted.
Two values allowed:
- **Service**: Kubernetes Service
**TraefikService**: Traefik Service.
More information [here](#externalname-service). | "Service" | No | -| `name` | Service name.
The character `@` is not authorized.
More information [here](#middleware). | | Yes | -| `namespace` | Service namespace.
Can be empty if the service belongs to the same namespace as the IngressRoute.
More information [here](#externalname-service). | | No | -| `port` | Service port (number or port name).
Evaluated only if the kind is **Service**. | | No | -| `responseForwarding.`
`flushInterval`
| Interval, in milliseconds, in between flushes to the client while copying the response body.
A negative value means to flush immediately after each write to the client.
This configuration is ignored when a response is a streaming response; for such responses, writes are flushed to the client immediately.
Evaluated only if the kind is **Service**. | 100ms | No | -| `scheme` | Scheme to use for the request to the upstream Kubernetes Service.
Evaluated only if the kind is **Service**. | "http"
"https" if `port` is 443 or contains the string *https*. | No | -| `serversTransport` | Name of ServersTransport resource to use to configure the transport between Traefik and your servers.
Evaluated only if the kind is **Service**. | "" | No | -| `passHostHeader` | Forward client Host header to server.
Evaluated only if the kind is **Service**. | true | No | -| `healthCheck.scheme` | Server URL scheme for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "" | No | -| `healthCheck.mode` | Health check mode.
If defined to grpc, will use the gRPC health check protocol to probe the server.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "http" | No | -| `healthCheck.path` | Server URL path for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "" | No | -| `healthCheck.interval` | Frequency of the health check calls for healthy targets.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "100ms" | No | -| `healthCheck.unhealthyInterval` | Frequency of the health check calls for unhealthy targets.
When not defined, it defaults to the `interval` value.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "100ms" | No | -| `healthCheck.method` | HTTP method for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "GET" | No | -| `healthCheck.status` | Expected HTTP status code of the response to the health check request.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type ExternalName.
If not set, expect a status between 200 and 399.
Evaluated only if the kind is **Service**. | | No | -| `healthCheck.port` | URL port for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | | No | -| `healthCheck.timeout` | Maximum duration to wait before considering the server unhealthy.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "5s" | No | -| `healthCheck.hostname` | Value in the Host header of the health check request.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "" | No | -| `healthCheck.`
`followRedirect`
| Follow the redirections during the healtchcheck.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | true | No | -| `healthCheck.headers` | Map of header to send to the health check endpoint
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service)). | | No | -| `sticky.`
`cookie.name`
| Name of the cookie used for the stickiness.
When sticky sessions are enabled, a `Set-Cookie` header is set on the initial response to let the client know which server handles the first response.
On subsequent requests, to keep the session alive with the same server, the client should send the cookie with the value set.
If the server pecified in the cookie becomes unhealthy, the request will be forwarded to a new server (and the cookie will keep track of the new server).
Evaluated only if the kind is **Service**. | "" | No | -| `sticky.`
`cookie.httpOnly`
| Allow the cookie can be accessed by client-side APIs, such as JavaScript.
Evaluated only if the kind is **Service**. | false | No | -| `sticky.`
`cookie.secure`
| Allow the cookie can only be transmitted over an encrypted connection (i.e. HTTPS).
Evaluated only if the kind is **Service**. | false | No | -| `sticky.`
`cookie.sameSite`
| [SameSite](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite) policy
Allowed values:
-`none`
-`lax`
`strict`
Evaluated only if the kind is **Service**. | "" | No | -| `sticky.`
`cookie.maxAge`
| Number of seconds until the cookie expires.
Negative number, the cookie expires immediately.
0, the cookie never expires.
Evaluated only if the kind is **Service**. | 0 | No | -| `strategy` | Load balancing strategy between the servers.
RoundRobin is the only supported value yet.
Evaluated only if the kind is **Service**. | "RoundRobin" | No | -| `nativeLB` | Allow using the Kubernetes Service load balancing between the pods instead of the one provided by Traefik.
Evaluated only if the kind is **Service**. | false | No | -| `nodePortLB` | Use the nodePort IP address when the service type is NodePort.
It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes.
Evaluated only if the kind is **Service**. | false | No | +| Field | Description | Default | Required | +|:---------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------|:---------| +| `kind` | Kind of the service targeted.
Two values allowed:
- **Service**: Kubernetes Service
**TraefikService**: Traefik Service.
More information [here](#externalname-service). | "Service" | No | +| `name` | Service name.
The character `@` is not authorized.
More information [here](#middleware). | | Yes | +| `namespace` | Service namespace.
Can be empty if the service belongs to the same namespace as the IngressRoute.
More information [here](#externalname-service). | | No | +| `port` | Service port (number or port name).
Evaluated only if the kind is **Service**. | | No | +| `responseForwarding.`
`flushInterval`
| Interval, in milliseconds, in between flushes to the client while copying the response body.
A negative value means to flush immediately after each write to the client.
This configuration is ignored when a response is a streaming response; for such responses, writes are flushed to the client immediately.
Evaluated only if the kind is **Service**. | 100ms | No | +| `scheme` | Scheme to use for the request to the upstream Kubernetes Service.
Evaluated only if the kind is **Service**. | "http"
"https" if `port` is 443 or contains the string *https*. | No | +| `serversTransport` | Name of ServersTransport resource to use to configure the transport between Traefik and your servers.
Evaluated only if the kind is **Service**. | "" | No | +| `passHostHeader` | Forward client Host header to server.
Evaluated only if the kind is **Service**. | true | No | +| `healthCheck.scheme` | Server URL scheme for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "" | No | +| `healthCheck.mode` | Health check mode.
If defined to grpc, will use the gRPC health check protocol to probe the server.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "http" | No | +| `healthCheck.path` | Server URL path for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "" | No | +| `healthCheck.interval` | Frequency of the health check calls for healthy targets.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "100ms" | No | +| `healthCheck.unhealthyInterval` | Frequency of the health check calls for unhealthy targets.
When not defined, it defaults to the `interval` value.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "100ms" | No | +| `healthCheck.method` | HTTP method for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "GET" | No | +| `healthCheck.status` | Expected HTTP status code of the response to the health check request.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type ExternalName.
If not set, expect a status between 200 and 399.
Evaluated only if the kind is **Service**. | | No | +| `healthCheck.port` | URL port for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | | No | +| `healthCheck.timeout` | Maximum duration to wait before considering the server unhealthy.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "5s" | No | +| `healthCheck.hostname` | Value in the Host header of the health check request.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | "" | No | +| `healthCheck.`
`followRedirect`
| Follow the redirections during the healtchcheck.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service). | true | No | +| `healthCheck.headers` | Map of header to send to the health check endpoint
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type [ExternalName](#externalname-service)). | | No | +| `sticky.`
`cookie.name`
| Name of the cookie used for the stickiness.
When sticky sessions are enabled, a `Set-Cookie` header is set on the initial response to let the client know which server handles the first response.
On subsequent requests, to keep the session alive with the same server, the client should send the cookie with the value set.
If the server pecified in the cookie becomes unhealthy, the request will be forwarded to a new server (and the cookie will keep track of the new server).
Evaluated only if the kind is **Service**. | "" | No | +| `sticky.`
`cookie.httpOnly`
| Allow the cookie can be accessed by client-side APIs, such as JavaScript.
Evaluated only if the kind is **Service**. | false | No | +| `sticky.`
`cookie.secure`
| Allow the cookie can only be transmitted over an encrypted connection (i.e. HTTPS).
Evaluated only if the kind is **Service**. | false | No | +| `sticky.`
`cookie.sameSite`
| [SameSite](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite) policy
Allowed values:
-`none`
-`lax`
`strict`
Evaluated only if the kind is **Service**. | "" | No | +| `sticky.`
`cookie.maxAge`
| Number of seconds until the cookie expires.
Negative number, the cookie expires immediately.
0, the cookie never expires.
Evaluated only if the kind is **Service**. | 0 | No | +| `strategy` | Strategy defines the load balancing strategy between the servers.
Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time).
Evaluated only if the kind is **Service**. | "RoundRobin" | No | +| `nativeLB` | Allow using the Kubernetes Service load balancing between the pods instead of the one provided by Traefik.
Evaluated only if the kind is **Service**. | false | No | +| `nodePortLB` | Use the nodePort IP address when the service type is NodePort.
It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes.
Evaluated only if the kind is **Service**. | false | No | ### ExternalName Service diff --git a/docs/content/reference/routing-configuration/kubernetes/crd/http/traefikservice.md b/docs/content/reference/routing-configuration/kubernetes/crd/http/traefikservice.md index 55bae513e..1d569aa54 100644 --- a/docs/content/reference/routing-configuration/kubernetes/crd/http/traefikservice.md +++ b/docs/content/reference/routing-configuration/kubernetes/crd/http/traefikservice.md @@ -3,11 +3,12 @@ title: "Traefik Kubernetes Services Documentation" description: "Learn how to configure routing and load balancing in Traefik Proxy to reach Services, which handle incoming requests. Read the technical documentation." --- -A `TraefikService` is a custom resource that sits on top of the Kubernetes Services. It enables advanced load-balancing features such as a [Weighted Round Robin](#weighted-round-robin) load balancing or a [Mirroring](#mirroring) between your Kubernetes Services. +A `TraefikService` is a custom resource that sits on top of the Kubernetes Services. It enables advanced load-balancing features such as a [Weighted Round Robin](#weighted-round-robin) load balancing, a [Highest Random Weight](#highest-random-weight) load balancing, or a [Mirroring](#mirroring) between your Kubernetes Services. Services configure how to reach the actual endpoints that will eventually handle incoming requests. In Traefik, the target service can be either a standard [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/)—which exposes a pod—or a TraefikService. The latter allows you to combine advanced load-balancing options like: - [Weighted Round Robin load balancing](#weighted-round-robin). +- [Highest Random Weight load balancing](#highest-random-weight). - [Mirroring](#mirroring). ## Weighted Round Robin @@ -227,6 +228,143 @@ In the example above, to keep a session open with the same server, the client wo curl -H Host:example.com -b "lvl1=default-whoami1-80; lvl2=http://10.42.0.6:80" http://localhost:8000/foo ``` +## Highest Random Weight + +The HRW (Highest Random Weight) load balancer uses consistent hashing to ensure that requests from the same client IP are always routed to the same service. Unlike weighted round-robin which distributes requests based on weights, HRW provides consistent routing based on the client's remote address. + +This is particularly useful for maintaining session affinity without requiring sticky cookies, as clients will consistently reach the same backend service based on their IP address. + +### Configuration Example + +```yaml tab="IngressRoute" +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: test-hrw + namespace: apps + +spec: + entryPoints: + - websecure + routes: + - match: Host(`example.com`) && PathPrefix(`/app`) + kind: Rule + services: + # Set an HRW TraefikService + - name: hrw1 + namespace: apps + kind: TraefikService + tls: + secretName: supersecret +``` + +```yaml tab="TraefikService HRW" +apiVersion: traefik.io/v1alpha1 +kind: TraefikService +metadata: + name: hrw1 + namespace: apps + +spec: + highestRandomWeight: + services: + # Kubernetes Service with weight 10 + - name: svc1 + namespace: apps + port: 80 + weight: 10 + # Kubernetes Service with weight 20 + - name: svc2 + namespace: apps + port: 80 + weight: 20 + # Another TraefikService + - name: wrr1 + namespace: apps + kind: TraefikService + weight: 15 +``` + +```yaml tab="Kubernetes Services" +apiVersion: v1 +kind: Service +metadata: + name: svc1 + namespace: apps + +spec: + ports: + - name: http + port: 80 + selector: + app: traefiklabs + task: app1 +--- +apiVersion: v1 +kind: Service +metadata: + name: svc2 + namespace: apps + +spec: + ports: + - name: http + port: 80 + selector: + app: traefiklabs + task: app2 +``` + +### Configuration Options + +| Field | Description | Default | Required | +|:---------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---------------------------------------------------------------------|:---------| +| `services` | List of any combination of TraefikService and [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/). Each service must have a weight assigned. | | Yes | +| `services[m].`
`kind`
| Kind of the service targeted.
Two values allowed:
- **Service**: Kubernetes Service
- **TraefikService**: Traefik Service. | "" | No | +| `services[m].`
`name`
| Service name.
The character `@` is not authorized. | "" | Yes | +| `services[m].`
`namespace`
| Service namespace. | "" | No | +| `services[m].`
`port`
| Service port (number or port name).
Evaluated only if the kind is **Service**. | "" | No | +| `services[m].`
`weight`
| Service weight used in the HRW algorithm. Higher weights increase the probability of selection for a given client IP. | 1 | No | +| `services[m].`
`responseForwarding.`
`flushInterval`
| Interval, in milliseconds, in between flushes to the client while copying the response body.
A negative value means to flush immediately after each write to the client.
This configuration is ignored when a response is a streaming response; for such responses, writes are flushed to the client immediately.
Evaluated only if the kind is **Service**. | 100ms | No | +| `services[m].`
`scheme`
| Scheme to use for the request to the upstream Kubernetes Service.
Evaluated only if the kind is **Service**. | "http"
"https" if `port` is 443 or contains the string *https*. | No | +| `services[m].`
`serversTransport`
| Name of ServersTransport resource to use to configure the transport between Traefik and your servers.
Evaluated only if the kind is **Service**. | "" | No | +| `services[m].`
`passHostHeader`
| Forward client Host header to server.
Evaluated only if the kind is **Service**. | true | No | +| `services[m].`
`healthCheck.scheme`
| Server URL scheme for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | "" | No | +| `services[m].`
`healthCheck.mode`
| Health check mode.
If defined to grpc, will use the gRPC health check protocol to probe the server.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | "http" | No | +| `services[m].`
`healthCheck.path`
| Server URL path for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | "" | No | +| `services[m].`
`healthCheck.interval`
| Frequency of the health check calls for healthy targets.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | "100ms" | No | +| `services[m].`
`healthCheck.unhealthyInterval`
| Frequency of the health check calls for unhealthy targets.
When not defined, it defaults to the `interval` value.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | "100ms" | No | +| `services[m].`
`healthCheck.method`
| HTTP method for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | "GET" | No | +| `services[m].`
`healthCheck.status`
| Expected HTTP status code of the response to the health check request.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type ExternalName.
If not set, expect a status between 200 and 399.
Evaluated only if the kind is **Service**. | | No | +| `services[m].`
`healthCheck.port`
| URL port for the health check endpoint.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | | No | +| `services[m].`
`healthCheck.timeout`
| Maximum duration to wait before considering the server unhealthy.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | "5s" | No | +| `services[m].`
`healthCheck.hostname`
| Value in the Host header of the health check request.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | "" | No | +| `services[m].`
`healthCheck.`
`followRedirect`
| Follow the redirections during the healtchcheck.
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | true | No | +| `services[m].`
`healthCheck.headers`
| Map of header to send to the health check endpoint
Evaluated only if the kind is **Service**.
Only for [Kubernetes service](https://kubernetes.io/docs/concepts/services-networking/service/) of type `ExternalName`. | | No | +| `services[m].`
`sticky.`
`cookie.name`
| Name of the cookie used for the stickiness.
Evaluated only if the kind is **Service**. | Abbreviation of a sha1
(ex: `_1d52e`). | No | +| `services[m].`
`sticky.`
`cookie.httpOnly`
| Allow the cookie can be accessed by client-side APIs, such as JavaScript.
Evaluated only if the kind is **Service**. | false | No | +| `services[m].`
`sticky.`
`cookie.secure`
| Allow the cookie can only be transmitted over an encrypted connection (i.e. HTTPS).
Evaluated only if the kind is **Service**. | false | No | +| `services[m].`
`sticky.`
`cookie.sameSite`
| [SameSite](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite) policy.
Allowed values:
-`none`
-`lax`
`strict`
Evaluated only if the kind is **Service**. | "" | No | +| `services[m].`
`sticky.`
`cookie.maxAge`
| Number of seconds until the cookie expires.
Negative number, the cookie expires immediately.
0, the cookie never expires.
Evaluated only if the kind is **Service**. | 0 | No | +| `services[m].`
`strategy`
| Load balancing strategy between the servers.
RoundRobin is the only supported value yet.
Evaluated only if the kind is **Service**. | "RoundRobin" | No | +| `services[m].`
`nativeLB`
| Allow using the Kubernetes Service load balancing between the pods instead of the one provided by Traefik.
Evaluated only if the kind is **Service**. | false | No | +| `services[m].`
`nodePortLB`
| Use the nodePort IP address when the service type is NodePort.
It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes.
Evaluated only if the kind is **Service**. | false | No | + +### How HRW Works + +The Highest Random Weight algorithm combines consistent hashing with weighted load balancing: + +1. **Consistent Hashing**: For each incoming request, the client's remote address is hashed to ensure consistent routing. +2. **Weighted Selection**: Each service is assigned a random value based on both the hash of the client IP and the service's weight. +3. **Highest Selection**: The service with the highest calculated value receives the request. + +This approach provides several benefits: + +- **Session Affinity**: Clients consistently reach the same backend service +- **Weighted Distribution**: Services with higher weights are more likely to be selected +- **No State Required**: Unlike sticky cookies, no client-side or server-side state is needed +- **Fault Tolerance**: If a service becomes unavailable, requests are redistributed consistently among remaining services + ## Mirroring The mirroring is able to mirror requests sent to a service to other services. diff --git a/docs/content/reference/routing-configuration/kubernetes/gateway-api.md b/docs/content/reference/routing-configuration/kubernetes/gateway-api.md index a25e147c4..675fa7a8d 100644 --- a/docs/content/reference/routing-configuration/kubernetes/gateway-api.md +++ b/docs/content/reference/routing-configuration/kubernetes/gateway-api.md @@ -8,11 +8,12 @@ description: "The Kubernetes Gateway API can be used as a provider for routing a When using the Kubernetes Gateway API provider, Traefik leverages the Gateway API Custom Resource Definitions (CRDs) to obtain its routing configuration. For detailed information on the Gateway API concepts and resources, refer to the official [documentation](https://gateway-api.sigs.k8s.io/). -The Kubernetes Gateway API provider supports version [v1.2.1](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.2.1) of the specification. +The Kubernetes Gateway API provider supports version [v1.4.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.4.0) of the specification. -It fully supports all `HTTPRoute` core and some extended features, like `GRPCRoute`, as well as the `TCPRoute` and `TLSRoute` resources from the [Experimental channel](https://gateway-api.sigs.k8s.io/concepts/versioning/?h=#release-channels). +It fully supports all `HTTPRoute` core and some extended features, like `BackendTLSPolicy`, and `GRPCRoute` resources from the [Standard channel](https://gateway-api.sigs.k8s.io/concepts/versioning/?h=#release-channels), as well as `TCPRoute`, and `TLSRoute` resources from the [Experimental channel](https://gateway-api.sigs.k8s.io/concepts/versioning/?h=#release-channels). + +For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.4.0/traefik-traefik). -For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.2.1/traefik-traefik). ## Deploying a Gateway @@ -762,4 +763,4 @@ spec: ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/kubernetes/ingress-nginx.md b/docs/content/reference/routing-configuration/kubernetes/ingress-nginx.md index af98d1b2b..ead159e18 100644 --- a/docs/content/reference/routing-configuration/kubernetes/ingress-nginx.md +++ b/docs/content/reference/routing-configuration/kubernetes/ingress-nginx.md @@ -5,21 +5,31 @@ description: "Understand the routing configuration for the Kubernetes Ingress NG # Traefik & Ingresses with NGINX Annotations -The experimental Kubernetes Controller for Ingresses with NGINX annotations. +Enable seamless migration from NGINX Ingress Controller to Traefik with NGINX annotation compatibility. {: .subtitle } -!!! warning "Ingress Discovery" +!!! warning "NGINX Ingress Controller Retirement" - The Kubernetes Ingress NGINX provider is discovering by default all Ingresses in the cluster, - which may lead to duplicated routers if you are also using the Kubernetes Ingress provider. - We recommend to use IngressClass for the Ingresses you want to be handled by this provider, - or to use the `watchNamespace` or `watchNamespaceSelector` options to limit the discovery of Ingresses to a specific namespace or set of namespaces. + The Kubernetes NGINX Ingress Controller project has announced its retirement in **March 2026** and will no longer receive updates or security patches. + Traefik provides a migration path by supporting NGINX annotations, allowing you to transition your workloads without rewriting all your Ingress configurations. + + **→ See the [NGINX to Traefik Migration Guide](../../../migrate/nginx-to-traefik.md) for step-by-step instructions.** + + For more information about the NGINX Ingress Controller retirement, see the [official Kubernetes blog announcement](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement). + +## Ingress Discovery + +This provider discovers all Ingresses in the cluster by default, which may lead to duplicated routers if you are also using the standard Kubernetes Ingress provider. + +**Best Practices:** + +- Use IngressClass to specify which Ingresses should be handled by this provider +- Configure `watchNamespace` to limit discovery to specific namespaces +- Use `watchNamespaceSelector` to target Ingresses based on namespace labels ## Routing Configuration -The Kubernetes Ingress NGINX provider watches for incoming ingresses events, such as the example below, -and derives the corresponding dynamic configuration from it, -which in turn will create the resulting routers, services, handlers, etc. +This provider watches for incoming Ingress events and automatically translates NGINX annotations into Traefik's dynamic configuration, creating the corresponding routers, services, middlewares, and other components needed to handle your traffic. ## Configuration Example @@ -138,7 +148,7 @@ which in turn will create the resulting routers, services, handlers, etc. serviceAccountName: traefik-ingress-controller containers: - name: traefik - image: traefik:v3.5 + image: traefik:v3.6 args: - --entryPoints.web.address=:80 - --providers.kubernetesingressnginx @@ -239,32 +249,13 @@ which in turn will create the resulting routers, services, handlers, etc. ## Annotations Support -This section lists all known NGINX Ingress annotations, split between those currently implemented (with limitations if any) and those not implemented. -Limitations or behavioral differences are indicated where relevant. +This section lists all known NGINX Ingress annotations. +The following annotations are organized by category for easier navigation. -!!! warning "Global configuration" - - Traefik does not expose all global configuration options to control default behaviors for ingresses. - - Some behaviors that are globally configurable in NGINX (such as default SSL redirect, rate limiting, or affinity) are currently not supported and cannot be overridden per-ingress as in NGINX. - -### Caveats and Key Behavioral Differences - -- **Authentication**: Forward auth behaves differently and session caching is not supported. NGINX supports sub-request based auth, while Traefik forwards the original request. -- **Session Affinity**: Only persistent mode is supported. -- **Leader Election**: Not supported; no cluster mode with leader election. -- **Default Backend**: Only `defaultBackend` in Ingress spec is supported; the annotation is ignored. -- **Load Balancing**: Only round_robin is supported; EWMA and IP hash are not supported. -- **CORS**: NGINX responds with all configured headers unconditionally; Traefik handles headers differently between pre-flight and regular requests. -- **TLS/Backend Protocols**: AUTO_HTTP, FCGI and some TLS options are not supported in Traefik. -- **Path Handling**: Traefik preserves trailing slashes by default; NGINX removes them unless configured otherwise. - -### Supported NGINX Annotations +### Authentication | Annotation | Limitations / Notes | |-------------------------------------------------------|--------------------------------------------------------------------------------------------| -| `nginx.ingress.kubernetes.io/affinity` | | -| `nginx.ingress.kubernetes.io/affinity-mode` | Only persistent mode supported; balanced/canary not supported. | | `nginx.ingress.kubernetes.io/auth-type` | | | `nginx.ingress.kubernetes.io/auth-secret` | | | `nginx.ingress.kubernetes.io/auth-secret-type` | | @@ -272,29 +263,72 @@ Limitations or behavioral differences are indicated where relevant. | `nginx.ingress.kubernetes.io/auth-url` | Only URL and response headers copy supported. Forward auth behaves differently than NGINX. | | `nginx.ingress.kubernetes.io/auth-method` | | | `nginx.ingress.kubernetes.io/auth-response-headers` | | + +### SSL/TLS + +| Annotation | Limitations / Notes | +|-------------------------------------------------------|--------------------------------------------------------------------------------------------| | `nginx.ingress.kubernetes.io/ssl-redirect` | Cannot opt-out per route if enabled globally. | | `nginx.ingress.kubernetes.io/force-ssl-redirect` | Cannot opt-out per route if enabled globally. | | `nginx.ingress.kubernetes.io/ssl-passthrough` | Some differences in SNI/default backend handling. | -| `nginx.ingress.kubernetes.io/use-regex` | | +| `nginx.ingress.kubernetes.io/proxy-ssl-server-name` | | +| `nginx.ingress.kubernetes.io/proxy-ssl-name` | | +| `nginx.ingress.kubernetes.io/proxy-ssl-verify` | | +| `nginx.ingress.kubernetes.io/proxy-ssl-secret` | | + +### Session Affinity + +| Annotation | Limitations / Notes | +|-------------------------------------------------------|--------------------------------------------------------------------------------------------| +| `nginx.ingress.kubernetes.io/affinity` | | +| `nginx.ingress.kubernetes.io/affinity-mode` | Only persistent mode supported; balanced/canary not supported. | | `nginx.ingress.kubernetes.io/session-cookie-name` | | +| `nginx.ingress.kubernetes.io/session-cookie-secure` | | | `nginx.ingress.kubernetes.io/session-cookie-path` | | | `nginx.ingress.kubernetes.io/session-cookie-domain` | | | `nginx.ingress.kubernetes.io/session-cookie-samesite` | | +| `nginx.ingress.kubernetes.io/session-cookie-max-age` | | + +### Load Balancing & Backend + +| Annotation | Limitations / Notes | +|-------------------------------------------------------|--------------------------------------------------------------------------------------------| | `nginx.ingress.kubernetes.io/load-balance` | Only round_robin supported; ewma and IP hash not supported. | | `nginx.ingress.kubernetes.io/backend-protocol` | FCGI and AUTO_HTTP not supported. | +| `nginx.ingress.kubernetes.io/service-upstream` | | + +### CORS + +| Annotation | Limitations / Notes | +|-------------------------------------------------------|--------------------------------------------------------------------------------------------| | `nginx.ingress.kubernetes.io/enable-cors` | Partial support. | | `nginx.ingress.kubernetes.io/cors-allow-credentials` | | | `nginx.ingress.kubernetes.io/cors-allow-headers` | | | `nginx.ingress.kubernetes.io/cors-allow-methods` | | | `nginx.ingress.kubernetes.io/cors-allow-origin` | | +| `nginx.ingress.kubernetes.io/cors-expose-headers` | | | `nginx.ingress.kubernetes.io/cors-max-age` | | -| `nginx.ingress.kubernetes.io/proxy-ssl-server-name` | | -| `nginx.ingress.kubernetes.io/proxy-ssl-name` | | -| `nginx.ingress.kubernetes.io/proxy-ssl-verify` | | -| `nginx.ingress.kubernetes.io/proxy-ssl-secret` | | -| `nginx.ingress.kubernetes.io/service-upstream` | | -### Unsupported NGINX Annotations +### Routing + +| Annotation | Limitations / Notes | +|-------------------------------------------------------|--------------------------------------------------------------------------------------------| +| `nginx.ingress.kubernetes.io/use-regex` | | + +## Limitations + +### Caveats and Key Behavioral Differences + +- **Authentication**: Forward auth behaves differently and session caching is not supported. NGINX supports sub-request based auth, while Traefik forwards the original request. +- **Session Affinity**: Only persistent mode is supported. +- **Leader Election**: Not supported; no cluster mode with leader election. +- **Default Backend**: Only defaultBackend in Ingress spec is supported; the annotation is ignored. +- **Load Balancing**: Only round_robin is supported; EWMA and IP hash are not supported. +- **CORS**: NGINX responds with all configured headers unconditionally; Traefik handles headers differently between pre-flight and regular requests. +- **TLS/Backend Protocols**: AUTO_HTTP, FCGI and some TLS options are not supported in Traefik. +- **Path Handling**: Traefik preserves trailing slashes by default; NGINX removes them unless configured otherwise + +### Unsupported Annotations !!! question "Want to Add Support for More Annotations?" @@ -305,98 +339,104 @@ Limitations or behavioral differences are indicated where relevant. All contributions and suggestions are welcome — let's build this together! - | Annotation | Notes | |-----------------------------------------------------------------------------|------------------------------------------------------| -| `nginx.ingress.kubernetes.io/app-root` | Not supported yet. | -| `nginx.ingress.kubernetes.io/affinity-canary-behavior` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-tls-secret` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-tls-verify-depth` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-tls-verify-client` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-tls-error-page` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-tls-match-cn` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-cache-key` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-cache-duration` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-keepalive` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-keepalive-share-vars` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-keepalive-requests` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-keepalive-timeout` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-proxy-set-headers` | Not supported yet. | -| `nginx.ingress.kubernetes.io/auth-snippet` | Not supported yet. | -| `nginx.ingress.kubernetes.io/enable-global-auth` | Not supported yet. | -| `nginx.ingress.kubernetes.io/canary` | Not supported yet. | -| `nginx.ingress.kubernetes.io/canary-by-header` | Not supported yet. | -| `nginx.ingress.kubernetes.io/canary-by-header-value` | Not supported yet. | -| `nginx.ingress.kubernetes.io/canary-by-header-pattern` | Not supported yet. | -| `nginx.ingress.kubernetes.io/canary-by-cookie` | Not supported yet. | -| `nginx.ingress.kubernetes.io/canary-weight` | Not supported yet. | -| `nginx.ingress.kubernetes.io/canary-weight-total` | Not supported yet. | -| `nginx.ingress.kubernetes.io/client-body-buffer-size` | Not supported yet. | -| `nginx.ingress.kubernetes.io/configuration-snippet` | Not supported yet. | -| `nginx.ingress.kubernetes.io/custom-http-errors` | Not supported yet. | -| `nginx.ingress.kubernetes.io/disable-proxy-intercept-errors` | Not supported yet. | -| `nginx.ingress.kubernetes.io/default-backend` | Not supported yet; use `defaultBackend` in Ingress spec. | -| `nginx.ingress.kubernetes.io/limit-rate-after` | Not supported yet. | -| `nginx.ingress.kubernetes.io/limit-rate` | Not supported yet. | -| `nginx.ingress.kubernetes.io/limit-whitelist` | Not supported yet. | -| `nginx.ingress.kubernetes.io/limit-rps` | Not supported yet. | -| `nginx.ingress.kubernetes.io/limit-rpm` | Not supported yet. | -| `nginx.ingress.kubernetes.io/limit-burst-multiplier` | Not supported yet. | -| `nginx.ingress.kubernetes.io/limit-connections` | Not supported yet. | -| `nginx.ingress.kubernetes.io/global-rate-limit` | Not supported yet. | -| `nginx.ingress.kubernetes.io/global-rate-limit-window` | Not supported yet. | -| `nginx.ingress.kubernetes.io/global-rate-limit-key` | Not supported yet. | -| `nginx.ingress.kubernetes.io/global-rate-limit-ignored-cidrs` | Not supported yet. | -| `nginx.ingress.kubernetes.io/permanent-redirect` | Not supported yet. | -| `nginx.ingress.kubernetes.io/permanent-redirect-code` | Not supported yet. | -| `nginx.ingress.kubernetes.io/temporal-redirect` | Not supported yet. | -| `nginx.ingress.kubernetes.io/preserve-trailing-slash` | Not supported yet; Traefik preserves by default. | -| `nginx.ingress.kubernetes.io/proxy-cookie-domain` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-cookie-path` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-connect-timeout` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-send-timeout` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-read-timeout` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-next-upstream` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-next-upstream-timeout` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-next-upstream-tries` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-request-buffering` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-redirect-from` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-redirect-to` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-http-version` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-ssl-ciphers` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-ssl-verify-depth` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-ssl-protocols` | Not supported yet. | -| `nginx.ingress.kubernetes.io/enable-rewrite-log` | Not supported yet. | -| `nginx.ingress.kubernetes.io/rewrite-target` | Not supported yet. | -| `nginx.ingress.kubernetes.io/satisfy` | Not supported yet. | -| `nginx.ingress.kubernetes.io/server-alias` | Not supported yet. | -| `nginx.ingress.kubernetes.io/server-snippet` | Not supported yet. | -| `nginx.ingress.kubernetes.io/session-cookie-conditional-samesite-none` | Not supported yet. | -| `nginx.ingress.kubernetes.io/session-cookie-expires` | Not supported yet. | -| `nginx.ingress.kubernetes.io/session-cookie-change-on-failure` | Not supported yet. | -| `nginx.ingress.kubernetes.io/ssl-ciphers` | Not supported yet. | -| `nginx.ingress.kubernetes.io/ssl-prefer-server-ciphers` | Not supported yet. | -| `nginx.ingress.kubernetes.io/connection-proxy-header` | Not supported yet. | -| `nginx.ingress.kubernetes.io/enable-access-log` | Not supported yet. | -| `nginx.ingress.kubernetes.io/enable-opentracing` | Not supported yet. | -| `nginx.ingress.kubernetes.io/opentracing-trust-incoming-span` | Not supported yet. | -| `nginx.ingress.kubernetes.io/enable-opentelemetry` | Not supported yet. | -| `nginx.ingress.kubernetes.io/opentelemetry-trust-incoming-span` | Not supported yet. | -| `nginx.ingress.kubernetes.io/enable-modsecurity` | Not supported yet. | -| `nginx.ingress.kubernetes.io/enable-owasp-core-rules` | Not supported yet. | -| `nginx.ingress.kubernetes.io/modsecurity-transaction-id` | Not supported yet. | -| `nginx.ingress.kubernetes.io/modsecurity-snippet` | Not supported yet. | -| `nginx.ingress.kubernetes.io/mirror-request-body` | Not supported yet. | -| `nginx.ingress.kubernetes.io/mirror-target` | Not supported yet. | -| `nginx.ingress.kubernetes.io/mirror-host` | Not supported yet. | -| `nginx.ingress.kubernetes.io/x-forwarded-prefix` | Not supported yet. | -| `nginx.ingress.kubernetes.io/upstream-hash-by` | Not supported yet. | -| `nginx.ingress.kubernetes.io/upstream-vhost` | Not supported yet. | -| `nginx.ingress.kubernetes.io/denylist-source-range` | Not supported yet. | -| `nginx.ingress.kubernetes.io/whitelist-source-range` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-buffering` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-buffers-number` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-buffer-size` | Not supported yet. | -| `nginx.ingress.kubernetes.io/proxy-max-temp-file-size` | Not supported yet. | -| `nginx.ingress.kubernetes.io/stream-snippet` | Not supported yet. | +| `nginx.ingress.kubernetes.io/app-root` | | +| `nginx.ingress.kubernetes.io/affinity-canary-behavior` | | +| `nginx.ingress.kubernetes.io/auth-signin` | | +| `nginx.ingress.kubernetes.io/auth-tls-secret` | | +| `nginx.ingress.kubernetes.io/auth-tls-verify-depth` | | +| `nginx.ingress.kubernetes.io/auth-tls-verify-client` | | +| `nginx.ingress.kubernetes.io/auth-tls-error-page` | | +| `nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream` | | +| `nginx.ingress.kubernetes.io/auth-tls-match-cn` | | +| `nginx.ingress.kubernetes.io/auth-cache-key` | | +| `nginx.ingress.kubernetes.io/auth-cache-duration` | | +| `nginx.ingress.kubernetes.io/auth-keepalive` | | +| `nginx.ingress.kubernetes.io/auth-keepalive-share-vars` | | +| `nginx.ingress.kubernetes.io/auth-keepalive-requests` | | +| `nginx.ingress.kubernetes.io/auth-keepalive-timeout` | | +| `nginx.ingress.kubernetes.io/auth-proxy-set-headers` | | +| `nginx.ingress.kubernetes.io/auth-snippet` | | +| `nginx.ingress.kubernetes.io/enable-global-auth` | | +| `nginx.ingress.kubernetes.io/canary` | | +| `nginx.ingress.kubernetes.io/canary-by-header` | | +| `nginx.ingress.kubernetes.io/canary-by-header-value` | | +| `nginx.ingress.kubernetes.io/canary-by-header-pattern` | | +| `nginx.ingress.kubernetes.io/canary-by-cookie` | | +| `nginx.ingress.kubernetes.io/canary-weight` | | +| `nginx.ingress.kubernetes.io/canary-weight-total` | | +| `nginx.ingress.kubernetes.io/client-body-buffer-size` | | +| `nginx.ingress.kubernetes.io/configuration-snippet` | | +| `nginx.ingress.kubernetes.io/custom-http-errors` | | +| `nginx.ingress.kubernetes.io/disable-proxy-intercept-errors` | | +| `nginx.ingress.kubernetes.io/default-backend` | Use `defaultBackend` in Ingress spec. | +| `nginx.ingress.kubernetes.io/limit-rate-after` | | +| `nginx.ingress.kubernetes.io/limit-rate` | | +| `nginx.ingress.kubernetes.io/limit-whitelist` | | +| `nginx.ingress.kubernetes.io/limit-rps` | | +| `nginx.ingress.kubernetes.io/limit-rpm` | | +| `nginx.ingress.kubernetes.io/limit-burst-multiplier` | | +| `nginx.ingress.kubernetes.io/limit-connections` | | +| `nginx.ingress.kubernetes.io/global-rate-limit` | | +| `nginx.ingress.kubernetes.io/global-rate-limit-window` | | +| `nginx.ingress.kubernetes.io/global-rate-limit-key` | | +| `nginx.ingress.kubernetes.io/global-rate-limit-ignored-cidrs` | | +| `nginx.ingress.kubernetes.io/permanent-redirect` | | +| `nginx.ingress.kubernetes.io/permanent-redirect-code` | | +| `nginx.ingress.kubernetes.io/temporal-redirect` | | +| `nginx.ingress.kubernetes.io/preserve-trailing-slash` | Traefik preserves trailing slash by default. | +| `nginx.ingress.kubernetes.io/proxy-cookie-domain` | | +| `nginx.ingress.kubernetes.io/proxy-cookie-path` | | +| `nginx.ingress.kubernetes.io/proxy-connect-timeout` | | +| `nginx.ingress.kubernetes.io/proxy-send-timeout` | | +| `nginx.ingress.kubernetes.io/proxy-read-timeout` | | +| `nginx.ingress.kubernetes.io/proxy-next-upstream` | | +| `nginx.ingress.kubernetes.io/proxy-next-upstream-timeout` | | +| `nginx.ingress.kubernetes.io/proxy-next-upstream-tries` | | +| `nginx.ingress.kubernetes.io/proxy-request-buffering` | | +| `nginx.ingress.kubernetes.io/proxy-redirect-from` | | +| `nginx.ingress.kubernetes.io/proxy-redirect-to` | | +| `nginx.ingress.kubernetes.io/proxy-http-version` | | +| `nginx.ingress.kubernetes.io/proxy-ssl-ciphers` | | +| `nginx.ingress.kubernetes.io/proxy-ssl-verify-depth` | | +| `nginx.ingress.kubernetes.io/proxy-ssl-protocols` | | +| `nginx.ingress.kubernetes.io/enable-rewrite-log` | | +| `nginx.ingress.kubernetes.io/rewrite-target` | | +| `nginx.ingress.kubernetes.io/satisfy` | | +| `nginx.ingress.kubernetes.io/server-alias` | | +| `nginx.ingress.kubernetes.io/server-snippet` | | +| `nginx.ingress.kubernetes.io/session-cookie-conditional-samesite-none` | | +| `nginx.ingress.kubernetes.io/session-cookie-expires` | | +| `nginx.ingress.kubernetes.io/session-cookie-change-on-failure` | | +| `nginx.ingress.kubernetes.io/ssl-ciphers` | | +| `nginx.ingress.kubernetes.io/ssl-prefer-server-ciphers` | | +| `nginx.ingress.kubernetes.io/connection-proxy-header` | | +| `nginx.ingress.kubernetes.io/enable-access-log` | | +| `nginx.ingress.kubernetes.io/enable-opentracing` | | +| `nginx.ingress.kubernetes.io/opentracing-trust-incoming-span` | | +| `nginx.ingress.kubernetes.io/enable-opentelemetry` | | +| `nginx.ingress.kubernetes.io/opentelemetry-trust-incoming-span` | | +| `nginx.ingress.kubernetes.io/enable-modsecurity` | | +| `nginx.ingress.kubernetes.io/enable-owasp-core-rules` | | +| `nginx.ingress.kubernetes.io/modsecurity-transaction-id` | | +| `nginx.ingress.kubernetes.io/modsecurity-snippet` | | +| `nginx.ingress.kubernetes.io/mirror-request-body` | | +| `nginx.ingress.kubernetes.io/mirror-target` | | +| `nginx.ingress.kubernetes.io/mirror-host` | | +| `nginx.ingress.kubernetes.io/x-forwarded-prefix` | | +| `nginx.ingress.kubernetes.io/upstream-hash-by` | | +| `nginx.ingress.kubernetes.io/upstream-vhost` | | +| `nginx.ingress.kubernetes.io/denylist-source-range` | | +| `nginx.ingress.kubernetes.io/whitelist-source-range` | | +| `nginx.ingress.kubernetes.io/proxy-buffering` | | +| `nginx.ingress.kubernetes.io/proxy-buffers-number` | | +| `nginx.ingress.kubernetes.io/proxy-buffer-size` | | +| `nginx.ingress.kubernetes.io/proxy-max-temp-file-size` | | +| `nginx.ingress.kubernetes.io/stream-snippet` | | + +### Global Configuration + +Traefik does not expose all global configuration options to control default behaviors for Ingresses in the same way NGINX does. + +Some behaviors that are globally configurable in NGINX (such as default SSL redirect, rate limiting, or affinity) are currently not supported and cannot be overridden per-Ingress as in NGINX. These limitations are noted in the annotation tables below where applicable. diff --git a/docs/content/reference/routing-configuration/kubernetes/ingress.md b/docs/content/reference/routing-configuration/kubernetes/ingress.md index a9b0aab71..f614b80b9 100644 --- a/docs/content/reference/routing-configuration/kubernetes/ingress.md +++ b/docs/content/reference/routing-configuration/kubernetes/ingress.md @@ -402,7 +402,7 @@ This way, any Ingress attached to this Entrypoint will have TLS termination by d serviceAccountName: traefik-ingress-controller containers: - name: traefik - image: traefik:v3.5 + image: traefik:v3.6 args: - --entryPoints.websecure.address=:443 - --entryPoints.websecure.http.tls @@ -619,4 +619,4 @@ This will allow users to create a "default router" that will match all unmatched To do this, use the `traefik.ingress.kubernetes.io/router.priority` annotation (as seen in [Annotations on Ingress](#on-ingress)) on your ingresses accordingly. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/kubernetes/knative.md b/docs/content/reference/routing-configuration/kubernetes/knative.md new file mode 100644 index 000000000..c4ac719a0 --- /dev/null +++ b/docs/content/reference/routing-configuration/kubernetes/knative.md @@ -0,0 +1,96 @@ +--- +title: "Traefik Knative Documentation" +description: "The Knative provider can be used for routing and load balancing in Traefik Proxy. View examples in the technical documentation." +--- + +# Traefik & Knative + +When using the Knative provider, Traefik leverages Knative's Custom Resource Definitions (CRDs) to obtain its routing configuration. +For detailed information on Knative concepts and resources, refer to the official [documentation](https://knative.dev/docs/). + +The Knative provider supports version [v1.19.0](https://github.com/knative/serving/releases/tag/knative-v1.19.0) of the specification. + +## Deploying a Knative Service + +A `Service` is a core resource in the Knative specification that defines the entry point for traffic into a Knative application. +It is linked to a `Ingress`, which specifies the Knative networking controller responsible for managing and handling the traffic, +ensuring that it is directed to the appropriate Knative backend services. + +The following `Service` manifest configures the running Traefik controller to handle the incoming traffic. + +```yaml +--- +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + name: helloworld-go + namespace: default +spec: + template: + spec: + containers: + - image: gcr.io/knative-samples/helloworld-go + env: + - name: TARGET + value: "Go Sample v1" +``` + +Once everything is deployed, sending a `GET` request to the HTTP endpoint should return the following response: + +```shell +$ curl http://helloworld-go.default.example.com + +Hello Go Sample v1! +``` + +!!! Note + + The `example.com` domain is the public domain configured when deploying the Traefik controller. + Check out [the install configuration](../../install-configuration/providers/kubernetes/knative.md) for more details. + +### Tag based routing + +To add tag-based routing with percentage in Knative, you can define the `traffic` section in your `Service` manifest to include different revisions with specific tags and percentages. +Here is an example: + +```yaml +apiVersion: serving.knative.dev/v1 +kind: Service +metadata: + name: helloworld-go + namespace: default +spec: + template: + spec: + containers: + - image: gcr.io/knative-samples/helloworld-go + env: + - name: TARGET + value: "Go Sample v2" + traffic: + - tag: v1 + revisionName: helloworld-go-00001 + percent: 50 + - tag: v2 + revisionName: helloworld-go-00002 + percent: 50 +``` + +In this example: +- The `traffic` section specifies two revisions (`helloworld-go-00001` and `helloworld-go-00002`) with tags `v1` and `v2`, each receiving 50% of the traffic. +- The `tag` field allows you to route traffic to specific revisions using the tag. + +You can access the tagged revisions using these URLs: + +- `http://v1-helloworld-go.default.example.com` +- `http://v2-helloworld-go.default.example.com` + +Use the default URL to access percentage-based routing: + +- `http://helloworld-go.default.example.com` + +### HTTP/HTTPS + +Check out the Knative documentation for [HTTP/HTTPS configuration](https://knative.dev/docs/serving/encryption/external-domain-tls/#configure-external-domain-encryption). + +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/other-providers/docker.md b/docs/content/reference/routing-configuration/other-providers/docker.md index a6773b190..4cc633bd1 100644 --- a/docs/content/reference/routing-configuration/other-providers/docker.md +++ b/docs/content/reference/routing-configuration/other-providers/docker.md @@ -285,6 +285,15 @@ you'd add the label `traefik.http.services..loadbalancer.pa "traefik.http.services.myservice.loadbalancer.server.scheme=http" ``` +??? info "`traefik.http.services..loadbalancer.server.url`" + + Defines the service URL. + This option cannot be used in combination with `port` or `scheme` definition. + + ```yaml + traefik.http.services..loadbalancer.server.url=http://foobar:8080 + ``` + ??? info "`traefik.http.services..loadbalancer.serverstransport`" Allows to reference a ServersTransport resource that is defined either with the File provider or the Kubernetes CRD one. @@ -688,6 +697,27 @@ You can tell Traefik to consider (or not) the container by setting `traefik.enab This option overrides the value of `exposedByDefault`. +#### `traefik.docker.allownonrunning` + +```yaml +- "traefik.docker.allownonrunning=true" +``` + +By default, Traefik only considers containers in "running" state. +This option controls whether containers that are not in "running" state (e.g., stopped, paused, exited) should still be visible to Traefik for service discovery. + +When this label is set to true, Traefik will: + +- Keep the router and service configuration even when the container is not running +- Create services with empty backend server lists +- Return 503 Service Unavailable for requests to stopped containers (instead of 404 Not Found) +- Execute the full middleware chain, allowing middlewares to intercept requests + +!!! warning "Configuration Collision" + + As the `traefik.docker.allownonrunning` enables the discovery of all containers exposing this option disregarding their state, + if multiple stopped containers expose the same router but their configurations diverge, then the routers will be dropped. + #### `traefik.docker.network` ```yaml @@ -700,4 +730,5 @@ If a container is linked to several networks, be sure to set the proper network otherwise it will randomly pick one (depending on how docker is returning them). !!! warning + When deploying a stack from a compose file `stack`, the networks defined are prefixed with `stack`. diff --git a/docs/content/reference/routing-configuration/other-providers/file.toml b/docs/content/reference/routing-configuration/other-providers/file.toml index 985f96e21..9e00b358f 100644 --- a/docs/content/reference/routing-configuration/other-providers/file.toml +++ b/docs/content/reference/routing-configuration/other-providers/file.toml @@ -7,6 +7,7 @@ middlewares = ["foobar", "foobar"] service = "foobar" rule = "foobar" + parentRefs = ["foobar", "foobar"] ruleSyntax = "foobar" priority = 42 [http.routers.Router0.tls] @@ -30,6 +31,7 @@ middlewares = ["foobar", "foobar"] service = "foobar" rule = "foobar" + parentRefs = ["foobar", "foobar"] ruleSyntax = "foobar" priority = 42 [http.routers.Router1.tls] @@ -55,12 +57,23 @@ fallback = "foobar" [http.services.Service01.failover.healthCheck] [http.services.Service02] - [http.services.Service02.loadBalancer] + [http.services.Service02.highestRandomWeight] + + [[http.services.Service02.highestRandomWeight.services]] + name = "foobar" + weight = 42 + + [[http.services.Service02.highestRandomWeight.services]] + name = "foobar" + weight = 42 + [http.services.Service02.highestRandomWeight.healthCheck] + [http.services.Service03] + [http.services.Service03.loadBalancer] strategy = "foobar" passHostHeader = true serversTransport = "foobar" - [http.services.Service02.loadBalancer.sticky] - [http.services.Service02.loadBalancer.sticky.cookie] + [http.services.Service03.loadBalancer.sticky] + [http.services.Service03.loadBalancer.sticky.cookie] name = "foobar" secure = true httpOnly = true @@ -69,16 +82,16 @@ path = "foobar" domain = "foobar" - [[http.services.Service02.loadBalancer.servers]] + [[http.services.Service03.loadBalancer.servers]] url = "foobar" weight = 42 preservePath = true - [[http.services.Service02.loadBalancer.servers]] + [[http.services.Service03.loadBalancer.servers]] url = "foobar" weight = 42 preservePath = true - [http.services.Service02.loadBalancer.healthCheck] + [http.services.Service03.loadBalancer.healthCheck] scheme = "foobar" mode = "foobar" path = "foobar" @@ -90,37 +103,40 @@ timeout = "42s" hostname = "foobar" followRedirects = true - [http.services.Service02.loadBalancer.healthCheck.headers] + [http.services.Service03.loadBalancer.healthCheck.headers] name0 = "foobar" name1 = "foobar" - [http.services.Service02.loadBalancer.responseForwarding] + [http.services.Service03.loadBalancer.passiveHealthCheck] + failureWindow = "42s" + maxFailedAttempts = 42 + [http.services.Service03.loadBalancer.responseForwarding] flushInterval = "42s" - [http.services.Service03] - [http.services.Service03.mirroring] + [http.services.Service04] + [http.services.Service04.mirroring] service = "foobar" mirrorBody = true maxBodySize = 42 - [[http.services.Service03.mirroring.mirrors]] + [[http.services.Service04.mirroring.mirrors]] name = "foobar" percent = 42 - [[http.services.Service03.mirroring.mirrors]] + [[http.services.Service04.mirroring.mirrors]] name = "foobar" percent = 42 - [http.services.Service03.mirroring.healthCheck] - [http.services.Service04] - [http.services.Service04.weighted] + [http.services.Service04.mirroring.healthCheck] + [http.services.Service05] + [http.services.Service05.weighted] - [[http.services.Service04.weighted.services]] + [[http.services.Service05.weighted.services]] name = "foobar" weight = 42 - [[http.services.Service04.weighted.services]] + [[http.services.Service05.weighted.services]] name = "foobar" weight = 42 - [http.services.Service04.weighted.sticky] - [http.services.Service04.weighted.sticky.cookie] + [http.services.Service05.weighted.sticky] + [http.services.Service05.weighted.sticky.cookie] name = "foobar" secure = true httpOnly = true @@ -128,7 +144,7 @@ maxAge = 42 path = "foobar" domain = "foobar" - [http.services.Service04.weighted.healthCheck] + [http.services.Service05.weighted.healthCheck] [http.middlewares] [http.middlewares.Middleware01] [http.middlewares.Middleware01.addPrefix] @@ -464,6 +480,13 @@ tls = true [tcp.services.TCPService01.loadBalancer.proxyProtocol] version = 42 + [tcp.services.TCPService01.loadBalancer.healthCheck] + port = 42 + send = "foobar" + expect = "foobar" + interval = "42s" + unhealthyInterval = "42s" + timeout = "42s" [tcp.services.TCPService02] [tcp.services.TCPService02.weighted] @@ -474,6 +497,7 @@ [[tcp.services.TCPService02.weighted.services]] name = "foobar" weight = 42 + [tcp.services.TCPService02.weighted.healthCheck] [tcp.middlewares] [tcp.middlewares.TCPMiddleware01] [tcp.middlewares.TCPMiddleware01.ipAllowList] diff --git a/docs/content/reference/routing-configuration/other-providers/file.yaml b/docs/content/reference/routing-configuration/other-providers/file.yaml index 29822fddb..fdb8f2c1e 100644 --- a/docs/content/reference/routing-configuration/other-providers/file.yaml +++ b/docs/content/reference/routing-configuration/other-providers/file.yaml @@ -11,6 +11,9 @@ http: - foobar service: foobar rule: foobar + parentRefs: + - foobar + - foobar ruleSyntax: foobar priority: 42 tls: @@ -39,6 +42,9 @@ http: - foobar service: foobar rule: foobar + parentRefs: + - foobar + - foobar ruleSyntax: foobar priority: 42 tls: @@ -65,6 +71,14 @@ http: fallback: foobar healthCheck: {} Service02: + highestRandomWeight: + services: + - name: foobar + weight: 42 + - name: foobar + weight: 42 + healthCheck: {} + Service03: loadBalancer: sticky: cookie: @@ -98,11 +112,14 @@ http: headers: name0: foobar name1: foobar + passiveHealthCheck: + failureWindow: 42s + maxFailedAttempts: 42 passHostHeader: true responseForwarding: flushInterval: 42s serversTransport: foobar - Service03: + Service04: mirroring: service: foobar mirrorBody: true @@ -113,7 +130,7 @@ http: - name: foobar percent: 42 healthCheck: {} - Service04: + Service05: weighted: services: - name: foobar @@ -527,6 +544,13 @@ tcp: proxyProtocol: version: 42 terminationDelay: 42 + healthCheck: + port: 42 + send: foobar + expect: foobar + interval: 42s + unhealthyInterval: 42s + timeout: 42s TCPService02: weighted: services: @@ -534,6 +558,7 @@ tcp: weight: 42 - name: foobar weight: 42 + healthCheck: {} middlewares: TCPMiddleware01: ipAllowList: diff --git a/docs/content/reference/routing-configuration/other-providers/swarm.md b/docs/content/reference/routing-configuration/other-providers/swarm.md index 427484d8d..2885b1e48 100644 --- a/docs/content/reference/routing-configuration/other-providers/swarm.md +++ b/docs/content/reference/routing-configuration/other-providers/swarm.md @@ -301,6 +301,15 @@ you'd add the label `traefik.http.services..loadbalancer.pa - "traefik.http.services.myservice.loadbalancer.server.scheme=http" ``` +??? info "`traefik.http.services..loadbalancer.server.url`" + + Defines the service URL. + This option cannot be used in combination with `port` or `scheme` definition. + + ```yaml + traefik.http.services..loadbalancer.server.url=http://foobar:8080 + ``` + ??? info "`traefik.http.services..loadbalancer.server.weight`" Overrides the default weight. diff --git a/docs/content/reference/routing-configuration/tcp/routing/router.md b/docs/content/reference/routing-configuration/tcp/routing/router.md index 4761ebf5c..54eeb09d2 100644 --- a/docs/content/reference/routing-configuration/tcp/routing/router.md +++ b/docs/content/reference/routing-configuration/tcp/routing/router.md @@ -101,4 +101,4 @@ labels: - Router names should be descriptive and follow your naming conventions - In provider-specific configurations (Docker, Kubernetes), router names are often auto-generated based on service names and rules -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/tcp/service.md b/docs/content/reference/routing-configuration/tcp/service.md index d85a75102..e719ddf42 100644 --- a/docs/content/reference/routing-configuration/tcp/service.md +++ b/docs/content/reference/routing-configuration/tcp/service.md @@ -23,6 +23,12 @@ tcp: servers: - address: "xx.xx.xx.xx:xx" - address: "xx.xx.xx.xx:xx" + healthCheck: + send: "PING" + expect: "PONG" + interval: "10s" + timeout: "3s" + serversTransport: "customTransport@file" ``` ```toml tab="Structured (TOML)" @@ -32,26 +38,79 @@ tcp: address = "xx.xx.xx.xx:xx" [[tcp.services.my-service.loadBalancer.servers]] address = "xx.xx.xx.xx:xx" + + [tcp.services.my-service.loadBalancer.healthCheck] + send = "PING" + expect = "PONG" + interval = "10s" + timeout = "3s" + + serversTransport = "customTransport@file" ``` -## Configuration Options +```yaml tab="Labels" +labels: + - "traefik.tcp.services.my-service.loadBalancer.servers[0].address=xx.xx.xx.xx:xx" + - "traefik.tcp.services.my-service.loadBalancer.servers[1].address=xx.xx.xx.xx:xx" + - "traefik.tcp.services.my-service.loadBalancer.healthCheck.send=PING" + - "traefik.tcp.services.my-service.loadBalancer.healthCheck.expect=PONG" + - "traefik.tcp.services.my-service.loadBalancer.healthCheck.interval=10s" + - "traefik.tcp.services.my-service.loadBalancer.healthCheck.timeout=3s" + - "traefik.tcp.services.my-service.loadBalancer.serversTransport=customTransport@file" +``` + +```json tab="Tags" +{ + "Tags": [ + "traefik.tcp.services.my-service.loadBalancer.servers[0].address=xx.xx.xx.xx:xx", + "traefik.tcp.services.my-service.loadBalancer.servers[1].address=xx.xx.xx.xx:xx", + "traefik.tcp.services.my-service.loadBalancer.healthCheck.send=PING", + "traefik.tcp.services.my-service.loadBalancer.healthCheck.expect=PONG", + "traefik.tcp.services.my-service.loadBalancer.healthCheck.interval=10s", + "traefik.tcp.services.my-service.loadBalancer.healthCheck.timeout=3s", + "traefik.tcp.services.my-service.loadBalancer.serversTransport=customTransport@file" + ] +} +``` + +### Configuration Options | Field | Description | Default | |----------|------------------------------------------|--------- | | `servers` | Servers declare a single instance of your program. | "" | | `servers.address` | The address option (IP:Port) point to a specific instance. | "" | | `servers.tls` | The `tls` option determines whether to use TLS when dialing with the backend. | false | -| `servers.serversTransport` | `serversTransport` allows to reference a TCP [ServersTransport](./serverstransport.md configuration for the communication between Traefik and your servers. If no serversTransport is specified, the default@internal will be used. | "" | +| `serversTransport` | `serversTransport` allows to reference a TCP [ServersTransport](./serverstransport.md) configuration for the communication between Traefik and your servers. If no serversTransport is specified, the default@internal will be used. | "" | +| `healthCheck` | Configures health check to remove unhealthy servers from the load balancing rotation. See [HealthCheck](#health-check) for details. | | No | + +### Health Check + +The `healthCheck` option configures health check to remove unhealthy servers from the load balancing rotation. +Traefik will consider TCP servers healthy as long as the connection to the target server succeeds. +For advanced health checks, you can configure TCP payload exchange by specifying `send` and `expect` parameters. + +To propagate status changes (e.g. all servers of this service are down) upwards, HealthCheck must also be enabled on the parent(s) of this service. + +Below are the available options for the health check mechanism: + +| Field | Description | Default | Required | +|-------|-------------|---------|----------| +| `port` | Replaces the server address port for the health check endpoint. | | No | +| `send` | Defines the payload to send to the server during the health check. | "" | No | +| `expect` | Defines the expected response payload from the server. | "" | No | +| `interval` | Defines the frequency of the health check calls for healthy targets. | 30s | No | +| `unhealthyInterval` | Defines the frequency of the health check calls for unhealthy targets. When not defined, it defaults to the `interval` value. | 30s | No | +| `timeout` | Defines the maximum duration Traefik will wait for a health check connection before considering the server unhealthy. | 5s | No | ## Weighted Round Robin -The Weighted Round Robin (alias `WRR`) load-balancer of services is in charge of balancing the requests between multiple services based on provided weights. +The Weighted Round Robin (alias `WRR`) load-balancer of services is in charge of balancing the connections between multiple services based on provided weights. This strategy is only available to load balance between [services](./service.md) and not between servers. !!! info "Supported Providers" - This strategy can be defined currently with the [File](../../install-configuration/providers/others/file.md) or [IngressRoute](../../install-configuration/providers/kubernetes/kubernetes-crd.md) providers. + This strategy can be defined currently with the [File provider](../../install-configuration/providers/others/file.md). ```yaml tab="Structured (YAML)" tcp: @@ -95,4 +154,83 @@ tcp: [[tcp.services.appv2.loadBalancer.servers]] address = "private-ip-server-2:8080/" ``` - + +### Health Check + +HealthCheck enables automatic self-healthcheck for this service, i.e. whenever one of its children is reported as down, +this service becomes aware of it, and takes it into account (i.e. it ignores the down child) when running the load-balancing algorithm. +In addition, if the parent of this service also has HealthCheck enabled, this service reports to its parent any status change. + +!!! note "Behavior" + + If HealthCheck is enabled for a given service and any of its descendants does not have it enabled, the creation of the service will fail. + + HealthCheck on Weighted services can be defined currently only with the [File provider](../../install-configuration/providers/others/file.md). + +```yaml tab="Structured (YAML)" +## Dynamic configuration +tcp: + services: + app: + weighted: + healthCheck: {} + services: + - name: appv1 + weight: 3 + - name: appv2 + weight: 1 + + appv1: + loadBalancer: + healthCheck: + send: "PING" + expect: "PONG" + interval: 10s + timeout: 3s + servers: + - address: "192.168.1.10:6379" + + appv2: + loadBalancer: + healthCheck: + send: "PING" + expect: "PONG" + interval: 10s + timeout: 3s + servers: + - address: "192.168.1.11:6379" +``` + +```toml tab="Structured (TOML)" +## Dynamic configuration +[tcp.services] + [tcp.services.app] + [tcp.services.app.weighted.healthCheck] + [[tcp.services.app.weighted.services]] + name = "appv1" + weight = 3 + [[tcp.services.app.weighted.services]] + name = "appv2" + weight = 1 + + [tcp.services.appv1] + [tcp.services.appv1.loadBalancer] + [tcp.services.appv1.loadBalancer.healthCheck] + send = "PING" + expect = "PONG" + interval = "10s" + timeout = "3s" + [[tcp.services.appv1.loadBalancer.servers]] + address = "192.168.1.10:6379" + + [tcp.services.appv2] + [tcp.services.appv2.loadBalancer] + [tcp.services.appv2.loadBalancer.healthCheck] + send = "PING" + expect = "PONG" + interval = "10s" + timeout = "3s" + [[tcp.services.appv2.loadBalancer.servers]] + address = "192.168.1.11:6379" +``` + diff --git a/docs/content/reference/routing-configuration/tcp/tls.md b/docs/content/reference/routing-configuration/tcp/tls.md index ccaf54dfb..c4d45c79d 100644 --- a/docs/content/reference/routing-configuration/tcp/tls.md +++ b/docs/content/reference/routing-configuration/tcp/tls.md @@ -123,4 +123,4 @@ This provides fine-grained control over certificate generation and takes precede Every domain must have A/AAAA records pointing to Traefik. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/udp/routing/rules-priority.md b/docs/content/reference/routing-configuration/udp/routing/rules-priority.md index a43d3653e..54e5f4000 100644 --- a/docs/content/reference/routing-configuration/udp/routing/rules-priority.md +++ b/docs/content/reference/routing-configuration/udp/routing/rules-priority.md @@ -109,4 +109,4 @@ labels: There must be one (and only one) UDP [service](../service.md) referenced per UDP router. Services are the target for the router. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/routing-configuration/udp/service.md b/docs/content/reference/routing-configuration/udp/service.md index f792d0b2c..671ce6283 100644 --- a/docs/content/reference/routing-configuration/udp/service.md +++ b/docs/content/reference/routing-configuration/udp/service.md @@ -41,4 +41,4 @@ udp: address = "xx.xx.xx.xx:xx" ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md index b06e80721..9f71fe776 100644 --- a/docs/content/reference/static-configuration/cli-ref.md +++ b/docs/content/reference/static-configuration/cli-ref.md @@ -135,6 +135,9 @@ Timeout for receiving the response headers when communicating with the ACME serv `--certificatesresolvers..acme.clienttimeout`: Timeout for a complete HTTP transaction with the ACME server. (Default: ```120```) +`--certificatesresolvers..acme.disablecommonname`: +Disable the common name in the CSR. (Default: ```false```) + `--certificatesresolvers..acme.dnschallenge`: Activate DNS-01 Challenge. (Default: ```false```) @@ -199,7 +202,10 @@ Certificate profile to use. Storage to use. (Default: ```acme.json```) `--certificatesresolvers..acme.tlschallenge`: -Activate TLS-ALPN-01 Challenge. (Default: ```true```) +Activate TLS-ALPN-01 Challenge. (Default: ```false```) + +`--certificatesresolvers..acme.tlschallenge.delay`: +Delay between the creation of the challenge and the validation. (Default: ```0```) `--certificatesresolvers..tailscale`: Enables Tailscale certificate resolution. (Default: ```true```) @@ -231,6 +237,27 @@ Trust only forwarded headers from selected IPs. `--entrypoints..http`: HTTP configuration. +`--entrypoints..http.encodedcharacters.allowencodedbackslash`: +Defines whether requests with encoded back slash characters in the path are allowed. (Default: ```true```) + +`--entrypoints..http.encodedcharacters.allowencodedhash`: +Defines whether requests with encoded hash characters in the path are allowed. (Default: ```true```) + +`--entrypoints..http.encodedcharacters.allowencodednullcharacter`: +Defines whether requests with encoded null characters in the path are allowed. (Default: ```true```) + +`--entrypoints..http.encodedcharacters.allowencodedpercent`: +Defines whether requests with encoded percent characters in the path are allowed. (Default: ```true```) + +`--entrypoints..http.encodedcharacters.allowencodedquestionmark`: +Defines whether requests with encoded question mark characters in the path are allowed. (Default: ```true```) + +`--entrypoints..http.encodedcharacters.allowencodedsemicolon`: +Defines whether requests with encoded semicolon characters in the path are allowed. (Default: ```true```) + +`--entrypoints..http.encodedcharacters.allowencodedslash`: +Defines whether requests with encoded slash characters in the path are allowed. (Default: ```true```) + `--entrypoints..http.encodequerysemicolons`: Defines whether request query semicolons should be URLEncoded. (Default: ```false```) @@ -361,7 +388,7 @@ Environment variables to forward to the wasm guest. Directory to mount to the wasm guest. `--experimental.localplugins..settings.useunsafe`: -Allow the plugin to use unsafe package. (Default: ```false```) +Allow the plugin to use unsafe and syscall packages. (Default: ```false```) `--experimental.otlplogs`: Enables the OpenTelemetry logs integration. (Default: ```false```) @@ -379,7 +406,7 @@ Environment variables to forward to the wasm guest. Directory to mount to the wasm guest. `--experimental.plugins..settings.useunsafe`: -Allow the plugin to use unsafe package. (Default: ```false```) +Allow the plugin to use unsafe and syscall packages. (Default: ```false```) `--experimental.plugins..version`: plugin's version. diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md index 65c009454..daf50f481 100644 --- a/docs/content/reference/static-configuration/env-ref.md +++ b/docs/content/reference/static-configuration/env-ref.md @@ -135,6 +135,9 @@ Timeout for receiving the response headers when communicating with the ACME serv `TRAEFIK_CERTIFICATESRESOLVERS__ACME_CLIENTTIMEOUT`: Timeout for a complete HTTP transaction with the ACME server. (Default: ```120```) +`TRAEFIK_CERTIFICATESRESOLVERS__ACME_DISABLECOMMONNAME`: +Disable the common name in the CSR. (Default: ```false```) + `TRAEFIK_CERTIFICATESRESOLVERS__ACME_DNSCHALLENGE`: Activate DNS-01 Challenge. (Default: ```false```) @@ -199,7 +202,10 @@ Certificate profile to use. Storage to use. (Default: ```acme.json```) `TRAEFIK_CERTIFICATESRESOLVERS__ACME_TLSCHALLENGE`: -Activate TLS-ALPN-01 Challenge. (Default: ```true```) +Activate TLS-ALPN-01 Challenge. (Default: ```false```) + +`TRAEFIK_CERTIFICATESRESOLVERS__ACME_TLSCHALLENGE_DELAY`: +Delay between the creation of the challenge and the validation. (Default: ```0```) `TRAEFIK_CERTIFICATESRESOLVERS__TAILSCALE`: Enables Tailscale certificate resolution. (Default: ```true```) @@ -240,6 +246,27 @@ HTTP/3 configuration. (Default: ```false```) `TRAEFIK_ENTRYPOINTS__HTTP3_ADVERTISEDPORT`: UDP port to advertise, on which HTTP/3 is available. (Default: ```0```) +`TRAEFIK_ENTRYPOINTS__HTTP_ENCODEDCHARACTERS_ALLOWENCODEDBACKSLASH`: +Defines whether requests with encoded back slash characters in the path are allowed. (Default: ```true```) + +`TRAEFIK_ENTRYPOINTS__HTTP_ENCODEDCHARACTERS_ALLOWENCODEDHASH`: +Defines whether requests with encoded hash characters in the path are allowed. (Default: ```true```) + +`TRAEFIK_ENTRYPOINTS__HTTP_ENCODEDCHARACTERS_ALLOWENCODEDNULLCHARACTER`: +Defines whether requests with encoded null characters in the path are allowed. (Default: ```true```) + +`TRAEFIK_ENTRYPOINTS__HTTP_ENCODEDCHARACTERS_ALLOWENCODEDPERCENT`: +Defines whether requests with encoded percent characters in the path are allowed. (Default: ```true```) + +`TRAEFIK_ENTRYPOINTS__HTTP_ENCODEDCHARACTERS_ALLOWENCODEDQUESTIONMARK`: +Defines whether requests with encoded question mark characters in the path are allowed. (Default: ```true```) + +`TRAEFIK_ENTRYPOINTS__HTTP_ENCODEDCHARACTERS_ALLOWENCODEDSEMICOLON`: +Defines whether requests with encoded semicolon characters in the path are allowed. (Default: ```true```) + +`TRAEFIK_ENTRYPOINTS__HTTP_ENCODEDCHARACTERS_ALLOWENCODEDSLASH`: +Defines whether requests with encoded slash characters in the path are allowed. (Default: ```true```) + `TRAEFIK_ENTRYPOINTS__HTTP_ENCODEQUERYSEMICOLONS`: Defines whether request query semicolons should be URLEncoded. (Default: ```false```) @@ -361,7 +388,7 @@ Environment variables to forward to the wasm guest. Directory to mount to the wasm guest. `TRAEFIK_EXPERIMENTAL_LOCALPLUGINS__SETTINGS_USEUNSAFE`: -Allow the plugin to use unsafe package. (Default: ```false```) +Allow the plugin to use unsafe and syscall packages. (Default: ```false```) `TRAEFIK_EXPERIMENTAL_OTLPLOGS`: Enables the OpenTelemetry logs integration. (Default: ```false```) @@ -379,7 +406,7 @@ Environment variables to forward to the wasm guest. Directory to mount to the wasm guest. `TRAEFIK_EXPERIMENTAL_PLUGINS__SETTINGS_USEUNSAFE`: -Allow the plugin to use unsafe package. (Default: ```false```) +Allow the plugin to use unsafe and syscall packages. (Default: ```false```) `TRAEFIK_EXPERIMENTAL_PLUGINS__VERSION`: plugin's version. diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml index 7d22522e1..43f4cdc53 100644 --- a/docs/content/reference/static-configuration/file.toml +++ b/docs/content/reference/static-configuration/file.toml @@ -73,6 +73,14 @@ [[entryPoints.EntryPoint0.http.tls.domains]] main = "foobar" sans = ["foobar", "foobar"] + [entryPoints.EntryPoint0.http.encodedCharacters] + allowEncodedSlash = true + allowEncodedBackSlash = true + allowEncodedNullCharacter = true + allowEncodedSemicolon = true + allowEncodedPercent = true + allowEncodedQuestionMark = true + allowEncodedHash = true [entryPoints.EntryPoint0.http2] maxConcurrentStreams = 42 [entryPoints.EntryPoint0.http3] @@ -549,6 +557,7 @@ preferredChain = "foobar" profile = "foobar" emailAddresses = ["foobar", "foobar"] + disableCommonName = true storage = "foobar" keyType = "foobar" certificatesDuration = 42 @@ -574,6 +583,7 @@ entryPoint = "foobar" delay = "42s" [certificatesResolvers.CertificateResolver0.acme.tlsChallenge] + delay = "42s" [certificatesResolvers.CertificateResolver0.tailscale] [certificatesResolvers.CertificateResolver1] [certificatesResolvers.CertificateResolver1.acme] @@ -582,6 +592,7 @@ preferredChain = "foobar" profile = "foobar" emailAddresses = ["foobar", "foobar"] + disableCommonName = true storage = "foobar" keyType = "foobar" certificatesDuration = 42 @@ -607,6 +618,7 @@ entryPoint = "foobar" delay = "42s" [certificatesResolvers.CertificateResolver1.acme.tlsChallenge] + delay = "42s" [certificatesResolvers.CertificateResolver1.tailscale] [experimental] diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml index 64aae401b..11cf01c88 100644 --- a/docs/content/reference/static-configuration/file.yaml +++ b/docs/content/reference/static-configuration/file.yaml @@ -86,6 +86,14 @@ entryPoints: sans: - foobar - foobar + encodedCharacters: + allowEncodedSlash: true + allowEncodedBackSlash: true + allowEncodedNullCharacter: true + allowEncodedSemicolon: true + allowEncodedPercent: true + allowEncodedQuestionMark: true + allowEncodedHash: true encodeQuerySemicolons: true sanitizePath: true maxHeaderBytes: 42 @@ -590,6 +598,7 @@ certificatesResolvers: emailAddresses: - foobar - foobar + disableCommonName: true storage: foobar keyType: foobar eab: @@ -618,7 +627,8 @@ certificatesResolvers: httpChallenge: entryPoint: foobar delay: 42s - tlsChallenge: {} + tlsChallenge: + delay: 42s tailscale: {} CertificateResolver1: acme: @@ -629,6 +639,7 @@ certificatesResolvers: emailAddresses: - foobar - foobar + disableCommonName: true storage: foobar keyType: foobar eab: @@ -657,7 +668,8 @@ certificatesResolvers: httpChallenge: entryPoint: foobar delay: 42s - tlsChallenge: {} + tlsChallenge: + delay: 42s tailscale: {} experimental: plugins: diff --git a/docs/content/routing/entrypoints.md b/docs/content/routing/entrypoints.md index 6911f43b9..5a426f7a5 100644 --- a/docs/content/routing/entrypoints.md +++ b/docs/content/routing/entrypoints.md @@ -107,6 +107,8 @@ They can be defined by using a file (YAML or TOML) or CLI arguments. address: ":8888" # same as ":8888/tcp" http2: maxConcurrentStreams: 42 + maxDecoderHeaderTableSize: 42 + maxEncoderHeaderTableSize: 42 http3: advertisedPort: 8888 transport: @@ -127,6 +129,15 @@ They can be defined by using a file (YAML or TOML) or CLI arguments. trustedIPs: - "127.0.0.1" - "192.168.0.1" + http: + encodedCharacters: + allowEncodedSlash: false + allowEncodedBackSlash: false + allowEncodedNullCharacter: false + allowEncodedSemicolon: false + allowEncodedPercent: false + allowEncodedQuestionMark: false + allowEncodedHash: false ``` ```toml tab="File (TOML)" @@ -136,6 +147,8 @@ They can be defined by using a file (YAML or TOML) or CLI arguments. address = ":8888" # same as ":8888/tcp" [entryPoints.name.http2] maxConcurrentStreams = 42 + maxDecoderHeaderTableSize = 42 + maxEncoderHeaderTableSize = 42 [entryPoints.name.http3] advertisedPort = 8888 [entryPoints.name.transport] @@ -152,12 +165,22 @@ They can be defined by using a file (YAML or TOML) or CLI arguments. [entryPoints.name.forwardedHeaders] insecure = true trustedIPs = ["127.0.0.1", "192.168.0.1"] + [entryPoints.name.http.encodedCharacters] + allowEncodedSlash = false + allowEncodedBackSlash = false + allowEncodedNullCharacter = false + allowEncodedSemicolon = false + allowEncodedPercent = false + allowEncodedQuestionMark = false + allowEncodedHash = false ``` ```bash tab="CLI" ## Static configuration --entryPoints.name.address=:8888 # same as :8888/tcp --entryPoints.name.http2.maxConcurrentStreams=42 + --entryPoints.name.http2.maxDecoderHeaderTableSize=42 + --entryPoints.name.http2.maxEncoderHeaderTableSize=42 --entryPoints.name.http3.advertisedport=8888 --entryPoints.name.transport.lifeCycle.requestAcceptGraceTimeout=42 --entryPoints.name.transport.lifeCycle.graceTimeOut=42 @@ -168,6 +191,13 @@ They can be defined by using a file (YAML or TOML) or CLI arguments. --entryPoints.name.proxyProtocol.trustedIPs=127.0.0.1,192.168.0.1 --entryPoints.name.forwardedHeaders.insecure=true --entryPoints.name.forwardedHeaders.trustedIPs=127.0.0.1,192.168.0.1 + --entryPoints.name.http.encodedCharacters.allowEncodedSlash=false + --entryPoints.name.http.encodedCharacters.allowEncodedBackSlash=false + --entryPoints.name.http.encodedCharacters.allowEncodedNullCharacter=false + --entryPoints.name.http.encodedCharacters.allowEncodedSemicolon=false + --entryPoints.name.http.encodedCharacters.allowEncodedPercent=false + --entryPoints.name.http.encodedCharacters.allowEncodedQuestionMark=false + --entryPoints.name.http.encodedCharacters.allowEncodedHash=false ``` ### Address @@ -408,6 +438,52 @@ entryPoints: --entryPoints.name.http2.maxConcurrentStreams=250 ``` +#### `maxDecoderHeaderTableSize` + +_Optional, Default=4096_ + +`maxDecoderHeaderTableSize` specifies the maximum size of the HTTP2 HPACK header table on the decoding (receiving from client) side. + +```yaml tab="File (YAML)" +entryPoints: + foo: + http2: + maxDecoderHeaderTableSize: 4096 +``` + +```toml tab="File (TOML)" +[entryPoints.foo] + [entryPoints.foo.http2] + maxDecoderHeaderTableSize = 4096 +``` + +```bash tab="CLI" +--entryPoints.name.http2.maxDecoderHeaderTableSize=4096 +``` + +#### `maxEncoderHeaderTableSize` + +_Optional, Default=4096_ + +`maxEncoderHeaderTableSize` specifies the maximum size of the HTTP2 HPACK header table on the encoding (sending to client) side. + +```yaml tab="File (YAML)" +entryPoints: + foo: + http2: + maxEncoderHeaderTableSize: 4096 +``` + +```toml tab="File (TOML)" +[entryPoints.foo] + [entryPoints.foo.http2] + maxEncoderHeaderTableSize = 4096 +``` + +```bash tab="CLI" +--entryPoints.name.http2.maxEncoderHeaderTableSize=4096 +``` + ### HTTP/3 #### `http3` @@ -1101,6 +1177,245 @@ entryPoints: | false | foo=bar&baz=bar;foo | foo=bar&baz=bar&foo | | true | foo=bar&baz=bar;foo | foo=bar&baz=bar%3Bfoo | +### Encoded Characters + +You can configure Traefik to control the handling of encoded characters in request paths for security purposes. +By default, Traefik do not reject requests with path containing certain encoded characters that could be used in path traversal or other security attacks. + +!!! info + + This check is not done against the request query parameters, + but only against the request path as defined in [RFC3986 section-3](https://datatracker.ietf.org/doc/html/rfc3986#section-3). + +!!! info "Security Considerations" + + When your backend is not fully compliant with [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) and notably decode encoded reserved characters in the requets path, + it is recommended to set these options to `false` to avoid split-view situation and helps prevent path traversal attacks or other malicious attempts to bypass security controls. + +??? info "`encodedCharacters.allowEncodedSlash`" + + _Optional, Default=true_ + + Controls whether requests with encoded slash characters (`%2F` or `%2f`) in the path are allowed. + + ```yaml tab="File (YAML)" + ## Static configuration + entryPoints: + web: + address: ":80" + http: + encodedCharacters: + allowEncodedSlash: false + ``` + + ```toml tab="File (TOML)" + ## Static configuration + [entryPoints] + [entryPoints.web] + address = ":80" + + [entryPoints.web.http.encodedCharacters] + allowEncodedSlash = false + ``` + + ```bash tab="CLI" + ## Static configuration + --entryPoints.web.address=:80 + --entryPoints.web.http.encodedCharacters.allowEncodedSlash=false + ``` + +??? info "`encodedCharacters.allowEncodedBackSlash`" + + _Optional, Default=true_ + + Controls whether requests with encoded back slash characters (`%5C` or `%5c`) in the path are allowed. + + ```yaml tab="File (YAML)" + ## Static configuration + entryPoints: + web: + address: ":80" + http: + encodedCharacters: + allowEncodedBackSlash: false + ``` + + ```toml tab="File (TOML)" + ## Static configuration + [entryPoints] + [entryPoints.web] + address = ":80" + + [entryPoints.web.http.encodedCharacters] + allowEncodedBackSlash = false + ``` + + ```bash tab="CLI" + ## Static configuration + --entryPoints.web.address=:80 + --entryPoints.web.http.encodedCharacters.allowEncodedBackSlash=false + ``` + +??? info "`encodedCharacters.allowEncodedNullCharacter`" + + _Optional, Default=true_ + + Controls whether requests with encoded null characters (`%00`) in the path are allowed. + + ```yaml tab="File (YAML)" + ## Static configuration + entryPoints: + web: + address: ":80" + http: + encodedCharacters: + allowEncodedNullCharacter: false + ``` + + ```toml tab="File (TOML)" + ## Static configuration + [entryPoints] + [entryPoints.web] + address = ":80" + + [entryPoints.web.http.encodedCharacters] + allowEncodedNullCharacter = false + ``` + + ```bash tab="CLI" + ## Static configuration + --entryPoints.web.address=:80 + --entryPoints.web.http.encodedCharacters.allowEncodedNullCharacter=false + ``` + +??? info "`encodedCharacters.allowEncodedSemicolon`" + + _Optional, Default=true_ + + Controls whether requests with encoded semicolon characters (`%3B` or `%3b`) in the path are allowed. + + ```yaml tab="File (YAML)" + ## Static configuration + entryPoints: + web: + address: ":80" + http: + encodedCharacters: + allowEncodedSemicolon: false + ``` + + ```toml tab="File (TOML)" + ## Static configuration + [entryPoints] + [entryPoints.web] + address = ":80" + + [entryPoints.web.http.encodedCharacters] + allowEncodedSemicolon = false + ``` + + ```bash tab="CLI" + ## Static configuration + --entryPoints.web.address=:80 + --entryPoints.web.http.encodedCharacters.allowEncodedSemicolon=false + ``` + +??? info "`encodedCharacters.allowEncodedPercent`" + + _Optional, Default=true_ + + Controls whether requests with encoded percent characters (`%25`) in the path are allowed. + + ```yaml tab="File (YAML)" + ## Static configuration + entryPoints: + web: + address: ":80" + http: + encodedCharacters: + allowEncodedPercent: false + ``` + + ```toml tab="File (TOML)" + ## Static configuration + [entryPoints] + [entryPoints.web] + address = ":80" + + [entryPoints.web.http.encodedCharacters] + allowEncodedPercent = false + ``` + + ```bash tab="CLI" + ## Static configuration + --entryPoints.web.address=:80 + --entryPoints.web.http.encodedCharacters.allowEncodedPercent=false + ``` + +??? info "`encodedCharacters.allowEncodedQuestionMark`" + + _Optional, Default=true_ + + Controls whether requests with encoded question mark characters (`%3F` or `%3f`) in the path are allowed. + + ```yaml tab="File (YAML)" + ## Static configuration + entryPoints: + web: + address: ":80" + http: + encodedCharacters: + allowEncodedQuestionMark: false + ``` + + ```toml tab="File (TOML)" + ## Static configuration + [entryPoints] + [entryPoints.web] + address = ":80" + + [entryPoints.web.http.encodedCharacters] + allowEncodedQuestionMark = false + ``` + + ```bash tab="CLI" + ## Static configuration + --entryPoints.web.address=:80 + --entryPoints.web.http.encodedCharacters.allowEncodedQuestionMark=false + ``` + +??? info "`encodedCharacters.allowEncodedHash`" + + _Optional, Default=true_ + + Controls whether requests with encoded hash characters (`%23`) in the path are allowed. + + ```yaml tab="File (YAML)" + ## Static configuration + entryPoints: + web: + address: ":80" + http: + encodedCharacters: + allowEncodedHash: false + ``` + + ```toml tab="File (TOML)" + ## Static configuration + [entryPoints] + [entryPoints.web] + address = ":80" + + [entryPoints.web.http.encodedCharacters] + allowEncodedHash = false + ``` + + ```bash tab="CLI" + ## Static configuration + --entryPoints.web.address=:80 + --entryPoints.web.http.encodedCharacters.allowEncodedHash=false + ``` + ### SanitizePath _Optional, Default=true_ @@ -1408,4 +1723,4 @@ entryPoints: --entryPoints.foo.observability.tracing=false ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/routing/overview.md b/docs/content/routing/overview.md index dd1d0342f..989a2704a 100644 --- a/docs/content/routing/overview.md +++ b/docs/content/routing/overview.md @@ -651,4 +651,4 @@ tcpServersTransport: --tcpServersTransport.spiffe.trustDomain=spiffe://trust-domain ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/routing/providers/kubernetes-crd.md b/docs/content/routing/providers/kubernetes-crd.md index 095691e14..1fd5028ea 100644 --- a/docs/content/routing/providers/kubernetes-crd.md +++ b/docs/content/routing/providers/kubernetes-crd.md @@ -48,7 +48,7 @@ The Kubernetes Ingress Controller, The Custom Resource Way. serviceAccountName: traefik-ingress-controller containers: - name: traefik - image: traefik:v3.5 + image: traefik:v3.6 args: - --log.level=DEBUG - --api @@ -2039,4 +2039,4 @@ If the ServersTransportTCP CRD is defined in another provider the cross-provider Also see the [full example](../../user-guides/crd-acme/index.md) with Let's Encrypt. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/routing/providers/kubernetes-gateway.md b/docs/content/routing/providers/kubernetes-gateway.md index f658ca4ba..4ff202e50 100644 --- a/docs/content/routing/providers/kubernetes-gateway.md +++ b/docs/content/routing/providers/kubernetes-gateway.md @@ -8,11 +8,11 @@ description: "The Kubernetes Gateway API can be used as a provider for routing a When using the Kubernetes Gateway API provider, Traefik leverages the Gateway API Custom Resource Definitions (CRDs) to obtain its routing configuration. For detailed information on the Gateway API concepts and resources, refer to the official [documentation](https://gateway-api.sigs.k8s.io/). -The Kubernetes Gateway API provider supports version [v1.3.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.3.0) of the specification. +The Kubernetes Gateway API provider supports version [v1.4.0](https://github.com/kubernetes-sigs/gateway-api/releases/tag/v1.4.0) of the specification. It fully supports all `HTTPRoute` core and some extended features, like `GRPCRoute`, as well as the `TCPRoute` and `TLSRoute` resources from the [Experimental channel](https://gateway-api.sigs.k8s.io/concepts/versioning/?h=#release-channels). -For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.3.0/traefik-traefik). +For more details, check out the conformance [report](https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.4.0/traefik-traefik). ## Deploying a Gateway @@ -750,4 +750,4 @@ spec: [...] ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/routing/providers/kubernetes-ingress.md b/docs/content/routing/providers/kubernetes-ingress.md index ff02ae929..1d8f8e0ef 100644 --- a/docs/content/routing/providers/kubernetes-ingress.md +++ b/docs/content/routing/providers/kubernetes-ingress.md @@ -130,7 +130,7 @@ which in turn will create the resulting routers, services, handlers, etc. serviceAccountName: traefik-ingress-controller containers: - name: traefik - image: traefik:v3.5 + image: traefik:v3.6 args: - --entryPoints.web.address=:80 - --providers.kubernetesingress @@ -593,7 +593,7 @@ This way, any Ingress attached to this Entrypoint will have TLS termination by d serviceAccountName: traefik-ingress-controller containers: - name: traefik - image: traefik:v3.5 + image: traefik:v3.6 args: - --entryPoints.websecure.address=:443 - --entryPoints.websecure.http.tls @@ -786,7 +786,7 @@ For more options, please refer to the available [annotations](#on-ingress). serviceAccountName: traefik-ingress-controller containers: - name: traefik - image: traefik:v3.5 + image: traefik:v3.6 args: - --entryPoints.websecure.address=:443 - --providers.kubernetesingress @@ -960,4 +960,4 @@ This will allow users to create a "default router" that will match all unmatched To do this, use the `traefik.ingress.kubernetes.io/router.priority` annotation (as seen in [Annotations on Ingress](#on-ingress)) on your ingresses accordingly. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/routing/routers/index.md b/docs/content/routing/routers/index.md index 06cd3f05b..9b0807f07 100644 --- a/docs/content/routing/routers/index.md +++ b/docs/content/routing/routers/index.md @@ -1790,4 +1790,4 @@ Services are the target for the router. !!! important "UDP routers can only target UDP services (and not HTTP or TCP services)." -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/routing/services/index.md b/docs/content/routing/services/index.md index 3a4cc5c0a..7ae01c5c1 100644 --- a/docs/content/routing/services/index.md +++ b/docs/content/routing/services/index.md @@ -175,6 +175,7 @@ Two load balancing algorithms are supported: - Weighed round-robin (wrr) - Power of two choices (p2c) +- Highest Random Weight (hrw) ##### WRR @@ -242,6 +243,34 @@ Power of two choices algorithm is a load balancing strategy that selects two ser url = "http://private-ip-server-3/" ``` +##### HRW + +HighestRandomWeight, also called RendezVous hashing allows to loadbalance clients in a pool of services or servers. + +??? example "Load Balancing HRW with-- Using the [File Provider](../../providers/file.md)" + + ```yaml tab="YAML" + ## Dynamic configuration + http: + services: + my-service: + loadBalancer: + strategy: hrw + servers: + - url: "http://private-ip-server-1/" + - url: "http://private-ip-server-2/" + ``` + + ```toml tab="TOML" + ## Dynamic configuration + [http.services] + [http.services.my-service.loadBalancer] + [[http.services.my-service.loadBalancer.servers]] + url = "http://private-ip-server-1/" + [[http.services.my-service.loadBalancer.servers]] + url = "http://private-ip-server-2/" + ``` + #### Sticky sessions When sticky sessions are enabled, a `Set-Cookie` header is set on the initial response to let the client know which server handles the first response. @@ -1253,6 +1282,147 @@ http: url = "http://private-ip-server-2/" ``` +### Highest Random Weight (service) + +The HRW is able to load balance the requests between multiple services based on weights. + +This strategy is only available to load balance between [services](./index.md) and not between [servers](./index.md#servers). + +!!! info "Supported Providers" + + This strategy can be defined currently with the [File](../../providers/file.md) or [IngressRoute](../../providers/kubernetes-crd.md) providers. + +```yaml tab="YAML" +## Dynamic configuration +http: + services: + app: + highestRandomWeight: + services: + - name: appv1 + weight: 3 + - name: appv2 + weight: 1 + + appv1: + loadBalancer: + strategy: hrw + servers: + - url: "http://private-ip-server-1/" + + appv2: + loadBalancer: + strategy: hrw + servers: + - url: "http://private-ip-server-2/" +``` + +```toml tab="TOML" +## Dynamic configuration +[http.services] + [http.services.app] + [[http.services.app.highestRandomWeight.services]] + name = "appv1" + weight = 3 + [[http.services.app.highestRandomWeight.services]] + name = "appv2" + weight = 1 + + [http.services.appv1] + [http.services.appv1.loadBalancer] + strategy = "hrw" + [[http.services.appv1.loadBalancer.servers]] + url = "http://private-ip-server-1/" + + [http.services.appv2] + [http.services.appv2.loadBalancer] + strategy = "hrw" + [[http.services.appv2.loadBalancer.servers]] + url = "http://private-ip-server-2/" +``` + +#### Health Check + +HealthCheck enables automatic self-healthcheck for this service, i.e. whenever +one of its children is reported as down, this service becomes aware of it, and +takes it into account (i.e. it ignores the down child) when running the +load-balancing algorithm. In addition, if the parent of this service also has +HealthCheck enabled, this service reports to its parent any status change. + +!!! info "All or nothing" + + If HealthCheck is enabled for a given service, but any of its descendants does + not have it enabled, the creation of the service will fail. + + HealthCheck on Weighted services can be defined currently only with the [File](../../providers/file.md) provider. + +```yaml tab="YAML" +## Dynamic configuration +http: + services: + app: + highestRandomWeight: + healthCheck: {} + services: + - name: appv1 + weight: 3 + - name: appv2 + weight: 1 + + appv1: + loadBalancer: + strategy: hrw + healthCheck: + path: /status + interval: 10s + timeout: 3s + servers: + - url: "http://private-ip-server-1/" + + appv2: + loadBalancer: + strategy: hrw + healthCheck: + path: /status + interval: 10s + timeout: 3s + servers: + - url: "http://private-ip-server-2/" +``` + +```toml tab="TOML" +## Dynamic configuration +[http.services] + [http.services.app] + [http.services.app.highestRandomWeight.healthCheck] + [[http.services.app.highestRandomWeight.services]] + name = "appv1" + weight = 3 + [[http.services.app.highestRandomWeight.services]] + name = "appv2" + weight = 1 + + [http.services.appv1] + [http.services.appv1.loadBalancer] + strategy="hrw" + [http.services.appv1.loadBalancer.healthCheck] + path = "/health" + interval = "10s" + timeout = "3s" + [[http.services.appv1.loadBalancer.servers]] + url = "http://private-ip-server-1/" + + [http.services.appv2] + [http.services.appv2.loadBalancer] + strategy="hrw" + [http.services.appv2.loadBalancer.healthCheck] + path = "/health" + interval = "10s" + timeout = "3s" + [[http.services.appv2.loadBalancer.servers]] + url = "http://private-ip-server-2/" +``` + ### Mirroring (service) The mirroring is able to mirror requests sent to a service to other services. @@ -2225,4 +2395,4 @@ udp: address = "private-ip-server-2:8080/" ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/secure/secure-api-access-with-jwt.md b/docs/content/secure/secure-api-access-with-jwt.md index 5e853a637..4c9b8eb65 100644 --- a/docs/content/secure/secure-api-access-with-jwt.md +++ b/docs/content/secure/secure-api-access-with-jwt.md @@ -201,4 +201,4 @@ spec: For example, the metadata recovered from the Identity Provider can be used to restrict the access to the applications. To do so, you can use the `claims` option, more information in the [dedicated section](../reference/routing-configuration/http/middlewares/jwt.md#claims). -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/secure/secure-api-access-with-oidc.md b/docs/content/secure/secure-api-access-with-oidc.md index f7b18def9..62347442c 100644 --- a/docs/content/secure/secure-api-access-with-oidc.md +++ b/docs/content/secure/secure-api-access-with-oidc.md @@ -107,4 +107,4 @@ spec: - Using a cookie ([Options `session`](../reference/routing-configuration/http/middlewares/oidc.md#configuration-options) (default behavior)) - Using a [Redis store](../reference/routing-configuration/http/middlewares/oidc.md#sessionstore). -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/secure/secure-api-access-with-waf.md b/docs/content/secure/secure-api-access-with-waf.md index 455839b99..6ca34e39a 100644 --- a/docs/content/secure/secure-api-access-with-waf.md +++ b/docs/content/secure/secure-api-access-with-waf.md @@ -187,4 +187,4 @@ spec: The WAF middleware supports extensive customization through Coraza directives. You can create custom rules, tune detection thresholds, configure logging levels, and integrate with external threat intelligence feeds. For comprehensive rule writing guidance, consult the [Coraza documentation](https://coraza.io/docs/tutorials/introduction/) and [OWASP CRS documentation](https://coreruleset.org/docs/). -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/security/content-length.md b/docs/content/security/content-length.md index fff161881..2e6c90706 100644 --- a/docs/content/security/content-length.md +++ b/docs/content/security/content-length.md @@ -3,7 +3,8 @@ title: "Content-Length" description: "Enforce strict Content‑Length validation in Traefik by streaming or full buffering to prevent truncated or over‑long requests and responses. Read the technical documentation." --- -Traefik acts as a streaming proxy. By default, it checks each chunk of data against the `Content-Length` header as it passes it on to the backend or client. This live check blocks truncated or over‑long streams without holding the entire message. +Traefik acts as a streaming proxy. By default, it checks each chunk of data against the `Content-Length` header as it passes it on to the backend or client. +This live check blocks truncated or over‑long streams without holding the entire message. If you need Traefik to read and verify the full body before any data moves on, add the [buffering middleware](../middlewares/http/buffering.md): @@ -20,5 +21,7 @@ With buffering enabled, Traefik will: - Compare the actual byte count to the `Content-Length` header. - Reject the message if the counts do not match. -!!!warning - Buffering adds overhead. Every request and response is held in full before forwarding, which can increase memory use and latency. Use it when strict content validation is critical to your security posture. +!!! warning + + Buffering adds overhead. Every request and response is held in full before forwarding, which can increase memory use and latency. + Use it when strict content validation is critical to your security posture. diff --git a/docs/content/security/tls-certs-in-multi-tenant-kubernetes.md b/docs/content/security/multi-tenant-kubernetes.md similarity index 55% rename from docs/content/security/tls-certs-in-multi-tenant-kubernetes.md rename to docs/content/security/multi-tenant-kubernetes.md index 8fd7ef959..5ff606282 100644 --- a/docs/content/security/tls-certs-in-multi-tenant-kubernetes.md +++ b/docs/content/security/multi-tenant-kubernetes.md @@ -1,13 +1,20 @@ --- -title: "TLS Certificates in Multi‑Tenant Kubernetes" -description: "Isolate TLS certificates in multi‑tenant clusters by keeping Secrets and routes in the same namespace and disabling cross‑namespace look‑ups in Traefik. Read the technical guidelines." +title: "Traefik in Multi-Tenant Kubernetes Clusterss" +description: "Traefik is not recommended for multi-tenant Kubernetes clusters due to TLS certificate management and broader isolation, traffic, and security concerns. Read the technical guidelines." --- -# TLS Certificates in Multi‑Tenant Kubernetes +# Traefik in Multi-Tenant Kubernetes Clusters -In a shared cluster, different teams can create `Ingress` or `IngressRoute` objects that Traefik consumes. +Traefik is primarily designed as a cluster-wide ingress controller. For this reason, when using the Kubernetes `Ingress` or `IngressRoute` specifications, **it is not recommended to use Traefik in multi-tenant Kubernetes clusters**, where multiple teams or tenants share the same cluster. -Traefik does not support multi-tenancy when using the Kubernetes `Ingress` or `IngressRoute` specifications due to the way TLS certificate management is handled. +The main reasons include: + +* **Resource visibility and isolation**: Traefik requires cluster-level permissions and watches resources across namespaces. Misconfigurations in one tenant’s resources may affect others. +* **Shared CRDs**: Advanced configuration resources, like Middleware or TLSOptions, are cluster-scoped. Conflicting definitions can impact multiple tenants. +* **Traffic and availability risks**: Routing rules, middleware, or heavy traffic from one tenant can interfere with others, affecting reliability and performance. +* **Observability and privacy**: Logs, metrics, and traces are shared by default, which may expose sensitive information across tenants. + +## TLS Certificates Management At the core of this limitation is the TLS Store, which holds all the TLS certificates used by Traefik. As this Store is global in Traefik, it is shared across all namespaces, meaning any `Ingress` or `IngressRoute` in the cluster can potentially reference or affect TLS configurations intended for other tenants. diff --git a/docs/content/security/request-path.md b/docs/content/security/request-path.md new file mode 100644 index 000000000..fe88c3142 --- /dev/null +++ b/docs/content/security/request-path.md @@ -0,0 +1,135 @@ +--- +title: "Request Path" +description: "Learn how Traefik processes and secures request paths through sanitization and encoded character filtering to protect against path traversal and injection attacks." +--- + +# Request Path + +Protecting Against Path-Based Attacks Through Sanitization and Filtering +{: .subtitle } + +Traefik implements multiple layers of security when processing incoming request paths. +This includes path sanitization to normalize potentially dangerous sequences and encoded character filtering to prevent attack vectors that use URL encoding. +Understanding how Traefik handles request paths is crucial for maintaining a secure routing infrastructure. + +## How Traefik Processes Request Paths + +When Traefik receives an HTTP request, it processes the request path through several security-focused stages: + +### 1. Encoded Character Filtering + +Traefik inspects the path for potentially dangerous encoded characters and rejects requests containing them unless explicitly allowed. + +Here is the list of the encoded characters that are allowed by default: + +| Encoded Character | Character | +|-------------------|-------------------------| +| `%2f` or `%2F` | `/` (slash) | +| `%5c` or `%5C` | `\` (backslash) | +| `%00` | `NULL` (null character) | +| `%3b` or `%3B` | `;` (semicolon) | +| `%25` | `%` (percent) | +| `%3f` or `%3F` | `?` (question mark) | +| `%23` | `#` (hash) | + +### 2. Path Normalization + +Traefik normalizes the request path by decoding the unreserved percent-encoded characters, +as they are equivalent to their non-encoded form (according to [rfc3986#section-2.3](https://datatracker.ietf.org/doc/html/rfc3986#section-2.3)), +and capitalizing the percent-encoded characters (according to [rfc3986#section-6.2.2.1](https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2.1)). + +### 3. Path Sanitization + +Traefik sanitizes request paths to prevent common attack vectors, +by removing the `..`, `.` and duplicate slash segments from the URL (according to [rfc3986#section-6.2.2.3](https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2.3)). + +## Path Security Configuration + +Traefik provides two main mechanisms for path security that work together to protect your applications. + +### Path Sanitization + +Path sanitization is enabled by default and helps prevent directory traversal attacks by normalizing request paths. +Configure it in the [EntryPoints](../routing/entrypoints.md#sanitizepath) HTTP section: + +```yaml tab="File (YAML)" +entryPoints: + websecure: + address: ":443" + http: + sanitizePath: true # Default: true (recommended) +``` + +```toml tab="File (TOML)" +[entryPoints.websecure] + address = ":443" + + [entryPoints.websecure.http] + sanitizePath = true +``` + +```bash tab="CLI" +--entryPoints.websecure.address=:443 +--entryPoints.websecure.http.sanitizePath=true +``` + +**Sanitization behavior:** + +- `./foo/bar` → `/foo/bar` (removes relative current directory) +- `/foo/../bar` → `/bar` (resolves parent directory traversal) +- `/foo/bar//` → `/foo/bar/` (removes duplicate slashes) +- `/./foo/../bar//` → `/bar/` (combines all normalizations) + +### Encoded Character Filtering + +Encoded character filtering provides an additional security layer by rejecting potentially dangerous URL-encoded characters. +Configure it in the [EntryPoints](../routing/entrypoints.md#encoded-characters) HTTP section. + +This filtering occurs before path sanitization and catches attack attempts that use encoding to bypass other security controls. + +All encoded character filtering is disabled by default (`true` means encoded characters are allowed). + +!!! info "Security Considerations" + + When your backend is not fully compliant with [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) and notably decode encoded reserved characters in the requets path, + it is recommended to set these options to `false` to avoid split-view situation and helps prevent path traversal attacks or other malicious attempts to bypass security controls. + +```yaml tab="File (YAML)" +entryPoints: + websecure: + address: ":443" + http: + encodedCharacters: + allowEncodedSlash: false # %2F - Default: true + allowEncodedBackSlash: false # %5C - Default: true + allowEncodedNullCharacter: false # %00 - Default: true + allowEncodedSemicolon: false # %3B - Default: true + allowEncodedPercent: false # %25 - Default: true + allowEncodedQuestionMark: false # %3F - Default: true + allowEncodedHash: false # %23 - Default: true +``` + +```toml tab="File (TOML)" +[entryPoints.websecure] + address = ":443" + + [entryPoints.websecure.http.encodedCharacters] + allowEncodedSlash = false + allowEncodedBackSlash = false + allowEncodedNullCharacter = false + allowEncodedSemicolon = false + allowEncodedPercent = false + allowEncodedQuestionMark = false + allowEncodedHash = false +``` + +```bash tab="CLI" +--entryPoints.websecure.address=:443 +--entryPoints.websecure.http.encodedCharacters.allowEncodedSlash=false +--entryPoints.websecure.http.encodedCharacters.allowEncodedBackSlash=false +--entryPoints.websecure.http.encodedCharacters.allowEncodedNullCharacter=false +--entryPoints.websecure.http.encodedCharacters.allowEncodedSemicolon=false +--entryPoints.websecure.http.encodedCharacters.allowEncodedPercent=false +--entryPoints.websecure.http.encodedCharacters.allowEncodedQuestionMark=false +--entryPoints.websecure.http.encodedCharacters.allowEncodedHash=false +``` diff --git a/docs/content/setup/docker.md b/docs/content/setup/docker.md index beaf29f9d..e270b318c 100644 --- a/docs/content/setup/docker.md +++ b/docs/content/setup/docker.md @@ -296,4 +296,4 @@ This enables access logs to the container's standard output (viewable via `docke You now have a basic Traefik setup in Docker with secure dashboard access and HTTP-to-HTTPS redirection. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/setup/kubernetes.md b/docs/content/setup/kubernetes.md index c54853e8c..4cdd6def0 100644 --- a/docs/content/setup/kubernetes.md +++ b/docs/content/setup/kubernetes.md @@ -126,7 +126,7 @@ ingressRoute: middlewares: - name: dashboard-auth -# Creates a BasiAuth Middleware and Secret for the Dashboard Security +# Creates a BasicAuth Middleware and Secret for the Dashboard Security extraObjects: - apiVersion: v1 kind: Secret @@ -397,4 +397,4 @@ This enables OTel tracing and specifies the collector endpoint. Consult the [Tra This setup establishes Traefik with secure dashboard access and HTTPS redirection, along with pointers to enable observability & TLS. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/setup/swarm.md b/docs/content/setup/swarm.md index 1b097831d..1d3237153 100644 --- a/docs/content/setup/swarm.md +++ b/docs/content/setup/swarm.md @@ -61,7 +61,7 @@ In the same directory, create `docker‑compose‑swarm.yaml`: ```yaml services: traefik: - image: traefik:v3.4 + image: traefik:v3.6 networks: # Connect to the 'traefik_proxy' overlay network for inter-container communication across nodes @@ -327,4 +327,4 @@ command: You now have Traefik running on Docker Swarm with HTTPS, a secured dashboard, automatic HTTP → HTTPS redirects, and foundational observability. Expand this stack with Let’s Encrypt, additional middlewares, or multiple Traefik replicas as your Swarm grows. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/user-guides/cert-manager.md b/docs/content/user-guides/cert-manager.md index fbb90d918..c6edd855f 100644 --- a/docs/content/user-guides/cert-manager.md +++ b/docs/content/user-guides/cert-manager.md @@ -180,4 +180,4 @@ There are multiple event sources available to investigate when using cert-manage cert-manager documentation provides a [detailed guide](https://cert-manager.io/docs/troubleshooting/) on how to troubleshoot a certificate request. -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/user-guides/crd-acme/03-deployments.yml b/docs/content/user-guides/crd-acme/03-deployments.yml index b2d15b3b7..7318b0daf 100644 --- a/docs/content/user-guides/crd-acme/03-deployments.yml +++ b/docs/content/user-guides/crd-acme/03-deployments.yml @@ -26,7 +26,7 @@ spec: serviceAccountName: traefik-ingress-controller containers: - name: traefik - image: traefik:v3.5 + image: traefik:v3.6 args: - --api.insecure - --accesslog diff --git a/docs/content/user-guides/crd-acme/index.md b/docs/content/user-guides/crd-acme/index.md index 49c9db013..f408b03c5 100644 --- a/docs/content/user-guides/crd-acme/index.md +++ b/docs/content/user-guides/crd-acme/index.md @@ -49,10 +49,10 @@ and the RBAC authorization resources which will be referenced through the `servi ```bash # Install Traefik Resource Definitions: -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml # Install RBAC for Traefik: -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/reference/dynamic-configuration/kubernetes-crd-rbac.yml ``` ### Services @@ -60,7 +60,7 @@ kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/con Then, the services. One for Traefik itself, and one for the app it routes for, i.e. in this case our demo HTTP server: [whoami](https://github.com/traefik/whoami). ```bash -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/user-guides/crd-acme/02-services.yml +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/user-guides/crd-acme/02-services.yml ``` ```yaml @@ -73,7 +73,7 @@ Next, the deployments, i.e. the actual pods behind the services. Again, one pod for Traefik, and one for the whoami app. ```bash -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/user-guides/crd-acme/03-deployments.yml +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/user-guides/crd-acme/03-deployments.yml ``` ```yaml @@ -100,7 +100,7 @@ Look it up. We can now finally apply the actual ingressRoutes, with: ```bash -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/user-guides/crd-acme/04-ingressroutes.yml +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/user-guides/crd-acme/04-ingressroutes.yml ``` ```yaml @@ -126,7 +126,7 @@ Nowadays, TLS v1.0 and v1.1 are deprecated. In order to force TLS v1.2 or later on all your IngressRoute, you can define the `default` TLSOption: ```bash -kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.5/docs/content/user-guides/crd-acme/05-tlsoption.yml +kubectl apply -f https://raw.githubusercontent.com/traefik/traefik/v3.6/docs/content/user-guides/crd-acme/05-tlsoption.yml ``` ```yaml diff --git a/docs/content/user-guides/crd-acme/k3s.yml b/docs/content/user-guides/crd-acme/k3s.yml index 23190c461..8701d7305 100644 --- a/docs/content/user-guides/crd-acme/k3s.yml +++ b/docs/content/user-guides/crd-acme/k3s.yml @@ -1,5 +1,5 @@ server: - image: rancher/k3s:v1.17.2-k3s1 + image: rancher/k3s:v1.34.2-k3s1 command: server --disable-agent --no-deploy traefik environment: - K3S_CLUSTER_SECRET=somethingtotallyrandom @@ -17,7 +17,7 @@ server: - 6443:6443 node: - image: rancher/k3s:v1.17.2-k3s1 + image: rancher/k3s:v1.34.2-k3s1 privileged: true links: - server @@ -26,5 +26,5 @@ node: - K3S_CLUSTER_SECRET=somethingtotallyrandom volumes: # this is where you would place a alternative traefik image (saved as a .tar file with - # 'docker save'), if you want to use it, instead of the traefik:v3.5 image. + # 'docker save'), if you want to use it, instead of the traefik:v3.6 image. - /somewhere/on/your/host/custom-image:/var/lib/rancher/k3s/agent/images diff --git a/docs/content/user-guides/docker-compose/acme-dns/docker-compose.yml b/docs/content/user-guides/docker-compose/acme-dns/docker-compose.yml index 2bcb322e6..d2e93d105 100644 --- a/docs/content/user-guides/docker-compose/acme-dns/docker-compose.yml +++ b/docs/content/user-guides/docker-compose/acme-dns/docker-compose.yml @@ -1,7 +1,7 @@ services: traefik: - image: "traefik:v3.5" + image: "traefik:v3.6" container_name: "traefik" command: #- "--log.level=DEBUG" diff --git a/docs/content/user-guides/docker-compose/acme-dns/docker-compose_secrets.yml b/docs/content/user-guides/docker-compose/acme-dns/docker-compose_secrets.yml index 63487e067..752f28755 100644 --- a/docs/content/user-guides/docker-compose/acme-dns/docker-compose_secrets.yml +++ b/docs/content/user-guides/docker-compose/acme-dns/docker-compose_secrets.yml @@ -11,7 +11,7 @@ secrets: services: traefik: - image: "traefik:v3.5" + image: "traefik:v3.6" container_name: "traefik" command: #- "--log.level=DEBUG" diff --git a/docs/content/user-guides/docker-compose/acme-dns/index.md b/docs/content/user-guides/docker-compose/acme-dns/index.md index 78f25f8d4..eb6178d82 100644 --- a/docs/content/user-guides/docker-compose/acme-dns/index.md +++ b/docs/content/user-guides/docker-compose/acme-dns/index.md @@ -187,4 +187,4 @@ environment: - "OVH_CONSUMER_KEY_FILE=/run/secrets/ovh_consumer_key" ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/user-guides/docker-compose/acme-http/docker-compose.yml b/docs/content/user-guides/docker-compose/acme-http/docker-compose.yml index 3deb01ca2..7684098d2 100644 --- a/docs/content/user-guides/docker-compose/acme-http/docker-compose.yml +++ b/docs/content/user-guides/docker-compose/acme-http/docker-compose.yml @@ -1,7 +1,7 @@ services: traefik: - image: "traefik:v3.5" + image: "traefik:v3.6" container_name: "traefik" command: #- "--log.level=DEBUG" diff --git a/docs/content/user-guides/docker-compose/acme-tls/docker-compose.yml b/docs/content/user-guides/docker-compose/acme-tls/docker-compose.yml index c95bb5eb9..b91269fe3 100644 --- a/docs/content/user-guides/docker-compose/acme-tls/docker-compose.yml +++ b/docs/content/user-guides/docker-compose/acme-tls/docker-compose.yml @@ -1,7 +1,7 @@ services: traefik: - image: "traefik:v3.5" + image: "traefik:v3.6" container_name: "traefik" command: #- "--log.level=DEBUG" diff --git a/docs/content/user-guides/docker-compose/acme-tls/index.md b/docs/content/user-guides/docker-compose/acme-tls/index.md index 942d17cc1..937664081 100644 --- a/docs/content/user-guides/docker-compose/acme-tls/index.md +++ b/docs/content/user-guides/docker-compose/acme-tls/index.md @@ -83,4 +83,4 @@ labels: - "traefik.http.routers.whoami.tls.certresolver=myresolver" ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/content/user-guides/docker-compose/basic-example/docker-compose.yml b/docs/content/user-guides/docker-compose/basic-example/docker-compose.yml index 842478ac8..fba201c4a 100644 --- a/docs/content/user-guides/docker-compose/basic-example/docker-compose.yml +++ b/docs/content/user-guides/docker-compose/basic-example/docker-compose.yml @@ -1,7 +1,7 @@ services: traefik: - image: "traefik:v3.5" + image: "traefik:v3.6" container_name: "traefik" command: #- "--log.level=DEBUG" diff --git a/docs/content/user-guides/docker-compose/basic-example/index.md b/docs/content/user-guides/docker-compose/basic-example/index.md index 6aef0c804..b29b84925 100644 --- a/docs/content/user-guides/docker-compose/basic-example/index.md +++ b/docs/content/user-guides/docker-compose/basic-example/index.md @@ -29,7 +29,7 @@ Create a `docker-compose.yml` file with the following content: services: traefik: - image: "traefik:v3.5" + image: "traefik:v3.6" ... networks: - traefiknet @@ -131,4 +131,4 @@ whoami: - "traefik.http.routers.whoami.entrypoints=web" ``` -{!traefik-for-business-applications.md!} +{% include-markdown "includes/traefik-for-business-applications.md" %} diff --git a/docs/docs.Dockerfile b/docs/docs.Dockerfile index 99e963cf4..bd9886863 100644 --- a/docs/docs.Dockerfile +++ b/docs/docs.Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.22 +FROM alpine:3.23 ENV PATH="${PATH}:/venv/bin" diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index dca15910d..77e5ce866 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -2,24 +2,27 @@ site_name: Traefik site_description: Traefik Documentation site_author: traefik.io site_url: https://doc.traefik.io/traefik -dev_addr: 0.0.0.0:8000 +dev_addr: localhost:8000 repo_name: 'GitHub' repo_url: 'https://github.com/traefik/traefik' docs_dir: 'content' -product: proxy -# https://squidfunk.github.io/mkdocs-material/ +# Use custom version of mkdocs-material +# See https://github.com/traefik/mkdocs-material theme: name: 'traefik-labs' + product: proxy language: en include_sidebar: true favicon: assets/img/traefikproxy-icon-color.png logo: assets/img/traefikproxy-vertical-logo-color.svg feature: tabs: false + features: + - content.code.copy palette: primary: 'cyan' accent: 'cyan' @@ -27,7 +30,7 @@ theme: prev: 'Previous' next: 'Next' -copyright: 'Traefik Labs • Copyright © 2016-2025' +copyright: 'Traefik Labs • Copyright © 2016-2026' extra_javascript: - assets/js/hljs/highlight.pack.js # Download from https://highlightjs.org/download/ and enable YAML, TOML and Dockerfile @@ -35,12 +38,15 @@ extra_javascript: extra_css: - assets/css/menu-icons.css + - assets/css/code-copy.css plugins: - search - exclude: glob: - "**/include-*.md" + - include-markdown: + encoding: utf-8 - redirects: redirect_maps: # Providers @@ -176,21 +182,19 @@ markdown_extensions: - pymdownx.tasklist - pymdownx.snippets: check_paths: true - - markdown_include.include: - base_path: content/includes/ - encoding: utf-8 - toc: permalink: true # Page tree nav: - 'What is Traefik': 'index.md' + - 'Features': 'features/index.md' - 'Getting Started': - 'Overview': 'getting-started/index.md' + - 'Configuration Introduction': 'getting-started/configuration-overview.md' - 'Quick Start': - 'Kubernetes': 'getting-started/kubernetes.md' - 'Docker': 'getting-started/docker.md' - - 'Configuration Introduction': 'getting-started/configuration-overview.md' - 'Setup': - 'Kubernetes': 'setup/kubernetes.md' - 'Docker': 'setup/docker.md' @@ -212,6 +216,7 @@ nav: - 'Extend': 'extend/extend-traefik.md' - 'Govern Traefik Hub API Gateway': 'govern/index.md' - 'Migrate': + - 'Ingress NGINX to Traefik': 'migrate/nginx-to-traefik.md' - 'Traefik v3 minor migrations': 'migrate/v3.md' - 'Traefik v2 to v3': - 'Migration guide': 'migrate/v2-to-v3.md' @@ -228,6 +233,7 @@ nav: - 'Kubernetes CRD' : 'reference/install-configuration/providers/kubernetes/kubernetes-crd.md' - 'Kubernetes Ingress' : 'reference/install-configuration/providers/kubernetes/kubernetes-ingress.md' - 'Kubernetes Ingress NGINX' : 'reference/install-configuration/providers/kubernetes/kubernetes-ingress-nginx.md' + - 'Knative': 'reference/install-configuration/providers/kubernetes/knative.md' - 'Docker': 'reference/install-configuration/providers/docker.md' - 'Swarm': 'reference/install-configuration/providers/swarm.md' - 'Hashicorp': @@ -266,6 +272,7 @@ nav: - 'Router' : 'reference/routing-configuration/http/routing/router.md' - 'Rules & Priority' : 'reference/routing-configuration/http/routing/rules-and-priority.md' - 'Observability': 'reference/routing-configuration/http/routing/observability.md' + - 'Multi-Layer Routing': 'reference/routing-configuration/http/routing/multi-layer-routing.md' - 'Load Balancing' : - 'Service' : 'reference/routing-configuration/http/load-balancing/service.md' - 'ServersTransport' : 'reference/routing-configuration/http/load-balancing/serverstransport.md' @@ -345,6 +352,7 @@ nav: - 'IngressRouteUDP' : 'reference/routing-configuration/kubernetes/crd/udp/ingressrouteudp.md' - 'Ingress' : 'reference/routing-configuration/kubernetes/ingress.md' - 'Ingress NGINX' : 'reference/routing-configuration/kubernetes/ingress-nginx.md' + - 'Knative': 'reference/routing-configuration/kubernetes/knative.md' - 'Label & Tag Providers' : - 'Docker' : 'reference/routing-configuration/other-providers/docker.md' - 'Swarm' : 'reference/routing-configuration/other-providers/swarm.md' @@ -354,13 +362,16 @@ nav: - 'KV' : 'reference/routing-configuration/other-providers/kv.md' - 'File' : 'reference/routing-configuration/other-providers/file.md' - 'Security': - - 'Content-Length': 'security/content-length.md' - - 'TLS in Multi-Tenant Kubernetes': 'security/tls-certs-in-multi-tenant-kubernetes.md' + - 'Request Path': 'security/request-path.md' + - 'Content-Length': 'security/content-length.md' + - 'Multi-Tenant Kubernetes': 'security/multi-tenant-kubernetes.md' - 'Deprecation Notices': - 'Releases': 'deprecation/releases.md' - 'Features': 'deprecation/features.md' - 'User Guides': - 'FastProxy': 'user-guides/fastproxy.md' + - 'Kubernetes and Let''s Encrypt': 'user-guides/crd-acme/index.md' + - 'Kubernetes and cert-manager': 'user-guides/cert-manager.md' - 'gRPC Examples': 'user-guides/grpc.md' - 'WebSocket Examples': 'user-guides/websocket.md' - 'Contributing': diff --git a/docs/readme.md b/docs/readme.md index de1d8b677..822c1cd6a 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -16,3 +16,15 @@ [pymdown-extensions]: https://facelessuser.github.io/pymdown-extensions "PyMdown Extensions" [pymdown-extensions-src]: https://github.com/facelessuser/pymdown-extensions "PyMdown Extensions - Sources" + +## Build locally without docker + +```sh +# Pre-requisite: python3, pip and virtualenv +DOCS="/tmp/traefik-docs" +mkdir "$DOCS" +virtualenv "$DOCS" +source "$DOCS/bin/activate" +pip install -r requirements.txt +mkdocs serve # or mkdocs build +``` diff --git a/docs/requirements.txt b/docs/requirements.txt index d2dff6242..f0d99127c 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,5 @@ -markdown-include==0.5.1 -mkdocs==1.2.4 +mkdocs==1.4.3 +mkdocs-include-markdown-plugin==7.2.0 mkdocs-exclude==1.0.2 mkdocs-traefiklabs>=100.0.7 mkdocs-redirects==1.2.2 diff --git a/docs/scripts/lint-yaml.sh b/docs/scripts/lint-yaml.sh new file mode 100755 index 000000000..d48e8b1a9 --- /dev/null +++ b/docs/scripts/lint-yaml.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# This script checks that YAML files with multiple Kubernetes resources +# do not start with '---' +# +# Rule: If a YAML file contains more than one Kubernetes resource +# (indicated by '---' separator in the middle of the file), +# it should NOT start with '---' + +set -eu + +BASE_DIR="${1:-/app}" + +echo "== Linting YAML files (Kubernetes multi-resource format)" + +# Find all YAML files in the content directory +find "${BASE_DIR}/content" -type f \( -name "*.yml" -o -name "*.yaml" \) | while read -r file; do + # Count the number of '---' lines in the file + separator_count=$(grep -c "^---" "$file" || true) + + # Check if file starts with '---' + starts_with_separator=false + if head -1 "$file" | grep -q "^---"; then + starts_with_separator=true + fi + + # If file has multiple resources (separator_count >= 1 when starting with ---, or >= 2 otherwise) + # and starts with '---', it's an error + # + # Logic: + # - If starts with '---' and has more than 1 separator -> multiple resources, error + # - If doesn't start with '---' and has 1+ separators -> multiple resources, ok + if [ "$starts_with_separator" = true ] && [ "$separator_count" -gt 1 ]; then + echo "ERROR: $file starts with '---' but contains multiple Kubernetes resources" + echo " Files with multiple resources should not start with '---'" + # We need to signal error but can't use EXIT_CODE in subshell + # So we output to a temp file + echo "1" > /tmp/yaml_lint_error + fi +done + +# Check if any errors were found +if [ -f /tmp/yaml_lint_error ]; then + rm -f /tmp/yaml_lint_error + exit 1 +fi + +echo "YAML lint passed" +exit 0 diff --git a/docs/scripts/lint.sh b/docs/scripts/lint.sh index a46066df8..89035b002 100755 --- a/docs/scripts/lint.sh +++ b/docs/scripts/lint.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # This script will run a couple of linter on the documentation set -eu @@ -6,14 +6,17 @@ set -eu # We want to run all linters before returning success (exit 0) or failure (exit 1) # So this variable holds the global exit code EXIT_CODE=0 -readonly BASE_DIR=/app +readonly BASE_DIR="${1:-/app}" + +# Run YAML linter for Kubernetes multi-resource files +./docs/scripts/lint-yaml.sh "${BASE_DIR}" || EXIT_CODE=1 echo "== Linting Markdown" # Uses the file ".markdownlint.json" for setup cd "${BASE_DIR}" || exit 1 -LINTER_EXCLUSIONS="$(find "${BASE_DIR}/content" -type f -name '.markdownlint.json')" -GLOBAL_LINT_OPTIONS="--config ${BASE_DIR}/.markdownlint.json" +LINTER_EXCLUSIONS="$(find "content" -type f -name '.markdownlint.json')" +GLOBAL_LINT_OPTIONS="--config .markdownlint.json" # Lint the specific folders (containing linter specific rulesets) for LINTER_EXCLUSION in ${LINTER_EXCLUSIONS} @@ -24,6 +27,6 @@ do done # Lint all the content, excluding the previously done` -eval markdownlint "${GLOBAL_LINT_OPTIONS}" "${BASE_DIR}/content/**/*.md" || EXIT_CODE=1 +eval markdownlint "${GLOBAL_LINT_OPTIONS}" "content/**/*.md" || EXIT_CODE=1 exit "${EXIT_CODE}" diff --git a/go.mod b/go.mod index 3d00ed55b..d3ceb8d09 100644 --- a/go.mod +++ b/go.mod @@ -3,18 +3,18 @@ module github.com/traefik/traefik/v3 go 1.24.0 require ( - github.com/BurntSushi/toml v1.5.0 + github.com/BurntSushi/toml v1.6.0 github.com/Masterminds/sprig/v3 v3.2.3 github.com/abbot/go-http-auth v0.0.0-00010101000000-000000000000 // No tag on the repo. github.com/andybalholm/brotli v1.1.1 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 - github.com/aws/aws-sdk-go-v2 v1.39.2 - github.com/aws/aws-sdk-go-v2/config v1.31.12 - github.com/aws/aws-sdk-go-v2/credentials v1.18.16 + github.com/aws/aws-sdk-go-v2 v1.41.0 + github.com/aws/aws-sdk-go-v2/config v1.32.6 + github.com/aws/aws-sdk-go-v2/credentials v1.19.6 github.com/aws/aws-sdk-go-v2/service/ec2 v1.203.1 github.com/aws/aws-sdk-go-v2/service/ecs v1.53.15 github.com/aws/aws-sdk-go-v2/service/ssm v1.56.13 - github.com/aws/smithy-go v1.23.0 + github.com/aws/smithy-go v1.24.0 github.com/cenkalti/backoff/v4 v4.3.0 github.com/containous/alice v0.0.0-20181107144136-d83ebdd94cbd // No tag on the repo. github.com/coreos/go-systemd/v22 v22.5.0 @@ -23,18 +23,18 @@ require ( github.com/docker/go-connections v0.5.0 github.com/fatih/structs v1.1.0 github.com/fsnotify/fsnotify v1.9.0 - github.com/go-acme/lego/v4 v4.27.0 + github.com/go-acme/lego/v4 v4.31.0 github.com/go-kit/kit v0.13.0 github.com/go-kit/log v0.2.1 github.com/golang/protobuf v1.5.4 github.com/google/go-github/v28 v28.1.1 github.com/gorilla/mux v1.8.1 - github.com/gorilla/websocket v1.5.3 + github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 github.com/hashicorp/consul/api v1.26.1 github.com/hashicorp/go-hclog v1.6.3 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-retryablehttp v0.7.8 - github.com/hashicorp/go-version v1.7.0 + github.com/hashicorp/go-version v1.8.0 github.com/hashicorp/nomad/api v0.0.0-20231213195942-64e3dca9274b // No tag on the repo. github.com/http-wasm/http-wasm-host-go v0.7.0 github.com/influxdata/influxdb-client-go/v2 v2.7.0 @@ -46,24 +46,24 @@ require ( github.com/kvtools/valkeyrie v1.0.0 github.com/kvtools/zookeeper v1.0.2 github.com/mailgun/ttlmap v0.0.0-20170619185759-c1c17f74874f // No tag on the repo. - github.com/miekg/dns v1.1.68 + github.com/miekg/dns v1.1.69 github.com/mitchellh/copystructure v1.2.0 github.com/mitchellh/hashstructure v1.0.0 github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // No tag on the repo. github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pires/go-proxyproto v0.8.1 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // No tag on the repo. - github.com/prometheus/client_golang v1.22.0 - github.com/prometheus/client_model v0.6.1 - github.com/quic-go/quic-go v0.55.0 + github.com/prometheus/client_golang v1.23.0 + github.com/prometheus/client_model v0.6.2 + github.com/quic-go/quic-go v0.58.0 github.com/redis/go-redis/v9 v9.8.0 github.com/rs/zerolog v1.33.0 github.com/sirupsen/logrus v1.9.3 - github.com/spiffe/go-spiffe/v2 v2.5.0 + github.com/spiffe/go-spiffe/v2 v2.6.0 github.com/stealthrocket/wasi-go v0.8.0 github.com/stealthrocket/wazergo v0.19.1 github.com/stretchr/testify v1.11.1 - github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154 // No tag on the repo. + github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807 // No tag on the repo. github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046 // No tag on the repo. github.com/testcontainers/testcontainers-go v0.32.0 github.com/testcontainers/testcontainers-go/modules/k3s v0.32.0 @@ -95,37 +95,40 @@ require ( go.opentelemetry.io/otel/sdk/log v0.14.0 go.opentelemetry.io/otel/sdk/metric v1.38.0 go.opentelemetry.io/otel/trace v1.38.0 - golang.org/x/crypto v0.43.0 - golang.org/x/mod v0.28.0 - golang.org/x/net v0.46.0 - golang.org/x/sync v0.17.0 - golang.org/x/sys v0.37.0 - golang.org/x/text v0.30.0 + golang.org/x/crypto v0.46.0 + golang.org/x/mod v0.31.0 + golang.org/x/net v0.48.0 + golang.org/x/sync v0.19.0 + golang.org/x/sys v0.39.0 + golang.org/x/text v0.32.0 golang.org/x/time v0.14.0 - golang.org/x/tools v0.37.0 - google.golang.org/grpc v1.75.1 + golang.org/x/tools v0.40.0 + google.golang.org/grpc v1.78.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.32.3 - k8s.io/apiextensions-apiserver v0.32.3 - k8s.io/apimachinery v0.32.3 - k8s.io/client-go v0.32.3 - k8s.io/utils v0.0.0-20241210054802-24370beab758 // No tag on the repo. + k8s.io/api v0.34.3 + k8s.io/apiextensions-apiserver v0.34.3 + k8s.io/apimachinery v0.34.3 + k8s.io/client-go v0.34.3 + k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d // No tag on the repo. + knative.dev/networking v0.0.0-20241022012959-60e29ff520dc + knative.dev/pkg v0.0.0-20241021183759-9b9d535af5ad mvdan.cc/xurls/v2 v2.5.0 - sigs.k8s.io/controller-runtime v0.20.4 - sigs.k8s.io/gateway-api v1.3.0 - sigs.k8s.io/yaml v1.4.0 + sigs.k8s.io/controller-runtime v0.22.1 + sigs.k8s.io/gateway-api v1.4.0 + sigs.k8s.io/structured-merge-diff/v6 v6.3.1 + sigs.k8s.io/yaml v1.6.0 ) require ( - cloud.google.com/go/auth v0.17.0 // indirect + cloud.google.com/go/auth v0.18.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect cloud.google.com/go/compute/metadata v0.9.0 // indirect dario.cat/mergo v1.0.1 // indirect github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 // indirect github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/dns/armdns v1.2.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/privatedns/armprivatedns v1.3.0 // indirect @@ -140,37 +143,38 @@ require ( github.com/Azure/go-autorest/autorest/to v0.4.1 // indirect github.com/Azure/go-autorest/logger v0.2.1 // indirect github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.3.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.13.0 // indirect - github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 // indirect github.com/VividCortex/gohistogram v1.0.0 // indirect github.com/akamai/AkamaiOPEN-edgegrid-golang/v11 v11.1.0 // indirect github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13 // indirect github.com/alibabacloud-go/debug v1.0.1 // indirect - github.com/alibabacloud-go/tea v1.3.13 // indirect + github.com/alibabacloud-go/tea v1.4.0 // indirect github.com/alibabacloud-go/tea-utils/v2 v2.0.7 // indirect github.com/aliyun/credentials-go v1.4.7 // indirect github.com/armon/go-metrics v0.4.1 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 // indirect - github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.0 // indirect - github.com/aws/aws-sdk-go-v2/service/route53 v1.58.4 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.29.6 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 // indirect - github.com/aziontech/azionapi-go-sdk v0.143.0 // indirect - github.com/baidubce/bce-sdk-go v0.9.248 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 // indirect + github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.10 // indirect + github.com/aws/aws-sdk-go-v2/service/route53 v1.62.0 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.8 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 // indirect + github.com/aziontech/azionapi-go-sdk v0.144.0 // indirect + github.com/baidubce/bce-sdk-go v0.9.256 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect + github.com/blendle/zapdriver v1.3.1 // indirect github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect github.com/bytedance/sonic v1.10.0 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect @@ -191,18 +195,20 @@ require ( github.com/distribution/reference v0.6.0 // indirect github.com/dnsimple/dnsimple-go/v4 v4.0.0 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/emicklei/go-restful/v3 v3.12.0 // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect - github.com/exoscale/egoscale/v3 v3.1.27 // indirect + github.com/exoscale/egoscale/v3 v3.1.33 // indirect github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect github.com/ghodss/yaml v1.0.0 // indirect github.com/gin-gonic/gin v1.9.1 // indirect - github.com/go-acme/alidns-20150109/v4 v4.6.1 // indirect - github.com/go-acme/tencentclouddnspod v1.1.10 // indirect - github.com/go-acme/tencentedgdeone v1.1.19 // indirect + github.com/go-acme/alidns-20150109/v4 v4.7.0 // indirect + github.com/go-acme/esa-20240910/v2 v2.44.0 // indirect + github.com/go-acme/jdcloud-sdk-go v1.64.0 // indirect + github.com/go-acme/tencentclouddnspod v1.1.25 // indirect + github.com/go-acme/tencentedgdeone v1.1.48 // indirect github.com/go-errors/errors v1.0.1 // indirect github.com/go-jose/go-jose/v4 v4.1.3 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect @@ -210,29 +216,30 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-ole/go-ole v1.2.6 // indirect - github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonpointer v0.21.2 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect - github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/swag v0.23.1 // indirect github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.23.0 // indirect - github.com/go-resty/resty/v2 v2.16.5 // indirect + github.com/go-resty/resty/v2 v2.17.1 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect github.com/go-zookeeper/zk v1.0.3 // indirect - github.com/goccy/go-yaml v1.11.3 // indirect - github.com/gofrs/flock v0.12.1 // indirect + github.com/goccy/go-yaml v1.18.0 // indirect + github.com/gofrs/flock v0.13.0 // indirect + github.com/gofrs/uuid v4.4.0+incompatible // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect - github.com/google/gnostic-models v0.6.8 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/go-querystring v1.1.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect + github.com/google/go-querystring v1.2.0 // indirect github.com/google/s2a-go v0.1.9 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect - github.com/googleapis/gax-go/v2 v2.15.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect + github.com/googleapis/gax-go/v2 v2.16.0 // indirect github.com/gophercloud/gophercloud v1.14.1 // indirect github.com/gophercloud/utils v0.0.0-20231010081019-80377eca5d56 // indirect github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf // indirect @@ -247,12 +254,12 @@ require ( github.com/hashicorp/hcl v1.0.1-vault-5 // indirect github.com/hashicorp/serf v0.10.1 // indirect github.com/huandu/xstrings v1.5.0 // indirect - github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.172 // indirect + github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.182 // indirect github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect github.com/imdario/mergo v0.3.16 // indirect github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 // indirect - github.com/jonboulle/clockwork v0.4.0 // indirect + github.com/jonboulle/clockwork v0.5.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect @@ -262,7 +269,7 @@ require ( github.com/labbsr0x/bindman-dns-webhook v1.0.2 // indirect github.com/labbsr0x/goh v1.0.1 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/linode/linodego v1.60.0 // indirect + github.com/linode/linodego v1.64.0 // indirect github.com/liquidweb/liquidweb-cli v0.6.9 // indirect github.com/liquidweb/liquidweb-go v1.6.4 // indirect github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect @@ -270,7 +277,7 @@ require ( github.com/mailgun/minheap v0.0.0-20170619185613-3dbe6c6bf55f // indirect github.com/mailgun/multibuf v0.1.2 // indirect github.com/mailgun/timetools v0.0.0-20141028012446-7e6055773c51 // indirect - github.com/mailru/easyjson v0.7.7 // indirect + github.com/mailru/easyjson v0.9.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mimuret/golang-iij-dpf v0.9.1 // indirect @@ -293,22 +300,24 @@ require ( github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/namedotcom/go/v4 v4.0.2 // indirect github.com/nrdcg/auroradns v1.1.0 // indirect - github.com/nrdcg/bunny-go v0.0.0-20250327222614-988a091fc7ea // indirect - github.com/nrdcg/desec v0.11.0 // indirect + github.com/nrdcg/bunny-go v0.1.0 // indirect + github.com/nrdcg/desec v0.11.1 // indirect github.com/nrdcg/dnspod-go v0.4.0 // indirect github.com/nrdcg/freemyip v0.3.0 // indirect github.com/nrdcg/goacmedns v0.2.0 // indirect - github.com/nrdcg/goinwx v0.11.0 // indirect - github.com/nrdcg/mailinabox v0.2.0 // indirect + github.com/nrdcg/goinwx v0.12.0 // indirect + github.com/nrdcg/mailinabox v0.3.0 // indirect github.com/nrdcg/namesilo v0.5.0 // indirect github.com/nrdcg/nodion v0.1.0 // indirect - github.com/nrdcg/oci-go-sdk/common/v1065 v1065.102.0 // indirect - github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.102.0 // indirect + github.com/nrdcg/oci-go-sdk/common/v1065 v1065.105.2 // indirect + github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.105.2 // indirect github.com/nrdcg/porkbun v0.4.0 // indirect + github.com/nrdcg/vegadns v0.3.0 // indirect github.com/nzdjb/go-metaname v1.0.0 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect + github.com/openzipkin/zipkin-go v0.4.3 // indirect github.com/ovh/go-ovh v1.9.0 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/peterhellberg/link v1.2.0 // indirect @@ -316,24 +325,24 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect github.com/pquerna/otp v1.5.0 // indirect - github.com/prometheus/common v0.62.0 // indirect - github.com/prometheus/procfs v0.16.1 // indirect - github.com/quic-go/qpack v0.5.1 // indirect + github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/procfs v0.17.0 // indirect + github.com/quic-go/qpack v0.6.0 // indirect github.com/regfish/regfish-dnsapi-go v0.1.1 // indirect github.com/rs/cors v1.7.0 // indirect + github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529 // indirect github.com/sacloud/api-client-go v0.3.3 // indirect github.com/sacloud/go-http v0.1.9 // indirect - github.com/sacloud/iaas-api-go v1.19.0 // indirect - github.com/sacloud/packages-go v0.0.11 // indirect + github.com/sacloud/iaas-api-go v1.23.1 // indirect + github.com/sacloud/packages-go v0.0.12 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/scaleway/scaleway-sdk-go v1.0.0-beta.35 // indirect + github.com/scaleway/scaleway-sdk-go v1.0.0-beta.36 // indirect github.com/selectel/domains-go v1.1.0 // indirect github.com/selectel/go-selvpcclient/v4 v4.1.0 // indirect github.com/shirou/gopsutil/v3 v3.24.4 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shopspring/decimal v1.4.0 // indirect - github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect github.com/softlayer/softlayer-go v1.2.1 // indirect github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e // indirect github.com/sony/gobreaker v1.0.0 // indirect @@ -344,7 +353,7 @@ require ( github.com/spf13/viper v1.18.2 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.41 // indirect + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.28 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect @@ -353,21 +362,21 @@ require ( github.com/transip/gotransip/v6 v6.26.1 // indirect github.com/ultradns/ultradns-go-sdk v1.8.1-20250722213956-faef419 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/vinyldns/go-vinyldns v0.9.16 // indirect - github.com/volcengine/volc-sdk-golang v1.0.223 // indirect - github.com/vultr/govultr/v3 v3.24.0 // indirect + github.com/vinyldns/go-vinyldns v0.9.17 // indirect + github.com/volcengine/volc-sdk-golang v1.0.233 // indirect + github.com/vultr/govultr/v3 v3.26.1 // indirect github.com/x448/float16 v0.8.4 // indirect - github.com/yandex-cloud/go-genproto v0.31.0 // indirect - github.com/yandex-cloud/go-sdk/services/dns v0.0.12 // indirect - github.com/yandex-cloud/go-sdk/v2 v2.19.0 // indirect + github.com/yandex-cloud/go-genproto v0.43.0 // indirect + github.com/yandex-cloud/go-sdk/services/dns v0.0.25 // indirect + github.com/yandex-cloud/go-sdk/v2 v2.37.0 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - github.com/zeebo/errs v1.4.0 // indirect go.etcd.io/etcd/api/v3 v3.6.4 // indirect go.etcd.io/etcd/client/pkg/v3 v3.6.4 // indirect go.etcd.io/etcd/client/v3 v3.6.4 // indirect go.mongodb.org/mongo-driver v1.13.1 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/collector/featuregate v1.41.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect go.opentelemetry.io/contrib/propagators/aws v1.38.0 // indirect @@ -379,25 +388,27 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.3.1 // indirect go.uber.org/zap v1.27.0 // indirect + go.yaml.in/yaml/v2 v2.4.2 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/arch v0.4.0 // indirect golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect - golang.org/x/oauth2 v0.32.0 // indirect - golang.org/x/term v0.36.0 // indirect - golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect - google.golang.org/api v0.252.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 // indirect - google.golang.org/protobuf v1.36.10 // indirect - gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + golang.org/x/oauth2 v0.34.0 // indirect + golang.org/x/term v0.38.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/api v0.259.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect + google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/ns1/ns1-go.v2 v2.15.0 // indirect + gopkg.in/ns1/ns1-go.v2 v2.16.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect + k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3 // indirect nhooyr.io/websocket v1.8.7 // indirect - sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect ) // Containous forks diff --git a/go.sum b/go.sum index 01f7b442d..a81ba3393 100644 --- a/go.sum +++ b/go.sum @@ -13,8 +13,8 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -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 v0.18.0 h1:wnqy5hrv7p3k7cShwAU/Br3nzod7fxoqG+k0VZ+/Pk0= +cloud.google.com/go/auth v0.18.0/go.mod h1:wwkPM1AgE1f2u6dG443MiWoD8C3BtOywNsUMcUTVDRo= 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/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= @@ -37,6 +37,10 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d h1:LblfooH1lKOpp1hIhukktmSAxFkqMPFk9KR6iZ0MJNI= +contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d/go.mod h1:IshRmMJBhDfFj5Y67nVhMYTTIze91RUeT73ipWKs/GY= +contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg= +contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -46,10 +50,10 @@ github.com/AdamSLevy/jsonrpc2/v14 v14.1.0 h1:Dy3M9aegiI7d7PF1LUdjbVigJReo+QOceYs github.com/AdamSLevy/jsonrpc2/v14 v14.1.0/go.mod h1:ZakZtbCXxCz82NJvq7MoREtiQesnDfrtF6RFUGzQfLo= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 h1:5YTBM8QDVIBN3sxBil89WfdAAqDZbyJTgh688DSxX5w= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0 h1:KpMC6LFL7mqpExyMC9jVOYRiVhLmamjeZfRsUpB7l4s= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.0/go.mod h1:J7MUC/wtRpfGVbQ5sIItY5/FuVWmvzlY21WAOfQnq/I= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= @@ -91,11 +95,11 @@ github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUM github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= -github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 h1:XkkQbfMyuH2jTSjQjSoihryI8GINRcs4xp8lNawg0FI= -github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= +github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= +github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= -github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= +github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/HdrHistogram/hdrhistogram-go v1.1.0/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= @@ -115,8 +119,6 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA github.com/Microsoft/hcsshim v0.13.0 h1:/BcXOiS6Qi7N9XqUcv27vkIuVOkBEcWstd2pMlWSeaA= github.com/Microsoft/hcsshim v0.13.0/go.mod h1:9KWJ/8DgU+QzYGupX4tzMhRQE8h6w90lH6HAaclpEok= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 h1:xPMsUicZ3iosVPSIP7bW5EcGUzjiiMl1OYTe14y/R24= -github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/sarama v1.30.1/go.mod h1:hGgx05L/DiW8XYBXeJdKIN6V2QUy2H6JqME5VT1NLRw= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= @@ -143,7 +145,6 @@ github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F4PKuMgEUETNZasrDM6vqVr/Can7H8= github.com/alibabacloud-go/darabonba-map v0.0.2 h1:qvPnGB4+dJbJIxOOfawxzF3hzMnIpjmafa0qOTp6udc= github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc= -github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.12/go.mod h1:f2wDpbM7hK9SvLIH09zSKVU1TsyemUNOqErMscMMl7c= github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13 h1:Q00FU3H94Ts0ZIHDmY+fYGgB7dV9D/YX6FGsgorQPgw= github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.13/go.mod h1:lxFGfobinVsQ49ntjpgWghXmIF0/Sm4+wvBJ1h5RtaE= github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg= @@ -166,9 +167,9 @@ github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/Ke github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= github.com/alibabacloud-go/tea v1.1.20/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk= -github.com/alibabacloud-go/tea v1.3.12/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg= -github.com/alibabacloud-go/tea v1.3.13 h1:WhGy6LIXaMbBM6VBYcsDCz6K/TPsT1Ri2hPmmZffZ94= github.com/alibabacloud-go/tea v1.3.13/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg= +github.com/alibabacloud-go/tea v1.4.0 h1:MSKhu/kWLPX7mplWMngki8nNt+CyUZ+kfkzaR5VpMhA= +github.com/alibabacloud-go/tea v1.4.0/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg= github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4= github.com/alibabacloud-go/tea-utils/v2 v2.0.7 h1:WDx5qW3Xa5ZgJ1c8NfqJkF6w+AU5wB8835UdhPr6Ax0= @@ -197,48 +198,50 @@ github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:W github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= github.com/aws/aws-sdk-go v1.40.45/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= github.com/aws/aws-sdk-go-v2 v1.9.1/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= -github.com/aws/aws-sdk-go-v2 v1.39.2 h1:EJLg8IdbzgeD7xgvZ+I8M1e0fL0ptn/M47lianzth0I= -github.com/aws/aws-sdk-go-v2 v1.39.2/go.mod h1:sDioUELIUO9Znk23YVmIk86/9DOpkbyyVb1i/gUNFXY= -github.com/aws/aws-sdk-go-v2/config v1.31.12 h1:pYM1Qgy0dKZLHX2cXslNacbcEFMkDMl+Bcj5ROuS6p8= -github.com/aws/aws-sdk-go-v2/config v1.31.12/go.mod h1:/MM0dyD7KSDPR+39p9ZNVKaHDLb9qnfDurvVS2KAhN8= -github.com/aws/aws-sdk-go-v2/credentials v1.18.16 h1:4JHirI4zp958zC026Sm+V4pSDwW4pwLefKrc0bF2lwI= -github.com/aws/aws-sdk-go-v2/credentials v1.18.16/go.mod h1:qQMtGx9OSw7ty1yLclzLxXCRbrkjWAM7JnObZjmCB7I= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9 h1:Mv4Bc0mWmv6oDuSWTKnk+wgeqPL5DRFu5bQL9BGPQ8Y= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.9/go.mod h1:IKlKfRppK2a1y0gy1yH6zD+yX5uplJ6UuPlgd48dJiQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9 h1:se2vOWGD3dWQUtfn4wEjRQJb1HK1XsNIt825gskZ970= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.9/go.mod h1:hijCGH2VfbZQxqCDN7bwz/4dzxV+hkyhjawAtdPWKZA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9 h1:6RBnKZLkJM4hQ+kN6E7yWFveOTg8NLPHAkqrs4ZPlTU= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.9/go.mod h1:V9rQKRmK7AWuEsOMnHzKj8WyrIir1yUJbZxDuZLFvXI= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= +github.com/aws/aws-sdk-go-v2 v1.41.0 h1:tNvqh1s+v0vFYdA1xq0aOJH+Y5cRyZ5upu6roPgPKd4= +github.com/aws/aws-sdk-go-v2 v1.41.0/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= +github.com/aws/aws-sdk-go-v2/config v1.32.6 h1:hFLBGUKjmLAekvi1evLi5hVvFQtSo3GYwi+Bx4lpJf8= +github.com/aws/aws-sdk-go-v2/config v1.32.6/go.mod h1:lcUL/gcd8WyjCrMnxez5OXkO3/rwcNmvfno62tnXNcI= +github.com/aws/aws-sdk-go-v2/credentials v1.19.6 h1:F9vWao2TwjV2MyiyVS+duza0NIRtAslgLUM0vTA1ZaE= +github.com/aws/aws-sdk-go-v2/credentials v1.19.6/go.mod h1:SgHzKjEVsdQr6Opor0ihgWtkWdfRAIwxYzSJ8O85VHY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 h1:80+uETIWS1BqjnN9uJ0dBUaETh+P1XwFy5vwHwK5r9k= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16/go.mod h1:wOOsYuxYuB/7FlnVtzeBYRcjSRtQpAW0hCP7tIULMwo= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 h1:rgGwPzb82iBYSvHMHXc8h9mRoOUBZIGFgKb9qniaZZc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16/go.mod h1:L/UxsGeKpGoIj6DxfhOWHWQ/kGKcd4I1VncE4++IyKA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 h1:1jtGzuV7c82xnqOVfx2F0xmJcOw5374L7N6juGW6x6U= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16/go.mod h1:M2E5OQf+XLe+SZGmmpaI2yy+J326aFf6/+54PoxSANc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.8.1/go.mod h1:CM+19rL1+4dFWnOQKwDc7H1KwXTz+h61oUSHyhV0b3o= github.com/aws/aws-sdk-go-v2/service/ec2 v1.203.1 h1:ZgY9zeVAe+54Qa7o1GXKRNTez79lffCeJSSinhl+qec= github.com/aws/aws-sdk-go-v2/service/ec2 v1.203.1/go.mod h1:0naMk66LtdeTmE+1CWQTKwtzOQ2t8mavOhMhR0Pv1m0= github.com/aws/aws-sdk-go-v2/service/ecs v1.53.15 h1:uH0DMwDjLGgjjYMk3M1MXHggk37trTiJIvwyJNP17Ig= github.com/aws/aws-sdk-go-v2/service/ecs v1.53.15/go.mod h1:49tE5yYdlAHqZIO8u5+u9Xy9k8IaV0v5cstZrjnX5+c= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1 h1:oegbebPEMA/1Jny7kvwejowCaHz1FWZAQ94WXFNCyTM= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.1/go.mod h1:kemo5Myr9ac0U9JfSjMo9yHLtw+pECEHsFtJ9tqCEI8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9 h1:5r34CgVOD4WZudeEKZ9/iKpiT6cM1JyEROpXjOcdWv8= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.9/go.mod h1:dB12CEbNWPbzO2uC6QSWHteqOg4JfBVJOojbAoAUb5I= -github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.0 h1:JOLRYFWMMKUABCp94HHfo0JBVQDVTLXOvWWphjpBBiQ= -github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.0/go.mod h1:WEOSRNyfIfvgrD9MuSIGrogKyuFahaVMziVq1pHI0NQ= -github.com/aws/aws-sdk-go-v2/service/route53 v1.58.4 h1:KycXrohD5OxAZ5h02YechO2gevvoHfAPAaJM5l8zqb0= -github.com/aws/aws-sdk-go-v2/service/route53 v1.58.4/go.mod h1:xNLZLn4SusktBQ5moqUOgiDKGz3a7vHwF4W0KD+WBPc= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 h1:oHjJHeUy0ImIV0bsrX0X91GkV5nJAyv1l1CC9lnO0TI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16/go.mod h1:iRSNGgOYmiYwSCXxXaKb9HfOEj40+oTKn8pTxMlYkRM= +github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.10 h1:MQuZZ6Tq1qQabPlkVxrCMdyVl70Ogl4AERZKo+y9Wzo= +github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.10/go.mod h1:U5C3JME1ibKESmpzBAqlRpTYZfVbTqrb5ICJm+sVVd8= +github.com/aws/aws-sdk-go-v2/service/route53 v1.62.0 h1:80pDB3Tpmb2RCSZORrK9/3iQxsd+w6vSzVqpT1FGiwE= +github.com/aws/aws-sdk-go-v2/service/route53 v1.62.0/go.mod h1:6EZUGGNLPLh5Unt30uEoA+KQcByERfXIkax9qrc80nA= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 h1:HpI7aMmJ+mm1wkSHIA2t5EaFFv5EFYXePW30p1EIrbQ= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.4/go.mod h1:C5RdGMYGlfM0gYq/tifqgn4EbyX99V15P2V3R+VHbQU= github.com/aws/aws-sdk-go-v2/service/ssm v1.56.13 h1:JfPeW7F6Y+VqBg6p+8zQv4wlgceguYu5ZT0USEGZ89g= github.com/aws/aws-sdk-go-v2/service/ssm v1.56.13/go.mod h1:EonGQFn66wZkJJrrKXrryrxoS3V30rcHvaWvc6oGHCI= -github.com/aws/aws-sdk-go-v2/service/sso v1.29.6 h1:A1oRkiSQOWstGh61y4Wc/yQ04sqrQZr1Si/oAXj20/s= -github.com/aws/aws-sdk-go-v2/service/sso v1.29.6/go.mod h1:5PfYspyCU5Vw1wNPsxi15LZovOnULudOQuVxphSflQA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1 h1:5fm5RTONng73/QA73LhCNR7UT9RpFH3hR6HWL6bIgVY= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.1/go.mod h1:xBEjWD13h+6nq+z4AkqSfSvqRKFgDIQeaMguAJndOWo= -github.com/aws/aws-sdk-go-v2/service/sts v1.38.6 h1:p3jIvqYwUZgu/XYeI48bJxOhvm47hZb5HUQ0tn6Q9kA= -github.com/aws/aws-sdk-go-v2/service/sts v1.38.6/go.mod h1:WtKK+ppze5yKPkZ0XwqIVWD4beCwv056ZbPQNoeHqM8= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.8 h1:aM/Q24rIlS3bRAhTyFurowU8A0SMyGDtEOY/l/s/1Uw= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.8/go.mod h1:+fWt2UHSb4kS7Pu8y+BMBvJF0EWx+4H0hzNwtDNRTrg= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 h1:AHDr0DaHIAo8c9t1emrzAlVDFp+iMMKnPdYy6XO4MCE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12/go.mod h1:GQ73XawFFiWxyWXMHWfhiomvP3tXtdNar/fi8z18sx0= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 h1:SciGFVNZ4mHdm7gpD1dgZYnCuVdX1s+lFTg4+4DOy70= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.5/go.mod h1:iW40X4QBmUxdP+fZNOpfmkdMZqsovezbAeO+Ubiv2pk= github.com/aws/smithy-go v1.8.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.23.0 h1:8n6I3gXzWJB2DxBDnfxgBaSX6oe0d/t10qGz7OKqMCE= -github.com/aws/smithy-go v1.23.0/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= -github.com/aziontech/azionapi-go-sdk v0.143.0 h1:4eEBlYT10prgeCVTNR9FIc7f59Crbl2zrH1a4D1BUqU= -github.com/aziontech/azionapi-go-sdk v0.143.0/go.mod h1:cA5DY/VP4X5Eu11LpQNzNn83ziKjja7QVMIl4J45feA= -github.com/baidubce/bce-sdk-go v0.9.248 h1:vB5OMuEC2xnO197M6OWUi24C8mYOZHKXT/8HuKQJUhU= -github.com/baidubce/bce-sdk-go v0.9.248/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= +github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= +github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= +github.com/aziontech/azionapi-go-sdk v0.144.0 h1:T+/w18o+FCiZsk3Z0ACBVVe7c/5EGLG15S3P8JfuPfo= +github.com/aziontech/azionapi-go-sdk v0.144.0/go.mod h1:OKxP/R0iVXnJJakYwMhh2BGAXnud8Ruy55Ak9ANuWoU= +github.com/baidubce/bce-sdk-go v0.9.256 h1:/6UwBzDp+dRFpKRIb5WsvxfSiG4SLOIOghvagOK/q4Y= +github.com/baidubce/bce-sdk-go v0.9.256/go.mod h1:zbYJMQwE4IZuyrJiFO8tO8NbtYiKTFTbwh4eIsqjVdg= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -248,6 +251,8 @@ 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +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/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI= github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= @@ -267,6 +272,8 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -363,8 +370,8 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o= github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= -github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk= -github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= +github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/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= @@ -372,10 +379,12 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/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.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/exoscale/egoscale/v3 v3.1.27 h1:vKdWZG8QFDc7rY7lCfcuudO+ovyp5psYjFwKVqmkhCE= -github.com/exoscale/egoscale/v3 v3.1.27/go.mod h1:0iY8OxgHJCS5TKqDNhwOW95JBKCnBZl3YGU4Yt+NqkU= +github.com/exoscale/egoscale/v3 v3.1.33 h1:5Lk/pwZ+K0sjNu9obS0VYPfhZQffRkvvO0BpdPoir4o= +github.com/exoscale/egoscale/v3 v3.1.33/go.mod h1:0iY8OxgHJCS5TKqDNhwOW95JBKCnBZl3YGU4Yt+NqkU= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM= @@ -399,8 +408,8 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4 github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +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/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= @@ -413,14 +422,18 @@ github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwv github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-acme/alidns-20150109/v4 v4.6.1 h1:Dch3aWRcw4U62+jKPjPQN3iW3TPvgIywATbvHzojXeo= -github.com/go-acme/alidns-20150109/v4 v4.6.1/go.mod h1:RBcqBA5IvUWtlpjx6dC6EkPVyBNLQ+mR18XoaP38BFY= -github.com/go-acme/lego/v4 v4.27.0 h1:cIhWd7Uj4BNFLEF3IpwuMkukVVRs5qjlp4KdUGa75yU= -github.com/go-acme/lego/v4 v4.27.0/go.mod h1:9FfNZHZmg6hf5CWOp4Lzo4gU8aBEvqZvrwdkBboa+4g= -github.com/go-acme/tencentclouddnspod v1.1.10 h1:ERVJ4mc3cT4Nb3+n6H/c1AwZnChGBqLoymE0NVYscKI= -github.com/go-acme/tencentclouddnspod v1.1.10/go.mod h1:Bo/0YQJ/99FM+44HmCQkByuptX1tJsJ9V14MGV/2Qco= -github.com/go-acme/tencentedgdeone v1.1.19 h1:1jdEpMITrDuXHnu7QLOy2hpLW0BlDof70/KuRT+EiTo= -github.com/go-acme/tencentedgdeone v1.1.19/go.mod h1:gpu7HvXfcKWBrq5HAEBowZNkdk7JFPkagRzC/infUY0= +github.com/go-acme/alidns-20150109/v4 v4.7.0 h1:PqJ/wR0JTpL4v0Owu1uM7bPQ1Yww0eQLAuuSdLjjQaQ= +github.com/go-acme/alidns-20150109/v4 v4.7.0/go.mod h1:btQvB6xZoN6ykKB74cPhiR+uvhrEE2AFVXm6RDmCHm0= +github.com/go-acme/esa-20240910/v2 v2.44.0 h1:ACi2uFb7ig4ousFs/YiFBR+aw3A4SHtOxvkMWB2Hbcs= +github.com/go-acme/esa-20240910/v2 v2.44.0/go.mod h1:ZYdN9EN9ikn26SNapxCVjZ65pHT/1qm4fzuJ7QGVX6g= +github.com/go-acme/jdcloud-sdk-go v1.64.0 h1:AW9j5khk8tRYbpBJPxKmqdwIqgLs2Fz3HUK3hn2YXjs= +github.com/go-acme/jdcloud-sdk-go v1.64.0/go.mod h1:qc/m8HNX1Zgd7GAv2DSEinup8fwy3Ted3/VVx7LB5bU= +github.com/go-acme/lego/v4 v4.31.0 h1:gd4oUYdfs83PR1/SflkNdit9xY1iul2I4EystnU8NXM= +github.com/go-acme/lego/v4 v4.31.0/go.mod h1:m6zcfX/zcbMYDa8s6AnCMnoORWNP8Epnei+6NBCTUGs= +github.com/go-acme/tencentclouddnspod v1.1.25 h1:7H3ZKshkaHzCXfRpAHVB5nvxeDDl2XLeNZfrNHiZj/s= +github.com/go-acme/tencentclouddnspod v1.1.25/go.mod h1:XXfzp0AYV7UAUsHKT6R0KAUJFhqAUXmWGF07Elpa5cE= +github.com/go-acme/tencentedgdeone v1.1.48 h1:WLyLBsRVhSLFmtbEFXk0naLODSQn7X6J0Fc/qR8xVUk= +github.com/go-acme/tencentedgdeone v1.1.48/go.mod h1:mu6tA+bPhlSd+CKUfzRikE0mfxmTlBI6dVTn9LY9dRI= github.com/go-chi/chi/v5 v5.0.0/go.mod h1:BBug9lr0cqtdAhsu6R4AAdvufI0/XBzAQSsUqJpoZOs= github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW4s= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= @@ -454,13 +467,13 @@ github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonpointer v0.21.2 h1:AqQaNADVwq/VnkCmQg6ogE+M3FOsKTytwges0JdwVuA= +github.com/go-openapi/jsonpointer v0.21.2/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= +github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es= github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= @@ -479,11 +492,11 @@ github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn github.com/go-playground/validator/v10 v10.9.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o= github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM= -github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= +github.com/go-resty/resty/v2 v2.17.1 h1:x3aMpHK1YM9e4va/TMDRlusDDoZiQ+ViDu/WpA6xTM4= +github.com/go-resty/resty/v2 v2.17.1/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= @@ -503,11 +516,13 @@ github.com/goccy/go-json v0.7.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGF github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-yaml v1.9.8/go.mod h1:JubOolP3gh0HpiBc4BLRD4YmjEjHAmIIB2aaXKkTfoE= -github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I= -github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= +github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= -github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= +github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= +github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= +github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= +github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -526,6 +541,7 @@ github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 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/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= @@ -561,8 +577,8 @@ github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Z github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= -github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -584,11 +600,10 @@ github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBE github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= github.com/google/go-github/v32 v32.1.0/go.mod h1:rIEpZD9CTDQwDK9GDrtMTycQNA4JU3qBsCizh3q2WCI= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= +github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -609,12 +624,12 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= -github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ= +github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -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/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y= +github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14= github.com/gophercloud/gophercloud v1.3.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= github.com/gophercloud/gophercloud v1.14.1 h1:DTCNaTVGl8/cFu58O1JwWgis9gtISAFONqpMKNg/Vpw= github.com/gophercloud/gophercloud v1.14.1/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= @@ -627,8 +642,8 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/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/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf h1:C1GPyPJrOlJlIrcaBBiBpDsqZena2Ks8spa5xZqr1XQ= github.com/gravitational/trace v1.1.16-0.20220114165159-14a9a7dd6aaf/go.mod h1:zXqxTI6jXDdKnlf8s+nT+3c8LrwUEy3yNpO4XJL90lA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -683,8 +698,8 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= +github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -714,8 +729,8 @@ github.com/http-wasm/http-wasm-host-go v0.7.0/go.mod h1:adXKcLmL7yuavH/e0kBAp7b3 github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.172 h1:68VUVbLKwBxPh8tjCXwnLO/d8/thdZ+ExpxdFMEdK5A= -github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.172/go.mod h1:M+yna96Fx9o5GbIUnF3OvVvQGjgfVSyeJbV9Yb1z/wI= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.182 h1:B3W9acgpqu5XsN8v+W8SOTfqn/6n4JsjgoKBsm30HFY= +github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.182/go.mod h1:M+yna96Fx9o5GbIUnF3OvVvQGjgfVSyeJbV9Yb1z/wI= github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -748,8 +763,8 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= -github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= -github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= +github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= +github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= @@ -830,8 +845,8 @@ github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++ github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc= github.com/lestrrat-go/jwx v1.2.7/go.mod h1:bw24IXWbavc0R2RsOtpXL7RtMyP589yZ1+L7kd09ZGA= github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= -github.com/linode/linodego v1.60.0 h1:SgsebJFRCi+lSmYy+C40wmKZeJllGGm+W12Qw4+yVdI= -github.com/linode/linodego v1.60.0/go.mod h1:1+Bt0oTz5rBnDOJbGhccxn7LYVytXTIIfAy7QYmijDs= +github.com/linode/linodego v1.64.0 h1:If6pULIwHuQytgogtpQaBdVLX7z2TTHUF5u1tj2TPiY= +github.com/linode/linodego v1.64.0/go.mod h1:GoiwLVuLdBQcAebxAVKVL3mMYUgJZR/puOUSla04xBE= github.com/liquidweb/go-lwApi v0.0.0-20190605172801-52a4864d2738/go.mod h1:0sYF9rMXb0vlG+4SzdiGMXHheCZxjguMq+Zb4S2BfBs= github.com/liquidweb/liquidweb-cli v0.6.9 h1:acbIvdRauiwbxIsOCEMXGwF75aSJDbDiyAWPjVnwoYM= github.com/liquidweb/liquidweb-cli v0.6.9/go.mod h1:cE1uvQ+x24NGUL75D0QagOFCG8Wdvmwu8aL9TLmA/eQ= @@ -852,8 +867,8 @@ github.com/mailgun/ttlmap v0.0.0-20170619185759-c1c17f74874f h1:ZZYhg16XocqSKPGN github.com/mailgun/ttlmap v0.0.0-20170619185759-c1c17f74874f/go.mod h1:8heskWJ5c0v5J9WH89ADhyal1DOZcayll8fSbhB+/9A= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +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/matryer/moq v0.0.0-20190312154309-6cfb0558e1bd/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= @@ -889,8 +904,8 @@ github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKju github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.47/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= -github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= -github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= +github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc= +github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g= github.com/mimuret/golang-iij-dpf v0.9.1 h1:Gj6EhHJkOhr+q2RnvRPJsPMcjuVnWPSccEHyoEehU34= github.com/mimuret/golang-iij-dpf v0.9.1/go.mod h1:sl9KyOkESib9+KRD3HaGpgi1xk7eoN2+d96LCLsME2M= github.com/minio/highwayhash v1.0.1/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= @@ -969,30 +984,32 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nrdcg/auroradns v1.1.0 h1:KekGh8kmf2MNwqZVVYo/fw/ZONt8QMEmbMFOeljteWo= github.com/nrdcg/auroradns v1.1.0/go.mod h1:O7tViUZbAcnykVnrGkXzIJTHoQCHcgalgAe6X1mzHfk= -github.com/nrdcg/bunny-go v0.0.0-20250327222614-988a091fc7ea h1:OSgRS4kqOs/WuxuFOObP2gwrenL4/qiKXQbQugr/Two= -github.com/nrdcg/bunny-go v0.0.0-20250327222614-988a091fc7ea/go.mod h1:IDRRngAngb2eTEaWgpO0hukQFI/vJId46fT1KErMytA= -github.com/nrdcg/desec v0.11.0 h1:XZVHy07sg12y8FozMp+l7XvzPsdzog0AYXuQMaHBsfs= -github.com/nrdcg/desec v0.11.0/go.mod h1:5+4vyhMRTs49V9CNoODF/HwT8Mwxv9DJ6j+7NekUnBs= +github.com/nrdcg/bunny-go v0.1.0 h1:GAHTRpHaG/TxfLZlqoJ8OJFzw8rI74+jOTkzxWh0uHA= +github.com/nrdcg/bunny-go v0.1.0/go.mod h1:u+C9dgsspgtWVaAz6QkyV17s9fxD8viwwKoxb9XMz1A= +github.com/nrdcg/desec v0.11.1 h1:ilpKmCr4gGsLcyq3RHfHNmlRzm9fzT2XbWxoVaUCS0s= +github.com/nrdcg/desec v0.11.1/go.mod h1:2LuxHlOcwML/7cntu0eimONmA1U+ZxFDAonoSXr4igQ= github.com/nrdcg/dnspod-go v0.4.0 h1:c/jn1mLZNKF3/osJ6mz3QPxTudvPArXTjpkmYj0uK6U= github.com/nrdcg/dnspod-go v0.4.0/go.mod h1:vZSoFSFeQVm2gWLMkyX61LZ8HI3BaqtHZWgPTGKr6KQ= github.com/nrdcg/freemyip v0.3.0 h1:0D2rXgvLwe2RRaVIjyUcQ4S26+cIS2iFwnhzDsEuuwc= github.com/nrdcg/freemyip v0.3.0/go.mod h1:c1PscDvA0ukBF0dwelU/IwOakNKnVxetpAQ863RMJoM= github.com/nrdcg/goacmedns v0.2.0 h1:ADMbThobzEMnr6kg2ohs4KGa3LFqmgiBA22/6jUWJR0= github.com/nrdcg/goacmedns v0.2.0/go.mod h1:T5o6+xvSLrQpugmwHvrSNkzWht0UGAwj2ACBMhh73Cg= -github.com/nrdcg/goinwx v0.11.0 h1:GER0SE3POub7rxARt3Y3jRy1OON1hwF1LRxHz5xsFBw= -github.com/nrdcg/goinwx v0.11.0/go.mod h1:0BXSC0FxVtU4aTjX0Zw3x0DK32tjugLzeNIAGtwXvPQ= -github.com/nrdcg/mailinabox v0.2.0 h1:IKq8mfKiVwNW2hQii/ng1dJ4yYMMv3HAP3fMFIq2CFk= -github.com/nrdcg/mailinabox v0.2.0/go.mod h1:0yxqeYOiGyxAu7Sb94eMxHPIOsPYXAjTeA9ZhePhGnc= +github.com/nrdcg/goinwx v0.12.0 h1:ujdUqDBnaRSFwzVnImvPHYw3w3m9XgmGImNUw1GyMb4= +github.com/nrdcg/goinwx v0.12.0/go.mod h1:IrVKd3ZDbFiMjdPgML4CSxZAY9wOoqLvH44zv3NodJ0= +github.com/nrdcg/mailinabox v0.3.0 h1:PHkC1elKXKAjEvdx2HHFMgcEGZFqudAl7aU3L2JDhM4= +github.com/nrdcg/mailinabox v0.3.0/go.mod h1:1eFIGcM4lI+AfFOUpbs548SFGz1ZWoMOGbECBmkghw4= github.com/nrdcg/namesilo v0.5.0 h1:6QNxT/XxE+f5B+7QlfWorthNzOzcGlBLRQxqi6YeBrE= github.com/nrdcg/namesilo v0.5.0/go.mod h1:4UkwlwQfDt74kSGmhLaDylnBrD94IfflnpoEaj6T2qw= github.com/nrdcg/nodion v0.1.0 h1:zLKaqTn2X0aDuBHHfyA1zFgeZfiCpmu/O9DM73okavw= github.com/nrdcg/nodion v0.1.0/go.mod h1:inbuh3neCtIWlMPZHtEpe43TmRXxHV6+hk97iCZicms= -github.com/nrdcg/oci-go-sdk/common/v1065 v1065.102.0 h1:W28ZizQSS2aRWkFA3iAP9eiZS4OLFaiv35nXtq2lW/s= -github.com/nrdcg/oci-go-sdk/common/v1065 v1065.102.0/go.mod h1:cVbzGjRhtXgrduaQbR1GR1x+VDU60NcXPMZ3+eQuiiY= -github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.102.0 h1:gAOs1dkE7LFoWflzqrDqAhOprc0kF1a0fyV8C4HUPj4= -github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.102.0/go.mod h1:EUBSYwop1K40VpcKy1haIK6kFK/gPT1atEk89OkY0Kg= +github.com/nrdcg/oci-go-sdk/common/v1065 v1065.105.2 h1:l0tH15ACQADZAzC+LZ+mo2tIX4H6uZu0ulrVmG5Tqz0= +github.com/nrdcg/oci-go-sdk/common/v1065 v1065.105.2/go.mod h1:Gcs8GCaZXL3FdiDWgdnMxlOLEdRprJJnPYB22TX1jw8= +github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.105.2 h1:gzB4c6ztb38C/jYiqEaFC+mCGcWFHDji9e6jwymY9d4= +github.com/nrdcg/oci-go-sdk/dns/v1065 v1065.105.2/go.mod h1:l1qIPIq2uRV5WTSvkbhbl/ndbeOu7OCb3UZ+0+2ZSb8= github.com/nrdcg/porkbun v0.4.0 h1:rWweKlwo1PToQ3H+tEO9gPRW0wzzgmI/Ob3n2Guticw= github.com/nrdcg/porkbun v0.4.0/go.mod h1:/QMskrHEIM0IhC/wY7iTCUgINsxdT2WcOphktJ9+Q54= +github.com/nrdcg/vegadns v0.3.0 h1:11FQMw7xVIRUWO9o5+Z/5YZhmPWlm4oxUUH3F6EVqQU= +github.com/nrdcg/vegadns v0.3.0/go.mod h1:NqSyRKZuJlAsv8VI/7rSubfPXN68NwaJ0aG9KxQVFVo= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -1025,6 +1042,8 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= +github.com/openzipkin/zipkin-go v0.4.3 h1:9EGwpqkgnwdEIJ+Od7QVSEIH+ocmm5nPat0G7sjsSdg= +github.com/openzipkin/zipkin-go v0.4.3/go.mod h1:M9wCJZFWCo2RiY+o1eBCEMe0Dp2S5LDHcMZmk3RmK7c= github.com/ovh/go-ovh v1.9.0 h1:6K8VoL3BYjVV3In9tPJUdT7qMx9h0GExN9EXx1r2kKE= github.com/ovh/go-ovh v1.9.0/go.mod h1:cTVDnl94z4tl8pP1uZ/8jlVxntjSIf09bNcQ5TJSC7c= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -1070,14 +1089,14 @@ github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQ github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= +github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/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.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -1086,8 +1105,8 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= -github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= +github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -1096,13 +1115,15 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= -github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +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/prometheus/statsd_exporter v0.22.7 h1:7Pji/i2GuhK6Lu7DHrtTkFmNBCudCPT1pX2CziuyQR0= +github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= -github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= -github.com/quic-go/quic-go v0.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk= -github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U= +github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= +github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= +github.com/quic-go/quic-go v0.58.0 h1:ggY2pvZaVdB9EyojxL1p+5mptkuHyX5MOSv4dgWF4Ug= +github.com/quic-go/quic-go v0.58.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= @@ -1118,6 +1139,8 @@ github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0t github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529 h1:18kd+8ZUlt/ARXhljq+14TwAoKa61q6dX8jtwOf6DH8= +github.com/rs/dnscache v0.0.0-20230804202142-fc85eb664529/go.mod h1:qe5TWALJ8/a1Lqznoc5BDHpYX/8HU60Hm2AwRmqzxqA= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= @@ -1127,16 +1150,16 @@ github.com/sacloud/api-client-go v0.3.3 h1:ZpSAyGpITA8UFO3Hq4qMHZLGuNI1FgxAxo4sq github.com/sacloud/api-client-go v0.3.3/go.mod h1:0p3ukcWYXRCc2AUWTl1aA+3sXLvurvvDqhRaLZRLBwo= github.com/sacloud/go-http v0.1.9 h1:Xa5PY8/pb7XWhwG9nAeXSrYXPbtfBWqawgzxD5co3VE= github.com/sacloud/go-http v0.1.9/go.mod h1:DpDG+MSyxYaBwPJ7l3aKLMzwYdTVtC5Bo63HActcgoE= -github.com/sacloud/iaas-api-go v1.19.0 h1:Bw4uygqukcvlblhWrITp94nikXqy2fnKoAC6929LkIA= -github.com/sacloud/iaas-api-go v1.19.0/go.mod h1:XV995RM1I7k5AHb7UZrCVyDF/8bZXDxa+uk1EXoj/Zs= -github.com/sacloud/packages-go v0.0.11 h1:hrRWLmfPM9w7GBs6xb5/ue6pEMl8t1UuDKyR/KfteHo= -github.com/sacloud/packages-go v0.0.11/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZOmmQORIzcurJ8= +github.com/sacloud/iaas-api-go v1.23.1 h1:rjYG0vVoxWyETiwc7R8YdD7CIzs9vVNEOzu7w6dgGzc= +github.com/sacloud/iaas-api-go v1.23.1/go.mod h1:EGIHOWRB9azOv7HPCVM8WpOEl28WIV9TNRbnEVg+Q3U= +github.com/sacloud/packages-go v0.0.12 h1:MKeZNN3FQn1heqUSRBrbZw89YusZA1n4kammjMFZYvQ= +github.com/sacloud/packages-go v0.0.12/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZOmmQORIzcurJ8= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.35 h1:8xfn1RzeI9yoCUuEwDy08F+No6PcKZGEDOQ6hrRyLts= -github.com/scaleway/scaleway-sdk-go v1.0.0-beta.35/go.mod h1:47B1d/YXmSAxlJxUJxClzHR6b3T4M1WyCvwENPQNBWc= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.36 h1:ObX9hZmK+VmijreZO/8x9pQ8/P/ToHD/bdSb4Eg4tUo= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.36/go.mod h1:LEsDu4BubxK7/cWhtlQWfuxwL4rf/2UEpxXz1o1EMtM= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM= @@ -1164,13 +1187,8 @@ github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0= github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= -github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 h1:hp2CYQUINdZMHdvTdXtPOY2ainKl4IoMcpAXEf2xj3Q= -github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/smartystreets/gunit v1.0.4 h1:tpTjnuH7MLlqhoD21vRoMZbMIi5GmBsAJDFyF67GhZA= -github.com/smartystreets/gunit v1.0.4/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ= github.com/softlayer/softlayer-go v1.2.1 h1:8ucHxn5laVsVPb0/aMGnr6tOMt1I9BgEtU5mn70OGKw= github.com/softlayer/softlayer-go v1.2.1/go.mod h1:Gz9/ktcmB7Z8EJlu+QEJJpkv8lAmnhYdB9Tc6gedjmo= github.com/softlayer/xmlrpc v0.0.0-20200409220501-5f089df7cb7e h1:3OgWYFw7jxCZPcvAg+4R8A50GZ+CCkARF10lxu2qDsQ= @@ -1201,8 +1219,8 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= -github.com/spiffe/go-spiffe/v2 v2.5.0 h1:N2I01KCUkv1FAjZXJMwh95KK1ZIQLYbPfhaxw8WS0hE= -github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= +github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= +github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/stealthrocket/wasi-go v0.8.0 h1:Hwnv3CUoMhhRyero9vt1vfwaYa9tu/Z5kmCW4WeAmVI= github.com/stealthrocket/wasi-go v0.8.0/go.mod h1:PJ5oVs2E1ciOJnsTnav4nvTtEcJ4D1jUZAewS9pzuZg= github.com/stealthrocket/wazergo v0.19.1 h1:BPrITETPgSFwiytwmToO0MbUC/+RGC39JScz1JmmG6c= @@ -1232,17 +1250,17 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 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/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154 h1:XGopsea1Dw7ecQ8JscCNQXDGYAKDiWjDeXnpN/+BY9g= -github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= +github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807 h1:LUsDduamlucuNnWcaTbXQ6aLILFcLXADpOzeEH3U+OI= +github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046 h1:8rUlviSVOEe7TMk7W0gIPrW8MqEzYfZHpsNWSf8s2vg= github.com/tailscale/tscert v0.0.0-20230806124524-28a91b69a046/go.mod h1:kNGUQ3VESx3VZwRwA9MSCUegIl6+saPL8Noq82ozCaU= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.10/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.19/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.41 h1:XQDGrLX6v4McMP+2myhgQcy5JaPqSgwpLM1qa7ngUII= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.41/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.25/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.48/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.28 h1:Rj1WXXNPm9AsPf0PJhWCvlsqfcKPUYdyVnkmEc3O8sI= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.3.28/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/testcontainers/testcontainers-go v0.32.0 h1:ug1aK08L3gCHdhknlTTwWjPHPS+/alvLJU/DRxTD/ME= github.com/testcontainers/testcontainers-go v0.32.0/go.mod h1:CRHrzHLQhlXUsa5gXjTOfqIEJcrK5+xMDmBr/WMI88E= github.com/testcontainers/testcontainers-go/modules/k3s v0.32.0 h1:Z3DTMveNUqeGJZ+CXZhpvI7OF1BS71Ywi3SwoXLZ4Lc= @@ -1299,16 +1317,16 @@ github.com/valyala/fasthttp v1.58.0 h1:GGB2dWxSbEprU9j0iMJHgdKYJVDyjrOwF9RE59PbR github.com/valyala/fasthttp v1.58.0/go.mod h1:SYXvHHaFp7QZHGKSHmoMipInhrI5StHrhDTYVEjK/Kw= github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= -github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ= -github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q= -github.com/volcengine/volc-sdk-golang v1.0.223 h1:1EEK6VOUaA2Tu0VBD4VC5iSTTFag+KuNo+Vix469Tz4= -github.com/volcengine/volc-sdk-golang v1.0.223/go.mod h1:zHJlaqiMbIB+0mcrsZPTwOb3FB7S/0MCfqlnO8R7hlM= +github.com/vinyldns/go-vinyldns v0.9.17 h1:hfPZfCaxcRBX6Gsgl42rLCeoal58/BH8kkvJShzjjdI= +github.com/vinyldns/go-vinyldns v0.9.17/go.mod h1:pwWhE9K/leGDOIduVhRGvQ3ecVMHWRfEnKYUTEU3gB4= +github.com/volcengine/volc-sdk-golang v1.0.233 h1:Hh2pzwu/Wq19rsZgNo3HdpjQB28D/F0+m6EjLVggmhM= +github.com/volcengine/volc-sdk-golang v1.0.233/go.mod h1:zHJlaqiMbIB+0mcrsZPTwOb3FB7S/0MCfqlnO8R7hlM= github.com/vulcand/oxy/v2 v2.0.3 h1:CPWVPfW4hVZXzwwiQzpFidbnJKpahjPHezM+7TkZRNw= github.com/vulcand/oxy/v2 v2.0.3/go.mod h1:k3t+xjyqmXVh88FdFDbYmUKMEvNpaejvBW14es6H70A= github.com/vulcand/predicate v1.2.0 h1:uFsW1gcnnR7R+QTID+FVcs0sSYlIGntoGOTb3rQJt50= github.com/vulcand/predicate v1.2.0/go.mod h1:VipoNYXny6c8N381zGUWkjuuNHiRbeAZhE7Qm9c+2GA= -github.com/vultr/govultr/v3 v3.24.0 h1:fTTTj0VBve+Miy+wGhlb90M2NMDfpGFi6Frlj3HVy6M= -github.com/vultr/govultr/v3 v3.24.0/go.mod h1:9WwnWGCKnwDlNjHjtt+j+nP+0QWq6hQXzaHgddqrLWY= +github.com/vultr/govultr/v3 v3.26.1 h1:G/M0rMQKwVSmL+gb0UgETbW5mcQi0Vf/o/ZSGdBCxJw= +github.com/vultr/govultr/v3 v3.26.1/go.mod h1:9WwnWGCKnwDlNjHjtt+j+nP+0QWq6hQXzaHgddqrLWY= 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/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= @@ -1319,12 +1337,12 @@ github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gi github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -github.com/yandex-cloud/go-genproto v0.31.0 h1:mFMS5SD1Lt8qErwefR8ChK3d0jg0tvbDLq57IqenpTg= -github.com/yandex-cloud/go-genproto v0.31.0/go.mod h1:0LDD/IZLIUIV4iPH+YcF+jysO3jkSvADFGm4dCAuwQo= -github.com/yandex-cloud/go-sdk/services/dns v0.0.12 h1:c5TNaX7r3DqY37YJFbr7HyQFRcSe1WzCbR81LVwxXyk= -github.com/yandex-cloud/go-sdk/services/dns v0.0.12/go.mod h1:6CRtIkxq6iTSZIOT42EFns54CEr35ncECy4ix9lXUd4= -github.com/yandex-cloud/go-sdk/v2 v2.19.0 h1:Cuzjn6kkOD/KrBF/QyDbKS7b5GAu8fC2ZUjdBjit60A= -github.com/yandex-cloud/go-sdk/v2 v2.19.0/go.mod h1:4SwghU8RB4v2OQzhESgq5SF8XmCXIP80WhgrrNpetJ8= +github.com/yandex-cloud/go-genproto v0.43.0 h1:HjBesEmCN8ZOhjjh8gs605vvi9/MBJAW3P20OJ4iQnw= +github.com/yandex-cloud/go-genproto v0.43.0/go.mod h1:0LDD/IZLIUIV4iPH+YcF+jysO3jkSvADFGm4dCAuwQo= +github.com/yandex-cloud/go-sdk/services/dns v0.0.25 h1:BcGEuOnwq2X3LS2kvFC6BOdZkOq4Lc7XAYvzap/SJJY= +github.com/yandex-cloud/go-sdk/services/dns v0.0.25/go.mod h1:B4QHijALUHIjRxL3aqmOwDrHYUI2XdeeG4WKItth3jI= +github.com/yandex-cloud/go-sdk/v2 v2.37.0 h1:WvttW6p9xcWag9j+GQv+GJXPggggXGwOlIJNfkWmFWw= +github.com/yandex-cloud/go-sdk/v2 v2.37.0/go.mod h1:Dt4a81enjRsm4xMJyW5E1Y/vaUYwXJvUGRdDLuM2k6I= github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= @@ -1339,8 +1357,6 @@ github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM= -github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.6.4 h1:7F6N7toCKcV72QmoUKa23yYLiiljMrT4xCeBL9BmXdo= @@ -1360,8 +1376,10 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/collector/featuregate v1.41.0 h1:CL4UMsMQj35nMJC3/jUu8VvYB4MHirbAX4B0Z/fCVLY= go.opentelemetry.io/collector/featuregate v1.41.0/go.mod h1:A72x92glpH3zxekaUybml1vMSv94BH6jQRn5+/htcjw= go.opentelemetry.io/collector/pdata v1.41.0 h1:2zurAaY0FkURbLa1x7f7ag6HaNZYZKSmI4wgzDegLgo= @@ -1445,6 +1463,10 @@ go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= 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/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= @@ -1487,8 +1509,8 @@ golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDf golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1533,8 +1555,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1597,17 +1619,16 @@ golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY= -golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= 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= @@ -1624,8 +1645,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1723,8 +1744,8 @@ golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1741,8 +1762,8 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= -golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1761,8 +1782,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1832,14 +1853,14 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= +golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= 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= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= @@ -1862,8 +1883,8 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -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/api v0.259.0 h1:90TaGVIxScrh1Vn/XI2426kRpBqHwWIzVBzJsVZ5XrQ= +google.golang.org/api v0.259.0/go.mod h1:LC2ISWGWbRoyQVpxGntWwLWN/vLNxxKBK9KuJRI8Te4= 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.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1902,12 +1923,12 @@ google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -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-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= -google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= -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/genproto v0.0.0-20251202230838-ff82c1b0f217 h1:GvESR9BIyHUahIb0NcTum6itIWtdoglGX+rnGxm2934= +google.golang.org/genproto v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:yJ2HH4EHEDTd3JiLmhds6NkJ17ITVYOdV3m3VKOnws0= +google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls= +google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -1925,8 +1946,8 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= -google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= 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= @@ -1941,8 +1962,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 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.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1951,8 +1972,8 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= -gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= @@ -1965,8 +1986,8 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/ns1/ns1-go.v2 v2.15.0 h1:cE3xSMdSCV8kf9SQldzqgW/Ueh7sv3yO2JwKtYxxz3E= -gopkg.in/ns1/ns1-go.v2 v2.15.0/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc= +gopkg.in/ns1/ns1-go.v2 v2.16.0 h1:mUczKFnrCystSV7yIODzVSbENoud3T7DwstmyVZfqg4= +gopkg.in/ns1/ns1-go.v2 v2.16.0/go.mod h1:pfaU0vECVP7DIOr453z03HXS6dFJpXdNRwOyRzwmPSc= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= @@ -1994,20 +2015,24 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= -k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= -k8s.io/apiextensions-apiserver v0.32.3 h1:4D8vy+9GWerlErCwVIbcQjsWunF9SUGNu7O7hiQTyPY= -k8s.io/apiextensions-apiserver v0.32.3/go.mod h1:8YwcvVRMVzw0r1Stc7XfGAzB/SIVLunqApySV5V7Dss= -k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= -k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= -k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= +k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4= +k8s.io/api v0.34.3/go.mod h1:PyVQBF886Q5RSQZOim7DybQjAbVs8g7gwJNhGtY5MBk= +k8s.io/apiextensions-apiserver v0.34.3 h1:p10fGlkDY09eWKOTeUSioxwLukJnm+KuDZdrW71y40g= +k8s.io/apiextensions-apiserver v0.34.3/go.mod h1:aujxvqGFRdb/cmXYfcRTeppN7S2XV/t7WMEc64zB5A0= +k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE= +k8s.io/apimachinery v0.34.3/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A= +k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM= 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-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= -k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= -k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= -k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3 h1:liMHz39T5dJO1aOKHLvwaCjDbf07wVh6yaUlTpunnkE= +k8s.io/kube-openapi v0.0.0-20250814151709-d7b6acb124c3/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= +k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d h1:wAhiDyZ4Tdtt7e46e9M5ZSAJ/MnPGPs+Ki1gHw4w1R0= +k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +knative.dev/networking v0.0.0-20241022012959-60e29ff520dc h1:0d9XXRLlyuHfINZLlYqo/BYe/+chqqNBMLKJldjTbtw= +knative.dev/networking v0.0.0-20241022012959-60e29ff520dc/go.mod h1:G56j6VCLzfaN9yZ4IqfNyN4c3U1czvhUmKeZX4UjQ8Q= +knative.dev/pkg v0.0.0-20241021183759-9b9d535af5ad h1:Nrjtr2H168rJeamH4QdyLMV1lEKHejNhaj1ymgQMfLk= +knative.dev/pkg v0.0.0-20241021183759-9b9d535af5ad/go.mod h1:StJI72GWcm/iErmk4RqFJiOo8RLbVqPbHxUqeVwAzeo= mvdan.cc/xurls/v2 v2.5.0 h1:lyBNOm8Wo71UknhUs4QTFUNNMyxy2JEIaKKo0RWOh+8= mvdan.cc/xurls/v2 v2.5.0/go.mod h1:yQgaGQ1rFtJUzkmKiHYSSfuQxqfYmd//X6PxvholpeE= nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= @@ -2017,16 +2042,16 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8 rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/controller-runtime v0.20.4 h1:X3c+Odnxz+iPTRobG4tp092+CvBU9UK0t/bRf+n0DGU= -sigs.k8s.io/controller-runtime v0.20.4/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY= -sigs.k8s.io/gateway-api v1.3.0 h1:q6okN+/UKDATola4JY7zXzx40WO4VISk7i9DIfOvr9M= -sigs.k8s.io/gateway-api v1.3.0/go.mod h1:d8NV8nJbaRbEKem+5IuxkL8gJGOZ+FJ+NvOIltV8gDk= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= -sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016 h1:kXv6kKdoEtedwuqMmkqhbkgvYKeycVbC8+iPCP9j5kQ= -sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= -sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/controller-runtime v0.22.1 h1:Ah1T7I+0A7ize291nJZdS1CabF/lB4E++WizgV24Eqg= +sigs.k8s.io/controller-runtime v0.22.1/go.mod h1:FwiwRjkRPbiN+zp2QRp7wlTCzbUXxZ/D4OzuQUDwBHY= +sigs.k8s.io/gateway-api v1.4.0 h1:ZwlNM6zOHq0h3WUX2gfByPs2yAEsy/EenYJB78jpQfQ= +sigs.k8s.io/gateway-api v1.4.0/go.mod h1:AR5RSqciWP98OPckEjOjh2XJhAe2Na4LHyXD2FUY7Qk= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/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.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/integration/acme_test.go b/integration/acme_test.go index c6c92e24b..a226ea88d 100644 --- a/integration/acme_test.go +++ b/integration/acme_test.go @@ -21,6 +21,7 @@ import ( "github.com/traefik/traefik/v3/pkg/provider/acme" "github.com/traefik/traefik/v3/pkg/testhelpers" "github.com/traefik/traefik/v3/pkg/types" + "k8s.io/utils/strings/slices" ) // ACME test suites. @@ -35,9 +36,9 @@ func TestAcmeSuite(t *testing.T) { } type subCases struct { - host string - expectedCommonName string - expectedAlgorithm x509.PublicKeyAlgorithm + host string + expectedDomain string + expectedAlgorithm x509.PublicKeyAlgorithm } type acmeTestCase struct { @@ -142,9 +143,9 @@ func (s *AcmeSuite) TestHTTP01Domains() { testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme_domains.toml", subCases: []subCases{{ - host: acmeDomain, - expectedCommonName: acmeDomain, - expectedAlgorithm: x509.RSA, + host: acmeDomain, + expectedDomain: acmeDomain, + expectedAlgorithm: x509.RSA, }}, template: templateModel{ Domains: []types.Domain{{ @@ -165,9 +166,9 @@ func (s *AcmeSuite) TestHTTP01StoreDomains() { testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme_store_domains.toml", subCases: []subCases{{ - host: acmeDomain, - expectedCommonName: acmeDomain, - expectedAlgorithm: x509.RSA, + host: acmeDomain, + expectedDomain: acmeDomain, + expectedAlgorithm: x509.RSA, }}, template: templateModel{ Domain: types.Domain{ @@ -188,9 +189,9 @@ func (s *AcmeSuite) TestHTTP01DomainsInSAN() { testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme_domains.toml", subCases: []subCases{{ - host: acmeDomain, - expectedCommonName: "acme.wtf", - expectedAlgorithm: x509.RSA, + host: acmeDomain, + expectedDomain: "acme.wtf", + expectedAlgorithm: x509.RSA, }}, template: templateModel{ Domains: []types.Domain{{ @@ -212,9 +213,9 @@ func (s *AcmeSuite) TestHTTP01OnHostRule() { testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme_base.toml", subCases: []subCases{{ - host: acmeDomain, - expectedCommonName: acmeDomain, - expectedAlgorithm: x509.RSA, + host: acmeDomain, + expectedDomain: acmeDomain, + expectedAlgorithm: x509.RSA, }}, template: templateModel{ Acme: map[string]static.CertificateResolver{ @@ -233,14 +234,14 @@ func (s *AcmeSuite) TestMultipleResolver() { traefikConfFilePath: "fixtures/acme/acme_multiple_resolvers.toml", subCases: []subCases{ { - host: acmeDomain, - expectedCommonName: acmeDomain, - expectedAlgorithm: x509.RSA, + host: acmeDomain, + expectedDomain: acmeDomain, + expectedAlgorithm: x509.RSA, }, { - host: "tchouk.acme.wtf", - expectedCommonName: "tchouk.acme.wtf", - expectedAlgorithm: x509.ECDSA, + host: "tchouk.acme.wtf", + expectedDomain: "tchouk.acme.wtf", + expectedAlgorithm: x509.ECDSA, }, }, template: templateModel{ @@ -263,9 +264,9 @@ func (s *AcmeSuite) TestHTTP01OnHostRuleECDSA() { testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme_base.toml", subCases: []subCases{{ - host: acmeDomain, - expectedCommonName: acmeDomain, - expectedAlgorithm: x509.ECDSA, + host: acmeDomain, + expectedDomain: acmeDomain, + expectedAlgorithm: x509.ECDSA, }}, template: templateModel{ Acme: map[string]static.CertificateResolver{ @@ -284,9 +285,9 @@ func (s *AcmeSuite) TestHTTP01OnHostRuleInvalidAlgo() { testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme_base.toml", subCases: []subCases{{ - host: acmeDomain, - expectedCommonName: acmeDomain, - expectedAlgorithm: x509.RSA, + host: acmeDomain, + expectedDomain: acmeDomain, + expectedAlgorithm: x509.RSA, }}, template: templateModel{ Acme: map[string]static.CertificateResolver{ @@ -301,13 +302,14 @@ func (s *AcmeSuite) TestHTTP01OnHostRuleInvalidAlgo() { s.retrieveAcmeCertificate(testCase) } +// TODO: check why this test do not use the ACME cert resolver. func (s *AcmeSuite) TestHTTP01OnHostRuleDefaultDynamicCertificatesWithWildcard() { testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme_tls.toml", subCases: []subCases{{ - host: acmeDomain, - expectedCommonName: wildcardDomain, - expectedAlgorithm: x509.RSA, + host: acmeDomain, + expectedDomain: wildcardDomain, + expectedAlgorithm: x509.RSA, }}, template: templateModel{ Acme: map[string]static.CertificateResolver{ @@ -321,13 +323,14 @@ func (s *AcmeSuite) TestHTTP01OnHostRuleDefaultDynamicCertificatesWithWildcard() s.retrieveAcmeCertificate(testCase) } +// TODO: check why this test do not use the ACME cert resolver. func (s *AcmeSuite) TestHTTP01OnHostRuleDynamicCertificatesWithWildcard() { testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme_tls_dynamic.toml", subCases: []subCases{{ - host: acmeDomain, - expectedCommonName: wildcardDomain, - expectedAlgorithm: x509.RSA, + host: acmeDomain, + expectedDomain: wildcardDomain, + expectedAlgorithm: x509.RSA, }}, template: templateModel{ Acme: map[string]static.CertificateResolver{ @@ -345,9 +348,9 @@ func (s *AcmeSuite) TestTLSALPN01OnHostRuleTCP() { testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme_tcp.toml", subCases: []subCases{{ - host: acmeDomain, - expectedCommonName: acmeDomain, - expectedAlgorithm: x509.RSA, + host: acmeDomain, + expectedDomain: acmeDomain, + expectedAlgorithm: x509.RSA, }}, template: templateModel{ Acme: map[string]static.CertificateResolver{ @@ -365,9 +368,9 @@ func (s *AcmeSuite) TestTLSALPN01OnHostRule() { testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme_base.toml", subCases: []subCases{{ - host: acmeDomain, - expectedCommonName: acmeDomain, - expectedAlgorithm: x509.RSA, + host: acmeDomain, + expectedDomain: acmeDomain, + expectedAlgorithm: x509.RSA, }}, template: templateModel{ Acme: map[string]static.CertificateResolver{ @@ -385,9 +388,9 @@ func (s *AcmeSuite) TestTLSALPN01Domains() { testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme_domains.toml", subCases: []subCases{{ - host: acmeDomain, - expectedCommonName: acmeDomain, - expectedAlgorithm: x509.RSA, + host: acmeDomain, + expectedDomain: acmeDomain, + expectedAlgorithm: x509.RSA, }}, template: templateModel{ Domains: []types.Domain{{ @@ -408,9 +411,9 @@ func (s *AcmeSuite) TestTLSALPN01DomainsInSAN() { testCase := acmeTestCase{ traefikConfFilePath: "fixtures/acme/acme_domains.toml", subCases: []subCases{{ - host: acmeDomain, - expectedCommonName: "acme.wtf", - expectedAlgorithm: x509.RSA, + host: acmeDomain, + expectedDomain: "acme.wtf", + expectedAlgorithm: x509.RSA, }}, template: templateModel{ Domains: []types.Domain{{ @@ -502,27 +505,38 @@ func (s *AcmeSuite) retrieveAcmeCertificate(testCase acmeTestCase) { req.Header.Set("Host", sub.host) req.Header.Set("Accept", "*/*") - var resp *http.Response + var ( + gotStatusCode int + gotDomains []string + gotPublicKeyAlgorithm x509.PublicKeyAlgorithm + ) // Retry to send a Request which uses the LE generated certificate err := try.Do(60*time.Second, func() error { - resp, err = client.Do(req) + resp, err := client.Do(req) if err != nil { return err } - cn := resp.TLS.PeerCertificates[0].Subject.CommonName - if cn != sub.expectedCommonName { - return fmt.Errorf("domain %s found instead of %s", cn, sub.expectedCommonName) + gotStatusCode = resp.StatusCode + gotPublicKeyAlgorithm = resp.TLS.PeerCertificates[0].PublicKeyAlgorithm + + // Here we are collecting the common name as it is used in wildcard tests. + gotDomains = append(gotDomains, resp.TLS.PeerCertificates[0].Subject.CommonName) + gotDomains = append(gotDomains, resp.TLS.PeerCertificates[0].DNSNames...) + + if !slices.Contains(gotDomains, sub.expectedDomain) { + return fmt.Errorf("domain name %s not found in domain names: %v", sub.expectedDomain, gotDomains) } return nil }) require.NoError(s.T(), err) - assert.Equal(s.T(), http.StatusOK, resp.StatusCode) + assert.Equal(s.T(), http.StatusOK, gotStatusCode) + // Check Domain into response certificate - assert.Equal(s.T(), sub.expectedCommonName, resp.TLS.PeerCertificates[0].Subject.CommonName) - assert.Equal(s.T(), sub.expectedAlgorithm, resp.TLS.PeerCertificates[0].PublicKeyAlgorithm) + assert.Contains(s.T(), gotDomains, sub.expectedDomain) + assert.Equal(s.T(), sub.expectedAlgorithm, gotPublicKeyAlgorithm) } } diff --git a/integration/docker_test.go b/integration/docker_test.go index d6a6819ff..08606b60b 100644 --- a/integration/docker_test.go +++ b/integration/docker_test.go @@ -4,7 +4,6 @@ import ( "encoding/json" "io" "net/http" - "strings" "testing" "time" @@ -33,7 +32,7 @@ func (s *DockerSuite) TearDownSuite() { } func (s *DockerSuite) TearDownTest() { - s.composeStop("simple", "withtcplabels", "withlabels1", "withlabels2", "withonelabelmissing", "powpow") + s.composeStop("simple", "withtcplabels", "withlabels1", "withlabels2", "withonelabelmissing", "powpow", "nonRunning") } func (s *DockerSuite) TestSimpleConfiguration() { @@ -56,56 +55,6 @@ func (s *DockerSuite) TestSimpleConfiguration() { require.NoError(s.T(), err) } -func (s *DockerSuite) TestWRRServer() { - tempObjects := struct { - DockerHost string - DefaultRule string - }{ - DockerHost: s.getDockerHost(), - DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)", - } - - file := s.adaptFile("fixtures/docker/simple.toml", tempObjects) - - s.composeUp() - - s.traefikCmd(withConfigFile(file)) - - whoami1IP := s.getComposeServiceIP("wrr-server") - whoami2IP := s.getComposeServiceIP("wrr-server2") - - // Expected a 404 as we did not configure anything - err := try.GetRequest("http://127.0.0.1:8000/", 500*time.Millisecond, try.StatusCodeIs(http.StatusNotFound)) - require.NoError(s.T(), err) - - err = try.GetRequest("http://127.0.0.1:8080/api/http/services", 1000*time.Millisecond, try.BodyContains("wrr-server")) - require.NoError(s.T(), err) - - repartition := map[string]int{} - for range 4 { - req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil) - req.Host = "my.wrr.host" - require.NoError(s.T(), err) - - response, err := http.DefaultClient.Do(req) - require.NoError(s.T(), err) - assert.Equal(s.T(), http.StatusOK, response.StatusCode) - - body, err := io.ReadAll(response.Body) - require.NoError(s.T(), err) - - if strings.Contains(string(body), whoami1IP) { - repartition[whoami1IP]++ - } - if strings.Contains(string(body), whoami2IP) { - repartition[whoami2IP]++ - } - } - - assert.Equal(s.T(), 3, repartition[whoami1IP]) - assert.Equal(s.T(), 1, repartition[whoami2IP]) -} - func (s *DockerSuite) TestDefaultDockerContainers() { tempObjects := struct { DockerHost string @@ -273,3 +222,59 @@ func (s *DockerSuite) TestRestartDockerContainers() { err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("powpow")) require.NoError(s.T(), err) } + +func (s *DockerSuite) TestDockerAllowNonRunning() { + tempObjects := struct { + DockerHost string + DefaultRule string + }{ + DockerHost: s.getDockerHost(), + DefaultRule: "Host(`{{ normalize .Name }}.docker.localhost`)", + } + + file := s.adaptFile("fixtures/docker/simple.toml", tempObjects) + + s.composeUp("nonRunning") + + // Start traefik + s.traefikCmd(withConfigFile(file)) + + // Verify the container is working when running + req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil) + require.NoError(s.T(), err) + req.Host = "non.running.host" + + resp, err := try.ResponseUntilStatusCode(req, 3*time.Second, http.StatusOK) + require.NoError(s.T(), err) + + body, err := io.ReadAll(resp.Body) + require.NoError(s.T(), err) + assert.Contains(s.T(), string(body), "Hostname:") + + // Verify the router exists in Traefik configuration + err = try.GetRequest("http://127.0.0.1:8080/api/http/routers", 1*time.Second, try.BodyContains("NonRunning")) + require.NoError(s.T(), err) + + // Stop the container + s.composeStop("nonRunning") + + // Wait a bit for container stop to be detected + time.Sleep(2 * time.Second) + + // Verify the router still exists in configuration even though container is stopped + // This is the key test - the router should persist due to allowNonRunning=true + err = try.GetRequest("http://127.0.0.1:8080/api/http/routers", 10*time.Second, try.BodyContains("NonRunning")) + require.NoError(s.T(), err) + + // Verify the service still exists in configuration + err = try.GetRequest("http://127.0.0.1:8080/api/http/services", 1*time.Second, try.BodyContains("nonRunning")) + require.NoError(s.T(), err) + + // HTTP requests should fail (502 Bad Gateway) since container is stopped but router exists + req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/", nil) + require.NoError(s.T(), err) + req.Host = "non.running.host" + + err = try.Request(req, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable)) + require.NoError(s.T(), err) +} diff --git a/integration/fixtures/k8s-conformance/00-experimental-v1.3.0.yml b/integration/fixtures/gateway-api-conformance/00-experimental-v1.4.0.yml similarity index 81% rename from integration/fixtures/k8s-conformance/00-experimental-v1.3.0.yml rename to integration/fixtures/gateway-api-conformance/00-experimental-v1.4.0.yml index 75725cc0f..b1e7bd2f2 100644 --- a/integration/fixtures/k8s-conformance/00-experimental-v1.3.0.yml +++ b/integration/fixtures/gateway-api-conformance/00-experimental-v1.4.0.yml @@ -24,9 +24,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null labels: gateway.networking.k8s.io/policy: Direct name: backendtlspolicies.gateway.networking.k8s.io @@ -47,7 +46,7 @@ spec: - jsonPath: .metadata.creationTimestamp name: Age type: date - name: v1alpha3 + name: v1 schema: openAPIV3Schema: description: |- @@ -114,6 +113,22 @@ spec: be unique across all targetRef entries in the BackendTLSPolicy. * They select different sectionNames in the same target. + When more than one BackendTLSPolicy selects the same target and + sectionName, implementations MUST determine precedence using the + following criteria, continuing on ties: + + * The older policy by creation timestamp takes precedence. For + example, a policy with a creation timestamp of "2021-07-15 + 01:02:03" MUST be given precedence over a policy with a + creation timestamp of "2021-07-15 01:02:04". + * The policy appearing first in alphabetical order by {name}. + For example, a policy named `bar` is given precedence over a + policy named `baz`. + + For any BackendTLSPolicy that does not take precedence, the + implementation MUST ensure the `Accepted` Condition is set to + `status: False`, with Reason `Conflicted`. + Support: Extended for Kubernetes Service Support: Implementation-specific for any other resource @@ -170,6 +185,7 @@ spec: maxItems: 16 minItems: 1 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName must be specified when targetRefs includes 2 or more references to the same target @@ -198,8 +214,31 @@ spec: not both. If CACertificateRefs is empty or unspecified, the configuration for WellKnownCACertificates MUST be honored instead if supported by the implementation. - References to a resource in a different namespace are invalid for the - moment, although we will revisit this in the future. + A CACertificateRef is invalid if: + + * It refers to a resource that cannot be resolved (e.g., the referenced resource + does not exist) or is misconfigured (e.g., a ConfigMap does not contain a key + named `ca.crt`). In this case, the Reason must be set to `InvalidCACertificateRef` + and the Message of the Condition must indicate which reference is invalid and why. + + * It refers to an unknown or unsupported kind of resource. In this case, the Reason + must be set to `InvalidKind` and the Message of the Condition must explain which + kind of resource is unknown or unsupported. + + * It refers to a resource in another namespace. This may change in future + spec updates. + + Implementations MAY choose to perform further validation of the certificate + content (e.g., checking expiry or enforcing specific formats). In such cases, + an implementation-specific Reason and Message must be set for the invalid reference. + + In all cases, the implementation MUST ensure the `ResolvedRefs` Condition on + the BackendTLSPolicy is set to `status: False`, with a Reason and Message + that indicate the cause of the error. Connections using an invalid + CACertificateRef MUST fail, and the client MUST receive an HTTP 5xx error + response. If ALL CACertificateRefs are invalid, the implementation MUST also + ensure the `Accepted` Condition on the BackendTLSPolicy is set to + `status: False`, with a Reason `NoValidCACertificate`. A single CACertificateRef to a Kubernetes ConfigMap kind has "Core" support. Implementations MAY choose to support attaching multiple certificates to @@ -208,8 +247,8 @@ spec: Support: Core - An optional single reference to a Kubernetes ConfigMap, with the CA certificate in a key named `ca.crt`. - Support: Implementation-specific (More than one reference, or other kinds - of resources). + Support: Implementation-specific - More than one reference, other kinds + of resources, or a single reference that includes multiple certificates. items: description: |- LocalObjectReference identifies an API object within the namespace of the @@ -247,15 +286,18 @@ spec: type: object maxItems: 8 type: array + x-kubernetes-list-type: atomic hostname: description: |- Hostname is used for two purposes in the connection between Gateways and backends: 1. Hostname MUST be used as the SNI to connect to the backend (RFC 6066). - 2. Hostname MUST be used for authentication and MUST match the certificate served by the matching backend, unless SubjectAltNames is specified. - authentication and MUST match the certificate served by the matching - backend. + 2. Hostname MUST be used for authentication and MUST match the certificate + served by the matching backend, unless SubjectAltNames is specified. + 3. If SubjectAltNames are specified, Hostname can be used for certificate selection + but MUST NOT be used for authentication. If you want to use the value + of the Hostname field for authentication, you MUST add it to the SubjectAltNames list. Support: Core maxLength: 253 @@ -325,6 +367,7 @@ spec: "")' maxItems: 5 type: array + x-kubernetes-list-type: atomic wellKnownCACertificates: description: |- WellKnownCACertificates specifies whether system CA certificates may be used in @@ -332,10 +375,11 @@ spec: If WellKnownCACertificates is unspecified or empty (""), then CACertificateRefs must be specified with at least one entry for a valid configuration. Only one of - CACertificateRefs or WellKnownCACertificates may be specified, not both. If an - implementation does not support the WellKnownCACertificates field or the value - supplied is not supported, the Status Conditions on the Policy MUST be - updated to include an Accepted: False Condition with Reason: Invalid. + CACertificateRefs or WellKnownCACertificates may be specified, not both. + If an implementation does not support the WellKnownCACertificates field, or + the supplied value is not recognized, the implementation MUST ensure the + `Accepted` Condition on the BackendTLSPolicy is set to `status: False`, with + a Reason `Invalid`. Support: Implementation-specific enum: @@ -646,10 +690,12 @@ spec: type: string required: - ancestorRef + - conditions - controllerName type: object maxItems: 16 type: array + x-kubernetes-list-type: atomic required: - ancestors type: object @@ -660,6 +706,667 @@ spec: storage: true subresources: status: {} + - deprecated: true + deprecationWarning: The v1alpha3 version of BackendTLSPolicy has been deprecated + and will be removed in a future release of the API. Please upgrade to v1. + name: v1alpha3 + schema: + openAPIV3Schema: + description: |- + BackendTLSPolicy provides a way to configure how a Gateway + connects to a Backend via TLS. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of BackendTLSPolicy. + properties: + options: + additionalProperties: + description: |- + AnnotationValue is the value of an annotation in Gateway API. This is used + for validation of maps such as TLS options. This roughly matches Kubernetes + annotation validation, although the length validation in that case is based + on the entire size of the annotations struct. + maxLength: 4096 + minLength: 0 + type: string + description: |- + Options are a list of key/value pairs to enable extended TLS + configuration for each implementation. For example, configuring the + minimum TLS version or supported cipher suites. + + A set of common keys MAY be defined by the API in the future. To avoid + any ambiguity, implementation-specific definitions MUST use + domain-prefixed names, such as `example.com/my-custom-option`. + Un-prefixed names are reserved for key names defined by Gateway API. + + Support: Implementation-specific + maxProperties: 16 + type: object + targetRefs: + description: |- + TargetRefs identifies an API object to apply the policy to. + Only Services have Extended support. Implementations MAY support + additional objects, with Implementation Specific support. + Note that this config applies to the entire referenced resource + by default, but this default may change in the future to provide + a more granular application of the policy. + + TargetRefs must be _distinct_. This means either that: + + * They select different targets. If this is the case, then targetRef + entries are distinct. In terms of fields, this means that the + multi-part key defined by `group`, `kind`, and `name` must + be unique across all targetRef entries in the BackendTLSPolicy. + * They select different sectionNames in the same target. + + When more than one BackendTLSPolicy selects the same target and + sectionName, implementations MUST determine precedence using the + following criteria, continuing on ties: + + * The older policy by creation timestamp takes precedence. For + example, a policy with a creation timestamp of "2021-07-15 + 01:02:03" MUST be given precedence over a policy with a + creation timestamp of "2021-07-15 01:02:04". + * The policy appearing first in alphabetical order by {name}. + For example, a policy named `bar` is given precedence over a + policy named `baz`. + + For any BackendTLSPolicy that does not take precedence, the + implementation MUST ensure the `Accepted` Condition is set to + `status: False`, with Reason `Conflicted`. + + Support: Extended for Kubernetes Service + + Support: Implementation-specific for any other resource + items: + description: |- + LocalPolicyTargetReferenceWithSectionName identifies an API object to apply a + direct policy to. This should be used as part of Policy resources that can + target single resources. For more information on how this policy attachment + mode works, and a sample Policy resource, refer to the policy attachment + documentation for Gateway API. + + Note: This should only be used for direct policy attachment when references + to SectionName are actually needed. In all other cases, + LocalPolicyTargetReference should be used. + properties: + group: + description: Group is the group of the target resource. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the target resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the target resource. + maxLength: 253 + minLength: 1 + type: string + sectionName: + description: |- + SectionName is the name of a section within the target resource. When + unspecified, this targetRef targets the entire resource. In the following + resources, SectionName is interpreted as the following: + + * Gateway: Listener name + * HTTPRoute: HTTPRouteRule name + * Service: Port name + + If a SectionName is specified, but does not exist on the targeted object, + the Policy must fail to attach, and the policy implementation should record + a `ResolvedRefs` or similar Condition in the Policy's status. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - group + - kind + - name + type: object + maxItems: 16 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + x-kubernetes-validations: + - message: sectionName must be specified when targetRefs includes + 2 or more references to the same target + rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name ? ((!has(p1.sectionName) || p1.sectionName + == '''') == (!has(p2.sectionName) || p2.sectionName == '''')) + : true))' + - message: sectionName must be unique when targetRefs includes 2 or + more references to the same target + rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name && (((!has(p1.sectionName) || + p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName + == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName + == p2.sectionName)))) + validation: + description: Validation contains backend TLS validation configuration. + properties: + caCertificateRefs: + description: |- + CACertificateRefs contains one or more references to Kubernetes objects that + contain a PEM-encoded TLS CA certificate bundle, which is used to + validate a TLS handshake between the Gateway and backend Pod. + + If CACertificateRefs is empty or unspecified, then WellKnownCACertificates must be + specified. Only one of CACertificateRefs or WellKnownCACertificates may be specified, + not both. If CACertificateRefs is empty or unspecified, the configuration for + WellKnownCACertificates MUST be honored instead if supported by the implementation. + + A CACertificateRef is invalid if: + + * It refers to a resource that cannot be resolved (e.g., the referenced resource + does not exist) or is misconfigured (e.g., a ConfigMap does not contain a key + named `ca.crt`). In this case, the Reason must be set to `InvalidCACertificateRef` + and the Message of the Condition must indicate which reference is invalid and why. + + * It refers to an unknown or unsupported kind of resource. In this case, the Reason + must be set to `InvalidKind` and the Message of the Condition must explain which + kind of resource is unknown or unsupported. + + * It refers to a resource in another namespace. This may change in future + spec updates. + + Implementations MAY choose to perform further validation of the certificate + content (e.g., checking expiry or enforcing specific formats). In such cases, + an implementation-specific Reason and Message must be set for the invalid reference. + + In all cases, the implementation MUST ensure the `ResolvedRefs` Condition on + the BackendTLSPolicy is set to `status: False`, with a Reason and Message + that indicate the cause of the error. Connections using an invalid + CACertificateRef MUST fail, and the client MUST receive an HTTP 5xx error + response. If ALL CACertificateRefs are invalid, the implementation MUST also + ensure the `Accepted` Condition on the BackendTLSPolicy is set to + `status: False`, with a Reason `NoValidCACertificate`. + + A single CACertificateRef to a Kubernetes ConfigMap kind has "Core" support. + Implementations MAY choose to support attaching multiple certificates to + a backend, but this behavior is implementation-specific. + + Support: Core - An optional single reference to a Kubernetes ConfigMap, + with the CA certificate in a key named `ca.crt`. + + Support: Implementation-specific - More than one reference, other kinds + of resources, or a single reference that includes multiple certificates. + items: + description: |- + LocalObjectReference identifies an API object within the namespace of the + referrer. + 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. + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example "HTTPRoute" + or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + maxItems: 8 + type: array + x-kubernetes-list-type: atomic + hostname: + description: |- + Hostname is used for two purposes in the connection between Gateways and + backends: + + 1. Hostname MUST be used as the SNI to connect to the backend (RFC 6066). + 2. Hostname MUST be used for authentication and MUST match the certificate + served by the matching backend, unless SubjectAltNames is specified. + 3. If SubjectAltNames are specified, Hostname can be used for certificate selection + but MUST NOT be used for authentication. If you want to use the value + of the Hostname field for authentication, you MUST add it to the SubjectAltNames list. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + subjectAltNames: + description: |- + SubjectAltNames contains one or more Subject Alternative Names. + When specified the certificate served from the backend MUST + have at least one Subject Alternate Name matching one of the specified SubjectAltNames. + + Support: Extended + items: + description: SubjectAltName represents Subject Alternative Name. + properties: + hostname: + description: |- + Hostname contains Subject Alternative Name specified in DNS name format. + Required when Type is set to Hostname, ignored otherwise. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + type: + description: |- + Type determines the format of the Subject Alternative Name. Always required. + + Support: Core + enum: + - Hostname + - URI + type: string + uri: + description: |- + URI contains Subject Alternative Name specified in a full URI format. + It MUST include both a scheme (e.g., "http" or "ftp") and a scheme-specific-part. + Common values include SPIFFE IDs like "spiffe://mycluster.example.com/ns/myns/sa/svc1sa". + Required when Type is set to URI, ignored otherwise. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))? + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: SubjectAltName element must contain Hostname, if + Type is set to Hostname + rule: '!(self.type == "Hostname" && (!has(self.hostname) || + self.hostname == ""))' + - message: SubjectAltName element must not contain Hostname, + if Type is not set to Hostname + rule: '!(self.type != "Hostname" && has(self.hostname) && + self.hostname != "")' + - message: SubjectAltName element must contain URI, if Type + is set to URI + rule: '!(self.type == "URI" && (!has(self.uri) || self.uri + == ""))' + - message: SubjectAltName element must not contain URI, if Type + is not set to URI + rule: '!(self.type != "URI" && has(self.uri) && self.uri != + "")' + maxItems: 5 + type: array + x-kubernetes-list-type: atomic + wellKnownCACertificates: + description: |- + WellKnownCACertificates specifies whether system CA certificates may be used in + the TLS handshake between the gateway and backend pod. + + If WellKnownCACertificates is unspecified or empty (""), then CACertificateRefs + must be specified with at least one entry for a valid configuration. Only one of + CACertificateRefs or WellKnownCACertificates may be specified, not both. + If an implementation does not support the WellKnownCACertificates field, or + the supplied value is not recognized, the implementation MUST ensure the + `Accepted` Condition on the BackendTLSPolicy is set to `status: False`, with + a Reason `Invalid`. + + Support: Implementation-specific + enum: + - System + type: string + required: + - hostname + type: object + x-kubernetes-validations: + - message: must not contain both CACertificateRefs and WellKnownCACertificates + rule: '!(has(self.caCertificateRefs) && size(self.caCertificateRefs) + > 0 && has(self.wellKnownCACertificates) && self.wellKnownCACertificates + != "")' + - message: must specify either CACertificateRefs or WellKnownCACertificates + rule: (has(self.caCertificateRefs) && size(self.caCertificateRefs) + > 0 || has(self.wellKnownCACertificates) && self.wellKnownCACertificates + != "") + required: + - targetRefs + - validation + type: object + status: + description: Status defines the current state of BackendTLSPolicy. + properties: + ancestors: + description: |- + Ancestors is a list of ancestor resources (usually Gateways) that are + associated with the policy, and the status of the policy with respect to + each ancestor. When this policy attaches to a parent, the controller that + manages the parent and the ancestors MUST add an entry to this list when + the controller first sees the policy and SHOULD update the entry as + appropriate when the relevant ancestor is modified. + + Note that choosing the relevant ancestor is left to the Policy designers; + an important part of Policy design is designing the right object level at + which to namespace this status. + + Note also that implementations MUST ONLY populate ancestor status for + the Ancestor resources they are responsible for. Implementations MUST + use the ControllerName field to uniquely identify the entries in this list + that they are responsible for. + + Note that to achieve this, the list of PolicyAncestorStatus structs + MUST be treated as a map with a composite key, made up of the AncestorRef + and ControllerName fields combined. + + A maximum of 16 ancestors will be represented in this list. An empty list + means the Policy is not relevant for any ancestors. + + If this slice is full, implementations MUST NOT add further entries. + Instead they MUST consider the policy unimplementable and signal that + on any related resources such as the ancestor that would be referenced + here. For example, if this list was full on BackendTLSPolicy, no + additional Gateways would be able to reference the Service targeted by + the BackendTLSPolicy. + items: + description: |- + PolicyAncestorStatus describes the status of a route with respect to an + associated Ancestor. + + Ancestors refer to objects that are either the Target of a policy or above it + in terms of object hierarchy. For example, if a policy targets a Service, the + Policy's Ancestors are, in order, the Service, the HTTPRoute, the Gateway, and + the GatewayClass. Almost always, in this hierarchy, the Gateway will be the most + useful object to place Policy status on, so we recommend that implementations + SHOULD use Gateway as the PolicyAncestorStatus object unless the designers + have a _very_ good reason otherwise. + + In the context of policy attachment, the Ancestor is used to distinguish which + resource results in a distinct application of this policy. For example, if a policy + targets a Service, it may have a distinct result per attached Gateway. + + Policies targeting the same resource may have different effects depending on the + ancestors of those resources. For example, different Gateways targeting the same + Service may have different capabilities, especially if they have different underlying + implementations. + + For example, in BackendTLSPolicy, the Policy attaches to a Service that is + used as a backend in a HTTPRoute that is itself attached to a Gateway. + In this case, the relevant object for status is the Gateway, and that is the + ancestor object referred to in this status. + + Note that a parent is also an ancestor, so for objects where the parent is the + relevant object for status, this struct SHOULD still be used. + + This struct is intended to be used in a slice that's effectively a map, + with a composite key made up of the AncestorRef and the ControllerName. + properties: + ancestorRef: + description: |- + AncestorRef corresponds with a ParentRef in the spec that this + PolicyAncestorStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + When the parent resource is a Service, this targets a specific port in the + Service spec. When both Port (experimental) and SectionName are specified, + the name and port of the selected port must match both specified values. + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + 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 + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + 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 + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + conditions: + description: Conditions describes the status of the Policy with + respect to the given Ancestor. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: |- + ControllerName is a domain/path string that indicates the name of the + controller that wrote this status. This corresponds with the + controllerName field on GatewayClass. + + Example: "example.net/gateway-controller". + + The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are + valid Kubernetes names + (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + + 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. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + required: + - ancestorRef + - conditions + - controllerName + type: object + maxItems: 16 + type: array + x-kubernetes-list-type: atomic + required: + - ancestors + type: object + required: + - spec + type: object + served: true + storage: false status: acceptedNames: kind: "" @@ -675,9 +1382,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null name: gatewayclasses.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -1195,9 +1901,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null name: gateways.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -1257,7 +1962,7 @@ spec: Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST - indicate this in the associated entry in GatewayStatus.Addresses. + indicate this in an associated entry in GatewayStatus.Conditions. The Addresses field represents a request for the address(es) on the "outside of the Gateway", that traffic bound for this Gateway will use. @@ -1312,19 +2017,22 @@ spec: type: string type: object x-kubernetes-validations: - - message: Hostname value must only contain valid characters (matching - ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) - rule: 'self.type == ''Hostname'' ? self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"""): + - message: Hostname value must be empty or contain only valid characters + (matching ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) + rule: 'self.type == ''Hostname'' ? (!has(self.value) || self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$""")): true' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: IPAddress values must be unique - rule: 'self.all(a1, a1.type == ''IPAddress'' ? self.exists_one(a2, - a2.type == a1.type && a2.value == a1.value) : true )' + rule: 'self.all(a1, a1.type == ''IPAddress'' && has(a1.value) ? + self.exists_one(a2, a2.type == a1.type && has(a2.value) && a2.value + == a1.value) : true )' - message: Hostname values must be unique - rule: 'self.all(a1, a1.type == ''Hostname'' ? self.exists_one(a2, - a2.type == a1.type && a2.value == a1.value) : true )' + rule: 'self.all(a1, a1.type == ''Hostname'' && has(a1.value) ? + self.exists_one(a2, a2.type == a1.type && has(a2.value) && a2.value + == a1.value) : true )' allowedListeners: description: |- AllowedListeners defines which ListenerSets can be attached to this Gateway. @@ -1406,70 +2114,29 @@ spec: x-kubernetes-map-type: atomic type: object type: object - backendTLS: + defaultScope: description: |- - BackendTLS configures TLS settings for when this Gateway is connecting to - backends with TLS. + DefaultScope, when set, configures the Gateway as a default Gateway, + meaning it will dynamically and implicitly have Routes (e.g. HTTPRoute) + attached to it, according to the scope configured here. - Support: Core - properties: - clientCertificateRef: - description: |- - ClientCertificateRef is a reference to an object that contains a Client - Certificate and the associated private key. - - References to a resource in different namespace are invalid UNLESS there - is a ReferenceGrant in the target namespace that allows the certificate - to be attached. If a ReferenceGrant does not allow this reference, the - "ResolvedRefs" condition MUST be set to False for this listener with the - "RefNotPermitted" reason. - - ClientCertificateRef can reference to standard Kubernetes resources, i.e. - Secret, or implementation-specific custom resources. - - This setting can be overridden on the service level by use of BackendTLSPolicy. - - Support: Core - properties: - group: - default: "" - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Secret - description: Kind is kind of the referent. For example "Secret". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - 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 - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - name - type: object - type: object + If unset (the default) or set to None, the Gateway will not act as a + default Gateway; if set, the Gateway will claim any Route with a + matching scope set in its UseDefaultGateway field, subject to the usual + rules about which routes the Gateway can attach to. + + Think carefully before using this functionality! While the normal rules + about which Route can apply are still enforced, it is simply easier for + the wrong Route to be accidentally attached to this Gateway in this + configuration. If the Gateway operator is not also the operator in + control of the scope (e.g. namespace) with tight controls and checks on + what kind of workloads and Routes get added in that scope, we strongly + recommend not using this just because it seems convenient, and instead + stick to direct Route attachment. + enum: + - All + - None + type: string gatewayClassName: description: |- GatewayClassName used for this Gateway. This is the name of a @@ -1825,6 +2492,7 @@ spec: type: object maxItems: 8 type: array + x-kubernetes-list-type: atomic namespaces: default: from: Same @@ -1992,7 +2660,7 @@ spec: the Protocol field is "HTTPS" or "TLS". It is invalid to set this field if the Protocol field is "HTTP", "TCP", or "UDP". - The association of SNIs to Certificate defined in GatewayTLSConfig is + The association of SNIs to Certificate defined in ListenerTLSConfig is defined based on the Hostname field for this listener. The GatewayClass MUST use the longest matching SNI out of all @@ -2079,93 +2747,7 @@ spec: type: object maxItems: 64 type: array - frontendValidation: - description: |- - FrontendValidation holds configuration information for validating the frontend (client). - Setting this field will require clients to send a client certificate - required for validation during the TLS handshake. In browsers this may result in a dialog appearing - that requests a user to specify the client certificate. - The maximum depth of a certificate chain accepted in verification is Implementation specific. - - Support: Extended - properties: - caCertificateRefs: - description: |- - CACertificateRefs contains one or more references to - Kubernetes objects that contain TLS certificates of - the Certificate Authorities that can be used - as a trust anchor to validate the certificates presented by the client. - - A single CA certificate reference to a Kubernetes ConfigMap - has "Core" support. - Implementations MAY choose to support attaching multiple CA certificates to - a Listener, but this behavior is implementation-specific. - - Support: Core - A single reference to a Kubernetes ConfigMap - with the CA certificate in a key named `ca.crt`. - - Support: Implementation-specific (More than one reference, or other kinds - of resources). - - References to a resource in a different namespace are invalid UNLESS there - is a ReferenceGrant in the target namespace that allows the certificate - to be attached. If a ReferenceGrant does not allow this reference, the - "ResolvedRefs" condition MUST be set to False for this listener with the - "RefNotPermitted" reason. - items: - description: |- - 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. - properties: - group: - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When set to the empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - description: Kind is kind of the referent. For - example "ConfigMap" or "Service". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - 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 - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - group - - kind - - name - type: object - maxItems: 8 - minItems: 1 - type: array - type: object + x-kubernetes-list-type: atomic mode: default: Terminate description: |- @@ -2244,6 +2826,366 @@ spec: rule: 'self.all(l1, self.exists_one(l2, l1.port == l2.port && l1.protocol == l2.protocol && (has(l1.hostname) && has(l2.hostname) ? l1.hostname == l2.hostname : !has(l1.hostname) && !has(l2.hostname))))' + tls: + description: |- + TLS specifies frontend and backend tls configuration for entire gateway. + + Support: Extended + properties: + backend: + description: |- + Backend describes TLS configuration for gateway when connecting + to backends. + + Note that this contains only details for the Gateway as a TLS client, + and does _not_ imply behavior about how to choose which backend should + get a TLS connection. That is determined by the presence of a BackendTLSPolicy. + + Support: Core + properties: + clientCertificateRef: + description: |- + ClientCertificateRef is a reference to an object that contains a Client + Certificate and the associated private key. + + References to a resource in different namespace are invalid UNLESS there + is a ReferenceGrant in the target namespace that allows the certificate + to be attached. If a ReferenceGrant does not allow this reference, the + "ResolvedRefs" condition MUST be set to False for this listener with the + "RefNotPermitted" reason. + + ClientCertificateRef can reference to standard Kubernetes resources, i.e. + Secret, or implementation-specific custom resources. + + Support: Core + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Secret + description: Kind is kind of the referent. For example + "Secret". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - name + type: object + type: object + frontend: + description: |- + Frontend describes TLS config when client connects to Gateway. + Support: Core + properties: + default: + description: |- + Default specifies the default client certificate validation configuration + for all Listeners handling HTTPS traffic, unless a per-port configuration + is defined. + + support: Core + properties: + validation: + description: |- + Validation holds configuration information for validating the frontend (client). + Setting this field will result in mutual authentication when connecting to the gateway. + In browsers this may result in a dialog appearing + that requests a user to specify the client certificate. + The maximum depth of a certificate chain accepted in verification is Implementation specific. + + Support: Core + properties: + caCertificateRefs: + description: |- + CACertificateRefs contains one or more references to + Kubernetes objects that contain TLS certificates of + the Certificate Authorities that can be used + as a trust anchor to validate the certificates presented by the client. + + A single CA certificate reference to a Kubernetes ConfigMap + has "Core" support. + Implementations MAY choose to support attaching multiple CA certificates to + a Listener, but this behavior is implementation-specific. + + Support: Core - A single reference to a Kubernetes ConfigMap + with the CA certificate in a key named `ca.crt`. + + Support: Implementation-specific (More than one certificate in a ConfigMap + with different keys or more than one reference, or other kinds of resources). + + References to a resource in a different namespace are invalid UNLESS there + is a ReferenceGrant in the target namespace that allows the certificate + to be attached. If a ReferenceGrant does not allow this reference, the + "ResolvedRefs" condition MUST be set to False for this listener with the + "RefNotPermitted" reason. + items: + description: |- + 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. + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When set to the empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For + example "ConfigMap" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + mode: + default: AllowValidOnly + description: |- + FrontendValidationMode defines the mode for validating the client certificate. + There are two possible modes: + + - AllowValidOnly: In this mode, the gateway will accept connections only if + the client presents a valid certificate. This certificate must successfully + pass validation against the CA certificates specified in `CACertificateRefs`. + - AllowInsecureFallback: In this mode, the gateway will accept connections + even if the client certificate is not presented or fails verification. + + This approach delegates client authorization to the backend and introduce + a significant security risk. It should be used in testing environments or + on a temporary basis in non-testing environments. + + Defaults to AllowValidOnly. + + Support: Core + enum: + - AllowValidOnly + - AllowInsecureFallback + type: string + required: + - caCertificateRefs + type: object + type: object + perPort: + description: |- + PerPort specifies tls configuration assigned per port. + Per port configuration is optional. Once set this configuration overrides + the default configuration for all Listeners handling HTTPS traffic + that match this port. + Each override port requires a unique TLS configuration. + + support: Core + items: + properties: + port: + description: |- + The Port indicates the Port Number to which the TLS configuration will be + applied. This configuration will be applied to all Listeners handling HTTPS + traffic that match this port. + + Support: Core + format: int32 + maximum: 65535 + minimum: 1 + type: integer + tls: + description: |- + TLS store the configuration that will be applied to all Listeners handling + HTTPS traffic and matching given port. + + Support: Core + properties: + validation: + description: |- + Validation holds configuration information for validating the frontend (client). + Setting this field will result in mutual authentication when connecting to the gateway. + In browsers this may result in a dialog appearing + that requests a user to specify the client certificate. + The maximum depth of a certificate chain accepted in verification is Implementation specific. + + Support: Core + properties: + caCertificateRefs: + description: |- + CACertificateRefs contains one or more references to + Kubernetes objects that contain TLS certificates of + the Certificate Authorities that can be used + as a trust anchor to validate the certificates presented by the client. + + A single CA certificate reference to a Kubernetes ConfigMap + has "Core" support. + Implementations MAY choose to support attaching multiple CA certificates to + a Listener, but this behavior is implementation-specific. + + Support: Core - A single reference to a Kubernetes ConfigMap + with the CA certificate in a key named `ca.crt`. + + Support: Implementation-specific (More than one certificate in a ConfigMap + with different keys or more than one reference, or other kinds of resources). + + References to a resource in a different namespace are invalid UNLESS there + is a ReferenceGrant in the target namespace that allows the certificate + to be attached. If a ReferenceGrant does not allow this reference, the + "ResolvedRefs" condition MUST be set to False for this listener with the + "RefNotPermitted" reason. + items: + description: |- + 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. + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When set to the empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. + For example "ConfigMap" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + mode: + default: AllowValidOnly + description: |- + FrontendValidationMode defines the mode for validating the client certificate. + There are two possible modes: + + - AllowValidOnly: In this mode, the gateway will accept connections only if + the client presents a valid certificate. This certificate must successfully + pass validation against the CA certificates specified in `CACertificateRefs`. + - AllowInsecureFallback: In this mode, the gateway will accept connections + even if the client certificate is not presented or fails verification. + + This approach delegates client authorization to the backend and introduce + a significant security risk. It should be used in testing environments or + on a temporary basis in non-testing environments. + + Defaults to AllowValidOnly. + + Support: Core + enum: + - AllowValidOnly + - AllowInsecureFallback + type: string + required: + - caCertificateRefs + type: object + type: object + required: + - port + - tls + type: object + maxItems: 64 + type: array + x-kubernetes-list-map-keys: + - port + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: Port for TLS configuration must be unique within + the Gateway + rule: self.all(t1, self.exists_one(t2, t1.port == t2.port)) + required: + - default + type: object + type: object required: - gatewayClassName - listeners @@ -2318,6 +3260,7 @@ spec: true' maxItems: 16 type: array + x-kubernetes-list-type: atomic conditions: default: - lastTransitionTime: "1970-01-01T00:00:00Z" @@ -2531,6 +3474,7 @@ spec: type: object maxItems: 8 type: array + x-kubernetes-list-type: atomic required: - attachedRoutes - conditions @@ -2595,7 +3539,7 @@ spec: Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST - indicate this in the associated entry in GatewayStatus.Addresses. + indicate this in an associated entry in GatewayStatus.Conditions. The Addresses field represents a request for the address(es) on the "outside of the Gateway", that traffic bound for this Gateway will use. @@ -2650,19 +3594,22 @@ spec: type: string type: object x-kubernetes-validations: - - message: Hostname value must only contain valid characters (matching - ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) - rule: 'self.type == ''Hostname'' ? self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"""): + - message: Hostname value must be empty or contain only valid characters + (matching ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) + rule: 'self.type == ''Hostname'' ? (!has(self.value) || self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$""")): true' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: IPAddress values must be unique - rule: 'self.all(a1, a1.type == ''IPAddress'' ? self.exists_one(a2, - a2.type == a1.type && a2.value == a1.value) : true )' + rule: 'self.all(a1, a1.type == ''IPAddress'' && has(a1.value) ? + self.exists_one(a2, a2.type == a1.type && has(a2.value) && a2.value + == a1.value) : true )' - message: Hostname values must be unique - rule: 'self.all(a1, a1.type == ''Hostname'' ? self.exists_one(a2, - a2.type == a1.type && a2.value == a1.value) : true )' + rule: 'self.all(a1, a1.type == ''Hostname'' && has(a1.value) ? + self.exists_one(a2, a2.type == a1.type && has(a2.value) && a2.value + == a1.value) : true )' allowedListeners: description: |- AllowedListeners defines which ListenerSets can be attached to this Gateway. @@ -2744,70 +3691,29 @@ spec: x-kubernetes-map-type: atomic type: object type: object - backendTLS: + defaultScope: description: |- - BackendTLS configures TLS settings for when this Gateway is connecting to - backends with TLS. + DefaultScope, when set, configures the Gateway as a default Gateway, + meaning it will dynamically and implicitly have Routes (e.g. HTTPRoute) + attached to it, according to the scope configured here. - Support: Core - properties: - clientCertificateRef: - description: |- - ClientCertificateRef is a reference to an object that contains a Client - Certificate and the associated private key. - - References to a resource in different namespace are invalid UNLESS there - is a ReferenceGrant in the target namespace that allows the certificate - to be attached. If a ReferenceGrant does not allow this reference, the - "ResolvedRefs" condition MUST be set to False for this listener with the - "RefNotPermitted" reason. - - ClientCertificateRef can reference to standard Kubernetes resources, i.e. - Secret, or implementation-specific custom resources. - - This setting can be overridden on the service level by use of BackendTLSPolicy. - - Support: Core - properties: - group: - default: "" - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Secret - description: Kind is kind of the referent. For example "Secret". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - 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 - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - name - type: object - type: object + If unset (the default) or set to None, the Gateway will not act as a + default Gateway; if set, the Gateway will claim any Route with a + matching scope set in its UseDefaultGateway field, subject to the usual + rules about which routes the Gateway can attach to. + + Think carefully before using this functionality! While the normal rules + about which Route can apply are still enforced, it is simply easier for + the wrong Route to be accidentally attached to this Gateway in this + configuration. If the Gateway operator is not also the operator in + control of the scope (e.g. namespace) with tight controls and checks on + what kind of workloads and Routes get added in that scope, we strongly + recommend not using this just because it seems convenient, and instead + stick to direct Route attachment. + enum: + - All + - None + type: string gatewayClassName: description: |- GatewayClassName used for this Gateway. This is the name of a @@ -3163,6 +4069,7 @@ spec: type: object maxItems: 8 type: array + x-kubernetes-list-type: atomic namespaces: default: from: Same @@ -3330,7 +4237,7 @@ spec: the Protocol field is "HTTPS" or "TLS". It is invalid to set this field if the Protocol field is "HTTP", "TCP", or "UDP". - The association of SNIs to Certificate defined in GatewayTLSConfig is + The association of SNIs to Certificate defined in ListenerTLSConfig is defined based on the Hostname field for this listener. The GatewayClass MUST use the longest matching SNI out of all @@ -3417,93 +4324,7 @@ spec: type: object maxItems: 64 type: array - frontendValidation: - description: |- - FrontendValidation holds configuration information for validating the frontend (client). - Setting this field will require clients to send a client certificate - required for validation during the TLS handshake. In browsers this may result in a dialog appearing - that requests a user to specify the client certificate. - The maximum depth of a certificate chain accepted in verification is Implementation specific. - - Support: Extended - properties: - caCertificateRefs: - description: |- - CACertificateRefs contains one or more references to - Kubernetes objects that contain TLS certificates of - the Certificate Authorities that can be used - as a trust anchor to validate the certificates presented by the client. - - A single CA certificate reference to a Kubernetes ConfigMap - has "Core" support. - Implementations MAY choose to support attaching multiple CA certificates to - a Listener, but this behavior is implementation-specific. - - Support: Core - A single reference to a Kubernetes ConfigMap - with the CA certificate in a key named `ca.crt`. - - Support: Implementation-specific (More than one reference, or other kinds - of resources). - - References to a resource in a different namespace are invalid UNLESS there - is a ReferenceGrant in the target namespace that allows the certificate - to be attached. If a ReferenceGrant does not allow this reference, the - "ResolvedRefs" condition MUST be set to False for this listener with the - "RefNotPermitted" reason. - items: - description: |- - 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. - properties: - group: - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When set to the empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - description: Kind is kind of the referent. For - example "ConfigMap" or "Service". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - 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 - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - group - - kind - - name - type: object - maxItems: 8 - minItems: 1 - type: array - type: object + x-kubernetes-list-type: atomic mode: default: Terminate description: |- @@ -3582,6 +4403,366 @@ spec: rule: 'self.all(l1, self.exists_one(l2, l1.port == l2.port && l1.protocol == l2.protocol && (has(l1.hostname) && has(l2.hostname) ? l1.hostname == l2.hostname : !has(l1.hostname) && !has(l2.hostname))))' + tls: + description: |- + TLS specifies frontend and backend tls configuration for entire gateway. + + Support: Extended + properties: + backend: + description: |- + Backend describes TLS configuration for gateway when connecting + to backends. + + Note that this contains only details for the Gateway as a TLS client, + and does _not_ imply behavior about how to choose which backend should + get a TLS connection. That is determined by the presence of a BackendTLSPolicy. + + Support: Core + properties: + clientCertificateRef: + description: |- + ClientCertificateRef is a reference to an object that contains a Client + Certificate and the associated private key. + + References to a resource in different namespace are invalid UNLESS there + is a ReferenceGrant in the target namespace that allows the certificate + to be attached. If a ReferenceGrant does not allow this reference, the + "ResolvedRefs" condition MUST be set to False for this listener with the + "RefNotPermitted" reason. + + ClientCertificateRef can reference to standard Kubernetes resources, i.e. + Secret, or implementation-specific custom resources. + + Support: Core + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Secret + description: Kind is kind of the referent. For example + "Secret". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - name + type: object + type: object + frontend: + description: |- + Frontend describes TLS config when client connects to Gateway. + Support: Core + properties: + default: + description: |- + Default specifies the default client certificate validation configuration + for all Listeners handling HTTPS traffic, unless a per-port configuration + is defined. + + support: Core + properties: + validation: + description: |- + Validation holds configuration information for validating the frontend (client). + Setting this field will result in mutual authentication when connecting to the gateway. + In browsers this may result in a dialog appearing + that requests a user to specify the client certificate. + The maximum depth of a certificate chain accepted in verification is Implementation specific. + + Support: Core + properties: + caCertificateRefs: + description: |- + CACertificateRefs contains one or more references to + Kubernetes objects that contain TLS certificates of + the Certificate Authorities that can be used + as a trust anchor to validate the certificates presented by the client. + + A single CA certificate reference to a Kubernetes ConfigMap + has "Core" support. + Implementations MAY choose to support attaching multiple CA certificates to + a Listener, but this behavior is implementation-specific. + + Support: Core - A single reference to a Kubernetes ConfigMap + with the CA certificate in a key named `ca.crt`. + + Support: Implementation-specific (More than one certificate in a ConfigMap + with different keys or more than one reference, or other kinds of resources). + + References to a resource in a different namespace are invalid UNLESS there + is a ReferenceGrant in the target namespace that allows the certificate + to be attached. If a ReferenceGrant does not allow this reference, the + "ResolvedRefs" condition MUST be set to False for this listener with the + "RefNotPermitted" reason. + items: + description: |- + 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. + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When set to the empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For + example "ConfigMap" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + mode: + default: AllowValidOnly + description: |- + FrontendValidationMode defines the mode for validating the client certificate. + There are two possible modes: + + - AllowValidOnly: In this mode, the gateway will accept connections only if + the client presents a valid certificate. This certificate must successfully + pass validation against the CA certificates specified in `CACertificateRefs`. + - AllowInsecureFallback: In this mode, the gateway will accept connections + even if the client certificate is not presented or fails verification. + + This approach delegates client authorization to the backend and introduce + a significant security risk. It should be used in testing environments or + on a temporary basis in non-testing environments. + + Defaults to AllowValidOnly. + + Support: Core + enum: + - AllowValidOnly + - AllowInsecureFallback + type: string + required: + - caCertificateRefs + type: object + type: object + perPort: + description: |- + PerPort specifies tls configuration assigned per port. + Per port configuration is optional. Once set this configuration overrides + the default configuration for all Listeners handling HTTPS traffic + that match this port. + Each override port requires a unique TLS configuration. + + support: Core + items: + properties: + port: + description: |- + The Port indicates the Port Number to which the TLS configuration will be + applied. This configuration will be applied to all Listeners handling HTTPS + traffic that match this port. + + Support: Core + format: int32 + maximum: 65535 + minimum: 1 + type: integer + tls: + description: |- + TLS store the configuration that will be applied to all Listeners handling + HTTPS traffic and matching given port. + + Support: Core + properties: + validation: + description: |- + Validation holds configuration information for validating the frontend (client). + Setting this field will result in mutual authentication when connecting to the gateway. + In browsers this may result in a dialog appearing + that requests a user to specify the client certificate. + The maximum depth of a certificate chain accepted in verification is Implementation specific. + + Support: Core + properties: + caCertificateRefs: + description: |- + CACertificateRefs contains one or more references to + Kubernetes objects that contain TLS certificates of + the Certificate Authorities that can be used + as a trust anchor to validate the certificates presented by the client. + + A single CA certificate reference to a Kubernetes ConfigMap + has "Core" support. + Implementations MAY choose to support attaching multiple CA certificates to + a Listener, but this behavior is implementation-specific. + + Support: Core - A single reference to a Kubernetes ConfigMap + with the CA certificate in a key named `ca.crt`. + + Support: Implementation-specific (More than one certificate in a ConfigMap + with different keys or more than one reference, or other kinds of resources). + + References to a resource in a different namespace are invalid UNLESS there + is a ReferenceGrant in the target namespace that allows the certificate + to be attached. If a ReferenceGrant does not allow this reference, the + "ResolvedRefs" condition MUST be set to False for this listener with the + "RefNotPermitted" reason. + items: + description: |- + 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. + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When set to the empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. + For example "ConfigMap" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + mode: + default: AllowValidOnly + description: |- + FrontendValidationMode defines the mode for validating the client certificate. + There are two possible modes: + + - AllowValidOnly: In this mode, the gateway will accept connections only if + the client presents a valid certificate. This certificate must successfully + pass validation against the CA certificates specified in `CACertificateRefs`. + - AllowInsecureFallback: In this mode, the gateway will accept connections + even if the client certificate is not presented or fails verification. + + This approach delegates client authorization to the backend and introduce + a significant security risk. It should be used in testing environments or + on a temporary basis in non-testing environments. + + Defaults to AllowValidOnly. + + Support: Core + enum: + - AllowValidOnly + - AllowInsecureFallback + type: string + required: + - caCertificateRefs + type: object + type: object + required: + - port + - tls + type: object + maxItems: 64 + type: array + x-kubernetes-list-map-keys: + - port + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: Port for TLS configuration must be unique within + the Gateway + rule: self.all(t1, self.exists_one(t2, t1.port == t2.port)) + required: + - default + type: object + type: object required: - gatewayClassName - listeners @@ -3656,6 +4837,7 @@ spec: true' maxItems: 16 type: array + x-kubernetes-list-type: atomic conditions: default: - lastTransitionTime: "1970-01-01T00:00:00Z" @@ -3869,6 +5051,7 @@ spec: type: object maxItems: 8 type: array + x-kubernetes-list-type: atomic required: - attachedRoutes - conditions @@ -3903,9 +5086,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null name: grpcroutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -4051,6 +5233,7 @@ spec: type: string maxItems: 16 type: array + x-kubernetes-list-type: atomic parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants @@ -4263,6 +5446,7 @@ spec: type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent @@ -4884,6 +6068,7 @@ spec: rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: RequestHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'RequestHeaderModifier').size() @@ -4980,6 +6165,7 @@ spec: ? has(self.port) : true' maxItems: 16 type: array + x-kubernetes-list-type: atomic filters: description: |- Filters define the filters that are applied to requests that match @@ -5530,6 +6716,7 @@ spec: rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: RequestHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'RequestHeaderModifier').size() @@ -5707,6 +6894,7 @@ spec: type: object maxItems: 64 type: array + x-kubernetes-list-type: atomic name: description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. @@ -5808,6 +6996,7 @@ spec: type: object maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - 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 @@ -5832,6 +7021,24 @@ spec: - 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)) + useDefaultGateways: + description: |- + 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. + enum: + - All + - None + type: string type: object status: description: Status defines the current state of GRPCRoute. @@ -6096,14 +7303,18 @@ spec: - name type: object required: + - conditions - controllerName - parentRef type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic required: - parents type: object + required: + - spec type: object served: true storage: true @@ -6124,9 +7335,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null name: httproutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -6252,6 +7462,7 @@ spec: type: string maxItems: 16 type: array + x-kubernetes-list-type: atomic parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants @@ -6464,6 +7675,7 @@ spec: type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent @@ -6589,16 +7801,14 @@ spec: AllowCredentials indicates whether the actual cross-origin request allows to include credentials. - The only valid value for the `Access-Control-Allow-Credentials` response - header is true (case-sensitive). + When set to true, the gateway will include the `Access-Control-Allow-Credentials` + response header with value true (case-sensitive). - If the credentials are not allowed in cross-origin requests, the gateway - will omit the header `Access-Control-Allow-Credentials` entirely rather - than setting its value to false. + 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 - enum: - - true type: boolean allowHeaders: description: |- @@ -6625,9 +7835,9 @@ spec: 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 unspecified. + wildcard as value when the `AllowCredentials` field is false or omitted. - When the `AllowCredentials` field is specified and `AllowHeaders` field + 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 @@ -6688,9 +7898,9 @@ spec: side. The `Access-Control-Allow-Methods` response header can only use `*` - wildcard as value when the `AllowCredentials` field is unspecified. + wildcard as value when the `AllowCredentials` field is false or omitted. - When the `AllowCredentials` field is specified and `AllowMethods` field + 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 @@ -6766,9 +7976,9 @@ spec: 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 unspecified. + wildcard as value when the `AllowCredentials` field is false or omitted. - When the `AllowCredentials` field is specified and `AllowOrigins` field + 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 @@ -6778,18 +7988,22 @@ spec: Support: Extended items: description: |- - 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 + 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. maxLength: 253 minLength: 1 - pattern: ^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))? + pattern: (^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$) type: string maxItems: 64 type: array x-kubernetes-list-type: set + x-kubernetes-validations: + - message: AllowOrigins cannot contain '*' alongside + other origins + rule: '!(''*'' in self && self.size() > 1)' exposeHeaders: description: |- ExposeHeaders indicates which HTTP response headers can be exposed @@ -6819,8 +8033,7 @@ spec: 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 - unspecified. + use `*` wildcard as value when the `AllowCredentials` field is false or omitted. Support: Extended items: @@ -6895,6 +8108,253 @@ spec: - kind - name type: object + externalAuth: + description: |- + 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 + properties: + backendRef: + description: |- + 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. + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + 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) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind + == ''Service'') ? has(self.port) : true' + forwardBody: + description: |- + 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 + properties: + maxSize: + description: |- + 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. + type: integer + type: object + grpc: + description: |- + 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. + properties: + allowedHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + type: object + http: + description: |- + HTTPAuthConfig contains configuration for communication with HTTP-speaking + backends. + + If unset, implementations must assume the default behavior for each + included field is intended. + properties: + allowedHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + allowedResponseHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + path: + description: |- + 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. + maxLength: 1024 + pattern: ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$ + type: string + type: object + protocol: + description: |- + 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 + enum: + - HTTP + - GRPC + type: string + required: + - backendRef + - protocol + type: object + x-kubernetes-validations: + - message: grpc must be specified when protocol + is set to 'GRPC' + rule: 'self.protocol == ''GRPC'' ? has(self.grpc) + : true' + - message: protocol must be 'GRPC' when grpc is + set + rule: 'has(self.grpc) ? self.protocol == ''GRPC'' + : true' + - message: http must be specified when protocol + is set to 'HTTP' + rule: 'self.protocol == ''HTTP'' ? has(self.http) + : true' + - message: protocol must be 'HTTP' when http is + set + rule: 'has(self.http) ? self.protocol == ''HTTP'' + : true' requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request @@ -7508,6 +8968,7 @@ spec: - URLRewrite - ExtensionRef - CORS + - ExternalAuth type: string urlRewrite: description: |- @@ -7645,13 +9106,16 @@ spec: rule: '!(has(self.cors) && self.type != ''CORS'')' - message: filter.cors must be specified for CORS filter.type rule: '!(!has(self.cors) && self.type == ''CORS'')' + - message: filter.externalAuth must be nil if the filter.type + is not ExternalAuth + rule: '!(has(self.externalAuth) && self.type != ''ExternalAuth'')' + - message: filter.externalAuth must be specified for + ExternalAuth filter.type + rule: '!(!has(self.externalAuth) && self.type == ''ExternalAuth'')' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - - message: May specify either httpRouteFilterRequestRedirect - or httpRouteFilterRequestRewrite, but not both - rule: '!(self.exists(f, f.type == ''RequestRedirect'') - && self.exists(f, f.type == ''URLRewrite''))' - message: May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both rule: '!(self.exists(f, f.type == ''RequestRedirect'') @@ -7757,6 +9221,7 @@ spec: ? has(self.port) : true' maxItems: 16 type: array + x-kubernetes-list-type: atomic filters: description: |- Filters define the filters that are applied to requests that match @@ -7816,16 +9281,14 @@ spec: AllowCredentials indicates whether the actual cross-origin request allows to include credentials. - The only valid value for the `Access-Control-Allow-Credentials` response - header is true (case-sensitive). + When set to true, the gateway will include the `Access-Control-Allow-Credentials` + response header with value true (case-sensitive). - If the credentials are not allowed in cross-origin requests, the gateway - will omit the header `Access-Control-Allow-Credentials` entirely rather - than setting its value to false. + 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 - enum: - - true type: boolean allowHeaders: description: |- @@ -7852,9 +9315,9 @@ spec: 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 unspecified. + wildcard as value when the `AllowCredentials` field is false or omitted. - When the `AllowCredentials` field is specified and `AllowHeaders` field + 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 @@ -7915,9 +9378,9 @@ spec: side. The `Access-Control-Allow-Methods` response header can only use `*` - wildcard as value when the `AllowCredentials` field is unspecified. + wildcard as value when the `AllowCredentials` field is false or omitted. - When the `AllowCredentials` field is specified and `AllowMethods` field + 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 @@ -7993,9 +9456,9 @@ spec: 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 unspecified. + wildcard as value when the `AllowCredentials` field is false or omitted. - When the `AllowCredentials` field is specified and `AllowOrigins` field + 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 @@ -8005,18 +9468,22 @@ spec: Support: Extended items: description: |- - 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 + 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. maxLength: 253 minLength: 1 - pattern: ^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))? + pattern: (^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$) type: string maxItems: 64 type: array x-kubernetes-list-type: set + x-kubernetes-validations: + - message: AllowOrigins cannot contain '*' alongside + other origins + rule: '!(''*'' in self && self.size() > 1)' exposeHeaders: description: |- ExposeHeaders indicates which HTTP response headers can be exposed @@ -8046,8 +9513,7 @@ spec: 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 - unspecified. + use `*` wildcard as value when the `AllowCredentials` field is false or omitted. Support: Extended items: @@ -8122,6 +9588,251 @@ spec: - kind - name type: object + externalAuth: + description: |- + 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 + properties: + backendRef: + description: |- + 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. + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + 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) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + forwardBody: + description: |- + 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 + properties: + maxSize: + description: |- + 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. + type: integer + type: object + grpc: + description: |- + 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. + properties: + allowedHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + type: object + http: + description: |- + HTTPAuthConfig contains configuration for communication with HTTP-speaking + backends. + + If unset, implementations must assume the default behavior for each + included field is intended. + properties: + allowedHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + allowedResponseHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + path: + description: |- + 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. + maxLength: 1024 + pattern: ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$ + type: string + type: object + protocol: + description: |- + 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 + enum: + - HTTP + - GRPC + type: string + required: + - backendRef + - protocol + type: object + x-kubernetes-validations: + - message: grpc must be specified when protocol is set + to 'GRPC' + rule: 'self.protocol == ''GRPC'' ? has(self.grpc) : + true' + - message: protocol must be 'GRPC' when grpc is set + rule: 'has(self.grpc) ? self.protocol == ''GRPC'' : + true' + - message: http must be specified when protocol is set + to 'HTTP' + rule: 'self.protocol == ''HTTP'' ? has(self.http) : + true' + - message: protocol must be 'HTTP' when http is set + rule: 'has(self.http) ? self.protocol == ''HTTP'' : + true' requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request @@ -8731,6 +10442,7 @@ spec: - URLRewrite - ExtensionRef - CORS + - ExternalAuth type: string urlRewrite: description: |- @@ -8865,8 +10577,15 @@ spec: rule: '!(has(self.cors) && self.type != ''CORS'')' - message: filter.cors must be specified for CORS filter.type rule: '!(!has(self.cors) && self.type == ''CORS'')' + - message: filter.externalAuth must be nil if the filter.type + is not ExternalAuth + rule: '!(has(self.externalAuth) && self.type != ''ExternalAuth'')' + - message: filter.externalAuth must be specified for ExternalAuth + filter.type + rule: '!(!has(self.externalAuth) && self.type == ''ExternalAuth'')' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both @@ -9178,6 +10897,7 @@ spec: type: object maxItems: 64 type: array + x-kubernetes-list-type: atomic name: description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. @@ -9273,6 +10993,7 @@ spec: minimum: 400 type: integer type: array + x-kubernetes-list-type: atomic type: object sessionPersistence: description: |- @@ -9468,6 +11189,7 @@ spec: != ''PathPrefix'') ? false : true) : true' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - 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 @@ -9486,6 +11208,24 @@ spec: - 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)) + useDefaultGateways: + description: |- + 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. + enum: + - All + - None + type: string type: object status: description: Status defines the current state of HTTPRoute. @@ -9750,11 +11490,13 @@ spec: - name type: object required: + - conditions - controllerName - parentRef type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic required: - parents type: object @@ -9878,6 +11620,7 @@ spec: type: string maxItems: 16 type: array + x-kubernetes-list-type: atomic parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants @@ -10090,6 +11833,7 @@ spec: type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent @@ -10215,16 +11959,14 @@ spec: AllowCredentials indicates whether the actual cross-origin request allows to include credentials. - The only valid value for the `Access-Control-Allow-Credentials` response - header is true (case-sensitive). + When set to true, the gateway will include the `Access-Control-Allow-Credentials` + response header with value true (case-sensitive). - If the credentials are not allowed in cross-origin requests, the gateway - will omit the header `Access-Control-Allow-Credentials` entirely rather - than setting its value to false. + 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 - enum: - - true type: boolean allowHeaders: description: |- @@ -10251,9 +11993,9 @@ spec: 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 unspecified. + wildcard as value when the `AllowCredentials` field is false or omitted. - When the `AllowCredentials` field is specified and `AllowHeaders` field + 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 @@ -10314,9 +12056,9 @@ spec: side. The `Access-Control-Allow-Methods` response header can only use `*` - wildcard as value when the `AllowCredentials` field is unspecified. + wildcard as value when the `AllowCredentials` field is false or omitted. - When the `AllowCredentials` field is specified and `AllowMethods` field + 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 @@ -10392,9 +12134,9 @@ spec: 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 unspecified. + wildcard as value when the `AllowCredentials` field is false or omitted. - When the `AllowCredentials` field is specified and `AllowOrigins` field + 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 @@ -10404,18 +12146,22 @@ spec: Support: Extended items: description: |- - 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 + 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. maxLength: 253 minLength: 1 - pattern: ^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))? + pattern: (^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$) type: string maxItems: 64 type: array x-kubernetes-list-type: set + x-kubernetes-validations: + - message: AllowOrigins cannot contain '*' alongside + other origins + rule: '!(''*'' in self && self.size() > 1)' exposeHeaders: description: |- ExposeHeaders indicates which HTTP response headers can be exposed @@ -10445,8 +12191,7 @@ spec: 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 - unspecified. + use `*` wildcard as value when the `AllowCredentials` field is false or omitted. Support: Extended items: @@ -10521,6 +12266,253 @@ spec: - kind - name type: object + externalAuth: + description: |- + 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 + properties: + backendRef: + description: |- + 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. + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + 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) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind + == ''Service'') ? has(self.port) : true' + forwardBody: + description: |- + 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 + properties: + maxSize: + description: |- + 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. + type: integer + type: object + grpc: + description: |- + 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. + properties: + allowedHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + type: object + http: + description: |- + HTTPAuthConfig contains configuration for communication with HTTP-speaking + backends. + + If unset, implementations must assume the default behavior for each + included field is intended. + properties: + allowedHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + allowedResponseHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + path: + description: |- + 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. + maxLength: 1024 + pattern: ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$ + type: string + type: object + protocol: + description: |- + 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 + enum: + - HTTP + - GRPC + type: string + required: + - backendRef + - protocol + type: object + x-kubernetes-validations: + - message: grpc must be specified when protocol + is set to 'GRPC' + rule: 'self.protocol == ''GRPC'' ? has(self.grpc) + : true' + - message: protocol must be 'GRPC' when grpc is + set + rule: 'has(self.grpc) ? self.protocol == ''GRPC'' + : true' + - message: http must be specified when protocol + is set to 'HTTP' + rule: 'self.protocol == ''HTTP'' ? has(self.http) + : true' + - message: protocol must be 'HTTP' when http is + set + rule: 'has(self.http) ? self.protocol == ''HTTP'' + : true' requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request @@ -11134,6 +13126,7 @@ spec: - URLRewrite - ExtensionRef - CORS + - ExternalAuth type: string urlRewrite: description: |- @@ -11271,13 +13264,16 @@ spec: rule: '!(has(self.cors) && self.type != ''CORS'')' - message: filter.cors must be specified for CORS filter.type rule: '!(!has(self.cors) && self.type == ''CORS'')' + - message: filter.externalAuth must be nil if the filter.type + is not ExternalAuth + rule: '!(has(self.externalAuth) && self.type != ''ExternalAuth'')' + - message: filter.externalAuth must be specified for + ExternalAuth filter.type + rule: '!(!has(self.externalAuth) && self.type == ''ExternalAuth'')' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - - message: May specify either httpRouteFilterRequestRedirect - or httpRouteFilterRequestRewrite, but not both - rule: '!(self.exists(f, f.type == ''RequestRedirect'') - && self.exists(f, f.type == ''URLRewrite''))' - message: May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both rule: '!(self.exists(f, f.type == ''RequestRedirect'') @@ -11383,6 +13379,7 @@ spec: ? has(self.port) : true' maxItems: 16 type: array + x-kubernetes-list-type: atomic filters: description: |- Filters define the filters that are applied to requests that match @@ -11442,16 +13439,14 @@ spec: AllowCredentials indicates whether the actual cross-origin request allows to include credentials. - The only valid value for the `Access-Control-Allow-Credentials` response - header is true (case-sensitive). + When set to true, the gateway will include the `Access-Control-Allow-Credentials` + response header with value true (case-sensitive). - If the credentials are not allowed in cross-origin requests, the gateway - will omit the header `Access-Control-Allow-Credentials` entirely rather - than setting its value to false. + 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 - enum: - - true type: boolean allowHeaders: description: |- @@ -11478,9 +13473,9 @@ spec: 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 unspecified. + wildcard as value when the `AllowCredentials` field is false or omitted. - When the `AllowCredentials` field is specified and `AllowHeaders` field + 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 @@ -11541,9 +13536,9 @@ spec: side. The `Access-Control-Allow-Methods` response header can only use `*` - wildcard as value when the `AllowCredentials` field is unspecified. + wildcard as value when the `AllowCredentials` field is false or omitted. - When the `AllowCredentials` field is specified and `AllowMethods` field + 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 @@ -11619,9 +13614,9 @@ spec: 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 unspecified. + wildcard as value when the `AllowCredentials` field is false or omitted. - When the `AllowCredentials` field is specified and `AllowOrigins` field + 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 @@ -11631,18 +13626,22 @@ spec: Support: Extended items: description: |- - 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 + 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. maxLength: 253 minLength: 1 - pattern: ^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))? + pattern: (^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$) type: string maxItems: 64 type: array x-kubernetes-list-type: set + x-kubernetes-validations: + - message: AllowOrigins cannot contain '*' alongside + other origins + rule: '!(''*'' in self && self.size() > 1)' exposeHeaders: description: |- ExposeHeaders indicates which HTTP response headers can be exposed @@ -11672,8 +13671,7 @@ spec: 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 - unspecified. + use `*` wildcard as value when the `AllowCredentials` field is false or omitted. Support: Extended items: @@ -11748,6 +13746,251 @@ spec: - kind - name type: object + externalAuth: + description: |- + 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 + properties: + backendRef: + description: |- + 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. + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + 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) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + forwardBody: + description: |- + 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 + properties: + maxSize: + description: |- + 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. + type: integer + type: object + grpc: + description: |- + 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. + properties: + allowedHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + type: object + http: + description: |- + HTTPAuthConfig contains configuration for communication with HTTP-speaking + backends. + + If unset, implementations must assume the default behavior for each + included field is intended. + properties: + allowedHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + allowedResponseHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + path: + description: |- + 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. + maxLength: 1024 + pattern: ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$ + type: string + type: object + protocol: + description: |- + 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 + enum: + - HTTP + - GRPC + type: string + required: + - backendRef + - protocol + type: object + x-kubernetes-validations: + - message: grpc must be specified when protocol is set + to 'GRPC' + rule: 'self.protocol == ''GRPC'' ? has(self.grpc) : + true' + - message: protocol must be 'GRPC' when grpc is set + rule: 'has(self.grpc) ? self.protocol == ''GRPC'' : + true' + - message: http must be specified when protocol is set + to 'HTTP' + rule: 'self.protocol == ''HTTP'' ? has(self.http) : + true' + - message: protocol must be 'HTTP' when http is set + rule: 'has(self.http) ? self.protocol == ''HTTP'' : + true' requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request @@ -12357,6 +14600,7 @@ spec: - URLRewrite - ExtensionRef - CORS + - ExternalAuth type: string urlRewrite: description: |- @@ -12491,8 +14735,15 @@ spec: rule: '!(has(self.cors) && self.type != ''CORS'')' - message: filter.cors must be specified for CORS filter.type rule: '!(!has(self.cors) && self.type == ''CORS'')' + - message: filter.externalAuth must be nil if the filter.type + is not ExternalAuth + rule: '!(has(self.externalAuth) && self.type != ''ExternalAuth'')' + - message: filter.externalAuth must be specified for ExternalAuth + filter.type + rule: '!(!has(self.externalAuth) && self.type == ''ExternalAuth'')' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both @@ -12804,6 +15055,7 @@ spec: type: object maxItems: 64 type: array + x-kubernetes-list-type: atomic name: description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. @@ -12899,6 +15151,7 @@ spec: minimum: 400 type: integer type: array + x-kubernetes-list-type: atomic type: object sessionPersistence: description: |- @@ -13094,6 +15347,7 @@ spec: != ''PathPrefix'') ? false : true) : true' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - 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 @@ -13112,6 +15366,24 @@ spec: - 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)) + useDefaultGateways: + description: |- + 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. + enum: + - All + - None + type: string type: object status: description: Status defines the current state of HTTPRoute. @@ -13376,11 +15648,13 @@ spec: - name type: object required: + - conditions - controllerName - parentRef type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic required: - parents type: object @@ -13406,9 +15680,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null name: referencegrants.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -13527,6 +15800,7 @@ spec: maxItems: 16 minItems: 1 type: array + x-kubernetes-list-type: atomic to: description: |- To describes the resources that may be referenced by the resources @@ -13576,6 +15850,7 @@ spec: maxItems: 16 minItems: 1 type: array + x-kubernetes-list-type: atomic required: - from - to @@ -13599,9 +15874,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null name: tcproutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -13858,6 +16132,7 @@ spec: type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent @@ -14022,6 +16297,7 @@ spec: maxItems: 16 minItems: 1 type: array + x-kubernetes-list-type: atomic name: description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. @@ -14031,14 +16307,35 @@ spec: minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string + required: + - backendRefs type: object maxItems: 16 minItems: 1 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - 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)) + useDefaultGateways: + description: |- + 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. + enum: + - All + - None + type: string required: - rules type: object @@ -14305,11 +16602,13 @@ spec: - name type: object required: + - conditions - controllerName - parentRef type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic required: - parents type: object @@ -14335,9 +16634,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null name: tlsroutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -14442,6 +16740,7 @@ spec: type: string maxItems: 16 type: array + x-kubernetes-list-type: atomic parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants @@ -14654,6 +16953,7 @@ spec: type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent @@ -14821,6 +17121,7 @@ spec: maxItems: 16 minItems: 1 type: array + x-kubernetes-list-type: atomic name: description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. @@ -14830,14 +17131,35 @@ spec: minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string + required: + - backendRefs type: object maxItems: 16 minItems: 1 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - 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)) + useDefaultGateways: + description: |- + 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. + enum: + - All + - None + type: string required: - rules type: object @@ -15104,11 +17426,810 @@ spec: - name type: object required: + - conditions - controllerName - parentRef type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: false + subresources: + status: {} + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha3 + schema: + openAPIV3Schema: + description: |- + The TLSRoute resource is similar to TCPRoute, but can be configured + to match against TLS-specific metadata. This allows more flexibility + in matching streams for a given TLS listener. + + If you need to forward traffic to a single target for a TLS listener, you + could choose to use a TCPRoute with a TLS listener. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of TLSRoute. + properties: + hostnames: + description: |- + Hostnames defines a set of SNI hostnames that should match against the + SNI attribute of TLS ClientHello message in TLS handshake. This matches + the RFC 1123 definition of a hostname with 2 notable exceptions: + + 1. IPs are not allowed in SNI hostnames per RFC 6066. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + If a hostname is specified by both the Listener and TLSRoute, there + must be at least one intersecting hostname for the TLSRoute to be + attached to the Listener. For example: + + * A Listener with `test.example.com` as the hostname matches TLSRoutes + that have specified at least one of `test.example.com` or + `*.example.com`. + * A Listener with `*.example.com` as the hostname matches TLSRoutes + that have specified at least one hostname that matches the Listener + hostname. For example, `test.example.com` and `*.example.com` would both + match. On the other hand, `example.com` and `test.example.net` would not + match. + + If both the Listener and TLSRoute have specified hostnames, any + TLSRoute hostnames that do not match the Listener hostname MUST be + ignored. For example, if a Listener specified `*.example.com`, and the + TLSRoute specified `test.example.com` and `test.example.net`, + `test.example.net` must not be considered for a match. + + If both the Listener and TLSRoute have specified hostnames, and none + match with the criteria above, then the TLSRoute is not accepted. The + implementation must raise an 'Accepted' Condition with a status of + `False` in the corresponding RouteParentStatus. + + Support: Core + items: + description: |- + Hostname is the fully qualified domain name of a network host. This matches + the RFC 1123 definition of a hostname with 2 notable exceptions: + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + Hostname can be "precise" which is a domain name without the terminating + dot of a network host (e.g. "foo.example.com") or "wildcard", which is a + domain name prefixed with a single wildcard label (e.g. `*.example.com`). + + Note that as per RFC1035 and RFC1123, a *label* must consist of lower case + alphanumeric characters or '-', and must start and end with an alphanumeric + character. No other punctuation is allowed. + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + parentRefs: + description: |- + ParentRefs references the resources (usually Gateways) that a Route wants + to be attached to. Note that the referenced parent resource needs to + allow this for the attachment to be complete. For Gateways, that means + the Gateway needs to allow attachment from Routes of this kind and + namespace. For Services, that means the Service must either be in the same + namespace for a "producer" route, or the mesh implementation must support + and allow "consumer" routes for the referenced Service. ReferenceGrant is + not applicable for governing ParentRefs to Services - it is not possible to + create a "producer" route for a Service in a different namespace from the + Route. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + This API may be extended in the future to support additional kinds of parent + resources. + + ParentRefs must be _distinct_. This means either that: + + * They select different objects. If this is the case, then parentRef + entries are distinct. In terms of fields, this means that the + multi-part key defined by `group`, `kind`, `namespace`, and `name` must + be unique across all parentRef entries in the Route. + * They do not select different objects, but for each optional field used, + each ParentRef that selects the same object must set the same set of + optional fields to different values. If one ParentRef sets a + combination of optional fields, all must set the same combination. + + Some examples: + + * If one ParentRef sets `sectionName`, all ParentRefs referencing the + same object must also set `sectionName`. + * If one ParentRef sets `port`, all ParentRefs referencing the same + object must also set `port`. + * If one ParentRef sets `sectionName` and `port`, all ParentRefs + referencing the same object must also set `sectionName` and `port`. + + It is possible to separately reference multiple distinct objects that may + be collapsed by an implementation. For example, some implementations may + choose to merge compatible Gateway Listeners together. If that is the + case, the list of routes attached to those resources should also be + merged. + + Note that for ParentRefs that cross namespace boundaries, there are specific + rules. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example, + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable other kinds of cross-namespace reference. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + items: + description: |- + ParentReference identifies an API object (usually a Gateway) that can be considered + a parent of this resource (usually a route). There are two kinds of parent resources + with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + This API may be extended in the future to support additional kinds of parent + resources. + + 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. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + When the parent resource is a Service, this targets a specific port in the + Service spec. When both Port (experimental) and SectionName are specified, + the name and port of the selected port must match both specified values. + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + 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 + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + 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 + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + x-kubernetes-list-type: atomic + x-kubernetes-validations: + - 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))' + - 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)))) + rules: + description: Rules are a list of actions. + items: + description: TLSRouteRule is the configuration for a given rule. + properties: + backendRefs: + description: |- + BackendRefs defines the backend(s) where matching requests should be + sent. If unspecified or invalid (refers to a nonexistent resource or + a Service with no endpoints), the rule performs no forwarding; if no + filters are specified that would result in a response being sent, the + underlying implementation must actively reject request attempts to this + backend, by rejecting the connection or returning a 500 status code. + Request rejections must respect weight; if an invalid backend is + requested to have 80% of requests, then 80% of requests must be rejected + instead. + + Support: Core for Kubernetes Service + + Support: Extended for Kubernetes ServiceImport + + Support: Implementation-specific for any other resource + + Support for weight: Extended + items: + description: |- + BackendRef defines how a Route should forward a request to a Kubernetes + resource. + + 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. + + + When the BackendRef points to a Kubernetes Service, implementations SHOULD + honor the appProtocol field if it is set for the target Service Port. + + Implementations supporting appProtocol SHOULD recognize the Kubernetes + Standard Application Protocols defined in KEP-3726. + + If a Service appProtocol isn't specified, an implementation MAY infer the + backend protocol through its own means. Implementations MAY infer the + protocol from the Route type referring to the backend Service. + + If a Route is not able to send traffic to the backend using the specified + protocol then the backend is considered invalid. Implementations MUST set the + "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. + + + Note that when the BackendTLSPolicy object is enabled by the implementation, + there are some extra rules about validity to consider here. See the fields + where this struct is used for more information about the exact behavior. + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + 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) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: |- + Weight specifies the proportion of requests forwarded to the referenced + backend. This is computed as weight/(sum of all weights in this + BackendRefs list). For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision an + implementation supports. Weight is not a percentage and the sum of + weights does not need to equal 100. + + If only one backend is specified and it has a weight greater than 0, 100% + of the traffic is forwarded to that backend. If weight is set to 0, no + traffic should be forwarded for this entry. If unspecified, weight + defaults to 1. + + Support for this field varies based on the context where used. + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + maxItems: 16 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + name: + description: |- + Name is the name of the route rule. This name MUST be unique within a Route if it is set. + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - backendRefs + type: object + maxItems: 1 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + x-kubernetes-validations: + - 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)) + useDefaultGateways: + description: |- + 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. + enum: + - All + - None + type: string + required: + - hostnames + - rules + type: object + status: + description: Status defines the current state of TLSRoute. + properties: + parents: + description: |- + Parents is a list of parent resources (usually Gateways) that are + associated with the route, and the status of the route with respect to + each parent. When this route attaches to a parent, the controller that + manages the parent must add an entry to this list when the controller + first sees the route and should update the entry as appropriate when the + route or gateway is modified. + + Note that parent references that cannot be resolved by an implementation + of this API will not be added to this list. Implementations of this API + can only populate Route status for the Gateways/parent resources they are + responsible for. + + A maximum of 32 Gateways will be represented in this list. An empty list + means the route has not been attached to any Gateway. + items: + description: |- + RouteParentStatus describes the status of a route with respect to an + associated Parent. + properties: + conditions: + description: |- + Conditions describes the status of the route with respect to the Gateway. + Note that the route's availability is also subject to the Gateway's own + status conditions and listener status. + + If the Route's ParentRef specifies an existing Gateway that supports + Routes of this kind AND that Gateway's controller has sufficient access, + then that Gateway's controller MUST set the "Accepted" condition on the + Route, to indicate whether the route has been accepted or rejected by the + Gateway, and why. + + A Route MUST be considered "Accepted" if at least one of the Route's + rules is implemented by the Gateway. + + 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 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. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: |- + ControllerName is a domain/path string that indicates the name of the + controller that wrote this status. This corresponds with the + controllerName field on GatewayClass. + + Example: "example.net/gateway-controller". + + The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are + valid Kubernetes names + (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + + 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. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: |- + ParentRef corresponds with a ParentRef in the spec that this + RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + When the parent resource is a Service, this targets a specific port in the + Service spec. When both Port (experimental) and SectionName are specified, + the name and port of the selected port must match both specified values. + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + 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 + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + 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 + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - conditions + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + x-kubernetes-list-type: atomic required: - parents type: object @@ -15134,9 +18255,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null name: udproutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -15393,6 +18513,7 @@ spec: type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent @@ -15557,6 +18678,7 @@ spec: maxItems: 16 minItems: 1 type: array + x-kubernetes-list-type: atomic name: description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. @@ -15566,14 +18688,35 @@ spec: minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string + required: + - backendRefs type: object maxItems: 16 minItems: 1 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - 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)) + useDefaultGateways: + description: |- + 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. + enum: + - All + - None + type: string required: - rules type: object @@ -15840,11 +18983,13 @@ spec: - name type: object required: + - conditions - controllerName - parentRef type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic required: - parents type: object @@ -15870,9 +19015,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null labels: gateway.networking.k8s.io/policy: Direct name: xbackendtrafficpolicies.gateway.networking.x-k8s.io @@ -16450,10 +19594,12 @@ spec: type: string required: - ancestorRef + - conditions - controllerName type: object maxItems: 16 type: array + x-kubernetes-list-type: atomic required: - ancestors type: object @@ -16479,9 +19625,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.3.0 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null name: xlistenersets.gateway.networking.x-k8s.io spec: group: gateway.networking.x-k8s.io @@ -16510,8 +19655,33 @@ spec: schema: openAPIV3Schema: description: |- - XListenerSet defines a set of additional listeners - to attach to an existing Gateway. + XListenerSet defines a set of additional listeners to attach to an existing Gateway. + This resource provides a mechanism to merge multiple listeners into a single Gateway. + + The parent Gateway must explicitly allow ListenerSet attachment through its + AllowedListeners configuration. By default, Gateways do not allow ListenerSet + attachment. + + Routes can attach to a ListenerSet by specifying it as a parentRef, and can + optionally target specific listeners using the sectionName field. + + Policy Attachment: + - Policies that attach to a ListenerSet apply to all listeners defined in that resource + - Policies do not impact listeners in the parent Gateway + - Different ListenerSets attached to the same Gateway can have different policies + - If an implementation cannot apply a policy to specific listeners, it should reject the policy + + ReferenceGrant Semantics: + - ReferenceGrants applied to a Gateway are not inherited by child ListenerSets + - ReferenceGrants applied to a ListenerSet do not grant permission to the parent Gateway's listeners + - A ListenerSet can reference secrets/backends in its own namespace without a ReferenceGrant + + Gateway Integration: + - The parent Gateway's status will include an "AttachedListenerSets" condition + - This condition will be: + - True: when AllowedListeners is set and at least one child ListenerSet is attached + - False: when AllowedListeners is set but no valid listeners are attached, or when AllowedListeners is not set or false + - Unknown: when no AllowedListeners config is present properties: apiVersion: description: |- @@ -16549,10 +19719,10 @@ spec: 1. "parent" Gateway 2. ListenerSet ordered by creation time (oldest first) - 3. ListenerSet ordered alphabetically by “{namespace}/{name}”. + 3. ListenerSet ordered alphabetically by "{namespace}/{name}". An implementation MAY reject listeners by setting the ListenerEntryStatus - `Accepted`` condition to False with the Reason `TooManyListeners` + `Accepted` condition to False with the Reason `TooManyListeners` If a listener has a conflict, this will be reported in the Status.ListenerEntryStatus setting the `Conflicted` condition to True. @@ -16625,6 +19795,7 @@ spec: type: object maxItems: 8 type: array + x-kubernetes-list-type: atomic namespaces: default: from: Same @@ -16747,12 +19918,18 @@ spec: pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string port: + default: 0 description: |- Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. + + If the port is not set or specified as zero, the implementation will assign + a unique port. If the implementation does not support dynamic port + assignment, it MUST set `Accepted` condition to `False` with the + `UnsupportedPort` reason. format: int32 maximum: 65535 - minimum: 1 + minimum: 0 type: integer protocol: description: Protocol specifies the network protocol this listener @@ -16767,7 +19944,7 @@ spec: the Protocol field is "HTTPS" or "TLS". It is invalid to set this field if the Protocol field is "HTTP", "TCP", or "UDP". - The association of SNIs to Certificate defined in GatewayTLSConfig is + The association of SNIs to Certificate defined in ListenerTLSConfig is defined based on the Hostname field for this listener. The GatewayClass MUST use the longest matching SNI out of all @@ -16852,93 +20029,7 @@ spec: type: object maxItems: 64 type: array - frontendValidation: - description: |- - FrontendValidation holds configuration information for validating the frontend (client). - Setting this field will require clients to send a client certificate - required for validation during the TLS handshake. In browsers this may result in a dialog appearing - that requests a user to specify the client certificate. - The maximum depth of a certificate chain accepted in verification is Implementation specific. - - Support: Extended - properties: - caCertificateRefs: - description: |- - CACertificateRefs contains one or more references to - Kubernetes objects that contain TLS certificates of - the Certificate Authorities that can be used - as a trust anchor to validate the certificates presented by the client. - - A single CA certificate reference to a Kubernetes ConfigMap - has "Core" support. - Implementations MAY choose to support attaching multiple CA certificates to - a Listener, but this behavior is implementation-specific. - - Support: Core - A single reference to a Kubernetes ConfigMap - with the CA certificate in a key named `ca.crt`. - - Support: Implementation-specific (More than one reference, or other kinds - of resources). - - References to a resource in a different namespace are invalid UNLESS there - is a ReferenceGrant in the target namespace that allows the certificate - to be attached. If a ReferenceGrant does not allow this reference, the - "ResolvedRefs" condition MUST be set to False for this listener with the - "RefNotPermitted" reason. - items: - description: |- - 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. - properties: - group: - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When set to the empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - description: Kind is kind of the referent. For - example "ConfigMap" or "Service". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - 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 - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - group - - kind - - name - type: object - maxItems: 8 - minItems: 1 - type: array - type: object + x-kubernetes-list-type: atomic mode: default: Terminate description: |- @@ -16990,7 +20081,6 @@ spec: > 0 || size(self.options) > 0 : true' required: - name - - port - protocol type: object maxItems: 64 @@ -17290,6 +20380,7 @@ spec: type: object maxItems: 8 type: array + x-kubernetes-list-type: atomic required: - attachedRoutes - conditions @@ -17316,3 +20407,255 @@ status: plural: "" conditions: null storedVersions: null +--- +# +# config/crd/experimental/gateway.networking.x-k8s.io_xmeshes.yaml +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.4.0 + gateway.networking.k8s.io/channel: experimental + name: xmeshes.gateway.networking.x-k8s.io +spec: + group: gateway.networking.x-k8s.io + names: + categories: + - gateway-api + kind: XMesh + listKind: XMeshList + plural: xmeshes + shortNames: + - mesh + singular: xmesh + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Accepted")].status + name: Accepted + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: XMesh defines mesh-wide characteristics of a GAMMA-compliant + service mesh. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of XMesh. + properties: + controllerName: + description: |- + ControllerName is the name of a controller that is managing Gateway API + resources for mesh traffic management. The value of this field MUST be a + domain prefixed path. + + Example: "example.com/awesome-mesh". + + This field is not mutable and cannot be empty. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + description: + description: Description optionally provides a human-readable description + of a Mesh. + maxLength: 64 + type: string + parametersRef: + description: |- + ParametersRef is an optional reference to a resource that contains + implementation-specific configuration for this Mesh. If no + implementation-specific parameters are needed, this field MUST be + omitted. + + ParametersRef can reference a standard Kubernetes resource, i.e. + ConfigMap, or an implementation-specific custom resource. The resource + can be cluster-scoped or namespace-scoped. + + If the referent cannot be found, refers to an unsupported kind, or when + the data within that resource is malformed, the Mesh MUST be rejected + with the "Accepted" status condition set to "False" and an + "InvalidParameters" reason. + + Support: Implementation-specific + properties: + group: + description: Group is the group of the referent. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. + This field is required when referring to a Namespace-scoped resource and + MUST be unset when referring to a Cluster-scoped resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + required: + - controllerName + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + description: Status defines the current state of XMesh. + properties: + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Programmed + description: |- + Conditions is the current status from the controller for + this Mesh. + + Controllers should prefer to publish conditions using values + of MeshConditionType for the type of each Condition. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + supportedFeatures: + description: |- + SupportedFeatures is the set of features the Mesh support. + It MUST be sorted in ascending alphabetical order by the Name key. + items: + properties: + name: + description: |- + FeatureName is used to describe distinct features that are covered by + conformance tests. + type: string + required: + - name + type: object + maxItems: 64 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/integration/fixtures/k8s-conformance/01-rbac.yml b/integration/fixtures/gateway-api-conformance/01-rbac.yml similarity index 100% rename from integration/fixtures/k8s-conformance/01-rbac.yml rename to integration/fixtures/gateway-api-conformance/01-rbac.yml diff --git a/integration/fixtures/k8s-conformance/02-traefik.yml b/integration/fixtures/gateway-api-conformance/02-traefik.yml similarity index 100% rename from integration/fixtures/k8s-conformance/02-traefik.yml rename to integration/fixtures/gateway-api-conformance/02-traefik.yml diff --git a/integration/fixtures/healthcheck/simple_passive.toml b/integration/fixtures/healthcheck/simple_passive.toml new file mode 100644 index 000000000..02fb698b4 --- /dev/null +++ b/integration/fixtures/healthcheck/simple_passive.toml @@ -0,0 +1,31 @@ +[global] + checkNewVersion = false + sendAnonymousUsage = false + +[log] + level = "DEBUG" + noColor = true + +[entryPoints] + [entryPoints.web] + address = ":8000" + +[api] + insecure = true + +[providers.file] + filename = "{{ .SelfFilename }}" + +## dynamic configuration ## + +[http.routers] + [http.routers.router1] + service = "service1" + rule = "Host(`test.localhost`)" + +[http.services] + [http.services.service1.loadBalancer] + [http.services.service1.loadBalancer.passiveHealthCheck] + failureWindow = "2s" + [[http.services.service1.loadBalancer.servers]] + url = "http://{{.Server1}}:80" diff --git a/integration/fixtures/highest_random_weight.toml b/integration/fixtures/highest_random_weight.toml new file mode 100644 index 000000000..de6bd1d4a --- /dev/null +++ b/integration/fixtures/highest_random_weight.toml @@ -0,0 +1,48 @@ +[global] + checkNewVersion = false + sendAnonymousUsage = false + +[api] + insecure = true + +[log] + level = "DEBUG" + noColor = true + +[entryPoints] + + [entryPoints.web] + address = ":8000" + +[providers.file] + filename = "{{ .SelfFilename }}" + +## dynamic configuration ## + +[http.routers] + [http.routers.router] + service = "hrw" + rule = "Path(`/whoami`)" + + +[http.services] + [http.services.hrw.highestRandomWeight] + [[http.services.hrw.highestRandomWeight.services]] + name = "service1" + weight = 10 + [[http.services.hrw.highestRandomWeight.services]] + name = "service2" + weight = 20 + [[http.services.hrw.highestRandomWeight.services]] + name = "service3" + weight = 30 + + [http.services.service1.loadBalancer] + [[http.services.service1.loadBalancer.servers]] + url = "{{ .Service1Server }}" + [http.services.service2.loadBalancer] + [[http.services.service2.loadBalancer.servers]] + url = "{{ .Service2Server }}" + [http.services.service3.loadBalancer] + [[http.services.service3.loadBalancer.servers]] + url = "{{ .Service3Server }}" \ No newline at end of file diff --git a/integration/fixtures/k8s/00-experimental-v1.2.1.yml b/integration/fixtures/k8s/00-experimental-v1.4.0.yml similarity index 68% rename from integration/fixtures/k8s/00-experimental-v1.2.1.yml rename to integration/fixtures/k8s/00-experimental-v1.4.0.yml index 69b689846..b1e7bd2f2 100644 --- a/integration/fixtures/k8s/00-experimental-v1.2.1.yml +++ b/integration/fixtures/k8s/00-experimental-v1.4.0.yml @@ -1,4 +1,4 @@ -# Copyright 2024 The Kubernetes Authors. +# Copyright 2025 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. @@ -17,507 +17,6 @@ # --- # -# config/crd/experimental/gateway.networking.k8s.io_backendlbpolicies.yaml -# -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.2.1 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - labels: - gateway.networking.k8s.io/policy: Direct - name: backendlbpolicies.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: BackendLBPolicy - listKind: BackendLBPolicyList - plural: backendlbpolicies - shortNames: - - blbpolicy - singular: backendlbpolicy - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1alpha2 - schema: - openAPIV3Schema: - description: |- - BackendLBPolicy provides a way to define load balancing rules - for a backend. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: Spec defines the desired state of BackendLBPolicy. - properties: - sessionPersistence: - description: |- - SessionPersistence defines and configures session persistence - for the backend. - - Support: Extended - properties: - absoluteTimeout: - description: |- - AbsoluteTimeout defines the absolute timeout of the persistent - session. Once the AbsoluteTimeout duration has elapsed, the - session becomes invalid. - - Support: Extended - pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ - type: string - cookieConfig: - description: |- - CookieConfig provides configuration settings that are specific - to cookie-based session persistence. - - Support: Core - properties: - lifetimeType: - default: Session - description: |- - 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. - - Support: Core for "Session" type - - Support: Extended for "Permanent" type - enum: - - Permanent - - Session - type: string - type: object - idleTimeout: - description: |- - 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 - pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ - type: string - sessionName: - description: |- - 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 - maxLength: 128 - type: string - type: - default: Cookie - description: |- - 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 - enum: - - Cookie - - Header - type: string - type: object - x-kubernetes-validations: - - 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)' - targetRefs: - description: |- - TargetRef identifies an API object to apply policy to. - Currently, Backends (i.e. Service, ServiceImport, or any - implementation-specific backendRef) are the only valid API - target references. - items: - description: |- - LocalPolicyTargetReference identifies an API object to apply a direct or - inherited policy to. This should be used as part of Policy resources - that can target Gateway API resources. For more information on how this - policy attachment model works, and a sample Policy resource, refer to - the policy attachment documentation for Gateway API. - properties: - group: - description: Group is the group of the target resource. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - description: Kind is kind of the target resource. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: Name is the name of the target resource. - maxLength: 253 - minLength: 1 - type: string - required: - - group - - kind - - name - type: object - maxItems: 16 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - group - - kind - - name - x-kubernetes-list-type: map - required: - - targetRefs - type: object - status: - description: Status defines the current state of BackendLBPolicy. - properties: - ancestors: - description: |- - Ancestors is a list of ancestor resources (usually Gateways) that are - associated with the policy, and the status of the policy with respect to - each ancestor. When this policy attaches to a parent, the controller that - manages the parent and the ancestors MUST add an entry to this list when - the controller first sees the policy and SHOULD update the entry as - appropriate when the relevant ancestor is modified. - - Note that choosing the relevant ancestor is left to the Policy designers; - an important part of Policy design is designing the right object level at - which to namespace this status. - - Note also that implementations MUST ONLY populate ancestor status for - the Ancestor resources they are responsible for. Implementations MUST - use the ControllerName field to uniquely identify the entries in this list - that they are responsible for. - - Note that to achieve this, the list of PolicyAncestorStatus structs - MUST be treated as a map with a composite key, made up of the AncestorRef - and ControllerName fields combined. - - A maximum of 16 ancestors will be represented in this list. An empty list - means the Policy is not relevant for any ancestors. - - If this slice is full, implementations MUST NOT add further entries. - Instead they MUST consider the policy unimplementable and signal that - on any related resources such as the ancestor that would be referenced - here. For example, if this list was full on BackendTLSPolicy, no - additional Gateways would be able to reference the Service targeted by - the BackendTLSPolicy. - items: - description: |- - PolicyAncestorStatus describes the status of a route with respect to an - associated Ancestor. - - Ancestors refer to objects that are either the Target of a policy or above it - in terms of object hierarchy. For example, if a policy targets a Service, the - Policy's Ancestors are, in order, the Service, the HTTPRoute, the Gateway, and - the GatewayClass. Almost always, in this hierarchy, the Gateway will be the most - useful object to place Policy status on, so we recommend that implementations - SHOULD use Gateway as the PolicyAncestorStatus object unless the designers - have a _very_ good reason otherwise. - - In the context of policy attachment, the Ancestor is used to distinguish which - resource results in a distinct application of this policy. For example, if a policy - targets a Service, it may have a distinct result per attached Gateway. - - Policies targeting the same resource may have different effects depending on the - ancestors of those resources. For example, different Gateways targeting the same - Service may have different capabilities, especially if they have different underlying - implementations. - - For example, in BackendTLSPolicy, the Policy attaches to a Service that is - used as a backend in a HTTPRoute that is itself attached to a Gateway. - In this case, the relevant object for status is the Gateway, and that is the - ancestor object referred to in this status. - - Note that a parent is also an ancestor, so for objects where the parent is the - relevant object for status, this struct SHOULD still be used. - - This struct is intended to be used in a slice that's effectively a map, - with a composite key made up of the AncestorRef and the ControllerName. - properties: - ancestorRef: - description: |- - AncestorRef corresponds with a ParentRef in the spec that this - PolicyAncestorStatus struct describes the status of. - properties: - group: - default: gateway.networking.k8s.io - description: |- - Group is the group of the referent. - When unspecified, "gateway.networking.k8s.io" is inferred. - To set the core API group (such as for a "Service" kind referent), - Group must be explicitly set to "" (empty string). - - Support: Core - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - default: Gateway - description: |- - Kind is kind of the referent. - - There are two kinds of parent resources with "Core" support: - - * Gateway (Gateway conformance profile) - * Service (Mesh conformance profile, ClusterIP Services only) - - Support for other resources is Implementation-Specific. - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: |- - Name is the name of the referent. - - Support: Core - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - Namespace is the namespace of the referent. When unspecified, this refers - to the local namespace of the Route. - - Note that there are specific rules for ParentRefs which cross namespace - boundaries. Cross-namespace references are only valid if they are explicitly - allowed by something in the namespace they are referring to. For example: - Gateway has the AllowedRoutes field, and ReferenceGrant provides a - generic way to enable any other kind of cross-namespace reference. - - - ParentRefs from a Route to a Service in the same namespace are "producer" - routes, which apply default routing rules to inbound connections from - any namespace to the Service. - - ParentRefs from a Route to a Service in a different namespace are - "consumer" routes, and these routing rules are only applied to outbound - connections originating from the same namespace as the Route, for which - the intended destination of the connections are a Service targeted as a - ParentRef of the Route. - - - Support: Core - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - port: - description: |- - Port is the network port this Route targets. It can be interpreted - differently based on the type of parent resource. - - When the parent resource is a Gateway, this targets all listeners - listening on the specified port that also support this kind of Route(and - select this Route). It's not recommended to set `Port` unless the - networking behaviors specified in a Route must apply to a specific port - as opposed to a listener(s) whose port(s) may be changed. When both Port - and SectionName are specified, the name and port of the selected listener - must match both specified values. - - - When the parent resource is a Service, this targets a specific port in the - Service spec. When both Port (experimental) and SectionName are specified, - the name and port of the selected port must match both specified values. - - - Implementations MAY choose to support other parent resources. - Implementations supporting other types of parent resources MUST clearly - document how/if Port is interpreted. - - For the purpose of status, an attachment is considered successful as - long as the parent resource accepts it partially. For example, Gateway - listeners can restrict which Routes can attach to them by Route kind, - namespace, or hostname. If 1 of 2 Gateway listeners accept attachment - from the referencing Route, the Route MUST be considered successfully - attached. If no Gateway listeners accept attachment from this Route, - the Route MUST be considered detached from the Gateway. - - Support: Extended - format: int32 - maximum: 65535 - minimum: 1 - type: integer - sectionName: - description: |- - 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 - are specified, the name and port of the selected listener must match - both specified values. - * Service: Port name. When both Port (experimental) and SectionName - are specified, the name and port of the selected listener must match - 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 - interpreted. - - When unspecified (empty string), this will reference the entire resource. - For the purpose of status, an attachment is considered successful if at - least one section in the parent resource accepts it. For example, Gateway - listeners can restrict which Routes can attach to them by Route kind, - namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from - the referencing Route, the Route MUST be considered successfully - attached. If no Gateway listeners accept attachment from this Route, the - Route MUST be considered detached from the Gateway. - - Support: Core - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - required: - - name - type: object - conditions: - description: Conditions describes the status of the Policy with - respect to the given Ancestor. - items: - description: Condition contains details for one aspect of - the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, - Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - maxItems: 8 - minItems: 1 - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - controllerName: - description: |- - ControllerName is a domain/path string that indicates the name of the - controller that wrote this status. This corresponds with the - controllerName field on GatewayClass. - - Example: "example.net/gateway-controller". - - The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are - valid Kubernetes names - (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). - - 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. - maxLength: 253 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ - type: string - required: - - ancestorRef - - controllerName - type: object - maxItems: 16 - type: array - required: - - ancestors - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: null - storedVersions: null ---- -# # config/crd/experimental/gateway.networking.k8s.io_backendtlspolicies.yaml # apiVersion: apiextensions.k8s.io/v1 @@ -525,9 +24,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.2.1 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null labels: gateway.networking.k8s.io/policy: Direct name: backendtlspolicies.gateway.networking.k8s.io @@ -548,7 +46,7 @@ spec: - jsonPath: .metadata.creationTimestamp name: Age type: date - name: v1alpha3 + name: v1 schema: openAPIV3Schema: description: |- @@ -607,6 +105,30 @@ spec: by default, but this default may change in the future to provide a more granular application of the policy. + TargetRefs must be _distinct_. This means either that: + + * They select different targets. If this is the case, then targetRef + entries are distinct. In terms of fields, this means that the + multi-part key defined by `group`, `kind`, and `name` must + be unique across all targetRef entries in the BackendTLSPolicy. + * They select different sectionNames in the same target. + + When more than one BackendTLSPolicy selects the same target and + sectionName, implementations MUST determine precedence using the + following criteria, continuing on ties: + + * The older policy by creation timestamp takes precedence. For + example, a policy with a creation timestamp of "2021-07-15 + 01:02:03" MUST be given precedence over a policy with a + creation timestamp of "2021-07-15 01:02:04". + * The policy appearing first in alphabetical order by {name}. + For example, a policy named `bar` is given precedence over a + policy named `baz`. + + For any BackendTLSPolicy that does not take precedence, the + implementation MUST ensure the `Accepted` Condition is set to + `status: False`, with Reason `Conflicted`. + Support: Extended for Kubernetes Service Support: Implementation-specific for any other resource @@ -663,6 +185,21 @@ spec: maxItems: 16 minItems: 1 type: array + x-kubernetes-list-type: atomic + x-kubernetes-validations: + - message: sectionName must be specified when targetRefs includes + 2 or more references to the same target + rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name ? ((!has(p1.sectionName) || p1.sectionName + == '''') == (!has(p2.sectionName) || p2.sectionName == '''')) + : true))' + - message: sectionName must be unique when targetRefs includes 2 or + more references to the same target + rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name && (((!has(p1.sectionName) || + p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName + == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName + == p2.sectionName)))) validation: description: Validation contains backend TLS validation configuration. properties: @@ -674,11 +211,34 @@ spec: If CACertificateRefs is empty or unspecified, then WellKnownCACertificates must be specified. Only one of CACertificateRefs or WellKnownCACertificates may be specified, - not both. If CACertifcateRefs is empty or unspecified, the configuration for + not both. If CACertificateRefs is empty or unspecified, the configuration for WellKnownCACertificates MUST be honored instead if supported by the implementation. - References to a resource in a different namespace are invalid for the - moment, although we will revisit this in the future. + A CACertificateRef is invalid if: + + * It refers to a resource that cannot be resolved (e.g., the referenced resource + does not exist) or is misconfigured (e.g., a ConfigMap does not contain a key + named `ca.crt`). In this case, the Reason must be set to `InvalidCACertificateRef` + and the Message of the Condition must indicate which reference is invalid and why. + + * It refers to an unknown or unsupported kind of resource. In this case, the Reason + must be set to `InvalidKind` and the Message of the Condition must explain which + kind of resource is unknown or unsupported. + + * It refers to a resource in another namespace. This may change in future + spec updates. + + Implementations MAY choose to perform further validation of the certificate + content (e.g., checking expiry or enforcing specific formats). In such cases, + an implementation-specific Reason and Message must be set for the invalid reference. + + In all cases, the implementation MUST ensure the `ResolvedRefs` Condition on + the BackendTLSPolicy is set to `status: False`, with a Reason and Message + that indicate the cause of the error. Connections using an invalid + CACertificateRef MUST fail, and the client MUST receive an HTTP 5xx error + response. If ALL CACertificateRefs are invalid, the implementation MUST also + ensure the `Accepted` Condition on the BackendTLSPolicy is set to + `status: False`, with a Reason `NoValidCACertificate`. A single CACertificateRef to a Kubernetes ConfigMap kind has "Core" support. Implementations MAY choose to support attaching multiple certificates to @@ -687,8 +247,8 @@ spec: Support: Core - An optional single reference to a Kubernetes ConfigMap, with the CA certificate in a key named `ca.crt`. - Support: Implementation-specific (More than one reference, or other kinds - of resources). + Support: Implementation-specific - More than one reference, other kinds + of resources, or a single reference that includes multiple certificates. items: description: |- LocalObjectReference identifies an API object within the namespace of the @@ -726,15 +286,18 @@ spec: type: object maxItems: 8 type: array + x-kubernetes-list-type: atomic hostname: description: |- Hostname is used for two purposes in the connection between Gateways and backends: 1. Hostname MUST be used as the SNI to connect to the backend (RFC 6066). - 2. If SubjectAltNames is not specified, Hostname MUST be used for - authentication and MUST match the certificate served by the matching - backend. + 2. Hostname MUST be used for authentication and MUST match the certificate + served by the matching backend, unless SubjectAltNames is specified. + 3. If SubjectAltNames are specified, Hostname can be used for certificate selection + but MUST NOT be used for authentication. If you want to use the value + of the Hostname field for authentication, you MUST add it to the SubjectAltNames list. Support: Core maxLength: 253 @@ -744,10 +307,10 @@ spec: subjectAltNames: description: |- SubjectAltNames contains one or more Subject Alternative Names. - When specified, the certificate served from the backend MUST have at least one - Subject Alternate Name matching one of the specified SubjectAltNames. + When specified the certificate served from the backend MUST + have at least one Subject Alternate Name matching one of the specified SubjectAltNames. - Support: Core + Support: Extended items: description: SubjectAltName represents Subject Alternative Name. properties: @@ -804,6 +367,7 @@ spec: "")' maxItems: 5 type: array + x-kubernetes-list-type: atomic wellKnownCACertificates: description: |- WellKnownCACertificates specifies whether system CA certificates may be used in @@ -811,10 +375,11 @@ spec: If WellKnownCACertificates is unspecified or empty (""), then CACertificateRefs must be specified with at least one entry for a valid configuration. Only one of - CACertificateRefs or WellKnownCACertificates may be specified, not both. If an - implementation does not support the WellKnownCACertificates field or the value - supplied is not supported, the Status Conditions on the Policy MUST be - updated to include an Accepted: False Condition with Reason: Invalid. + CACertificateRefs or WellKnownCACertificates may be specified, not both. + If an implementation does not support the WellKnownCACertificates field, or + the supplied value is not recognized, the implementation MUST ensure the + `Accepted` Condition on the BackendTLSPolicy is set to `status: False`, with + a Reason `Invalid`. Support: Implementation-specific enum: @@ -1125,10 +690,12 @@ spec: type: string required: - ancestorRef + - conditions - controllerName type: object maxItems: 16 type: array + x-kubernetes-list-type: atomic required: - ancestors type: object @@ -1139,6 +706,667 @@ spec: storage: true subresources: status: {} + - deprecated: true + deprecationWarning: The v1alpha3 version of BackendTLSPolicy has been deprecated + and will be removed in a future release of the API. Please upgrade to v1. + name: v1alpha3 + schema: + openAPIV3Schema: + description: |- + BackendTLSPolicy provides a way to configure how a Gateway + connects to a Backend via TLS. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of BackendTLSPolicy. + properties: + options: + additionalProperties: + description: |- + AnnotationValue is the value of an annotation in Gateway API. This is used + for validation of maps such as TLS options. This roughly matches Kubernetes + annotation validation, although the length validation in that case is based + on the entire size of the annotations struct. + maxLength: 4096 + minLength: 0 + type: string + description: |- + Options are a list of key/value pairs to enable extended TLS + configuration for each implementation. For example, configuring the + minimum TLS version or supported cipher suites. + + A set of common keys MAY be defined by the API in the future. To avoid + any ambiguity, implementation-specific definitions MUST use + domain-prefixed names, such as `example.com/my-custom-option`. + Un-prefixed names are reserved for key names defined by Gateway API. + + Support: Implementation-specific + maxProperties: 16 + type: object + targetRefs: + description: |- + TargetRefs identifies an API object to apply the policy to. + Only Services have Extended support. Implementations MAY support + additional objects, with Implementation Specific support. + Note that this config applies to the entire referenced resource + by default, but this default may change in the future to provide + a more granular application of the policy. + + TargetRefs must be _distinct_. This means either that: + + * They select different targets. If this is the case, then targetRef + entries are distinct. In terms of fields, this means that the + multi-part key defined by `group`, `kind`, and `name` must + be unique across all targetRef entries in the BackendTLSPolicy. + * They select different sectionNames in the same target. + + When more than one BackendTLSPolicy selects the same target and + sectionName, implementations MUST determine precedence using the + following criteria, continuing on ties: + + * The older policy by creation timestamp takes precedence. For + example, a policy with a creation timestamp of "2021-07-15 + 01:02:03" MUST be given precedence over a policy with a + creation timestamp of "2021-07-15 01:02:04". + * The policy appearing first in alphabetical order by {name}. + For example, a policy named `bar` is given precedence over a + policy named `baz`. + + For any BackendTLSPolicy that does not take precedence, the + implementation MUST ensure the `Accepted` Condition is set to + `status: False`, with Reason `Conflicted`. + + Support: Extended for Kubernetes Service + + Support: Implementation-specific for any other resource + items: + description: |- + LocalPolicyTargetReferenceWithSectionName identifies an API object to apply a + direct policy to. This should be used as part of Policy resources that can + target single resources. For more information on how this policy attachment + mode works, and a sample Policy resource, refer to the policy attachment + documentation for Gateway API. + + Note: This should only be used for direct policy attachment when references + to SectionName are actually needed. In all other cases, + LocalPolicyTargetReference should be used. + properties: + group: + description: Group is the group of the target resource. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the target resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the target resource. + maxLength: 253 + minLength: 1 + type: string + sectionName: + description: |- + SectionName is the name of a section within the target resource. When + unspecified, this targetRef targets the entire resource. In the following + resources, SectionName is interpreted as the following: + + * Gateway: Listener name + * HTTPRoute: HTTPRouteRule name + * Service: Port name + + If a SectionName is specified, but does not exist on the targeted object, + the Policy must fail to attach, and the policy implementation should record + a `ResolvedRefs` or similar Condition in the Policy's status. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - group + - kind + - name + type: object + maxItems: 16 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + x-kubernetes-validations: + - message: sectionName must be specified when targetRefs includes + 2 or more references to the same target + rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name ? ((!has(p1.sectionName) || p1.sectionName + == '''') == (!has(p2.sectionName) || p2.sectionName == '''')) + : true))' + - message: sectionName must be unique when targetRefs includes 2 or + more references to the same target + rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind + == p2.kind && p1.name == p2.name && (((!has(p1.sectionName) || + p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName + == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName + == p2.sectionName)))) + validation: + description: Validation contains backend TLS validation configuration. + properties: + caCertificateRefs: + description: |- + CACertificateRefs contains one or more references to Kubernetes objects that + contain a PEM-encoded TLS CA certificate bundle, which is used to + validate a TLS handshake between the Gateway and backend Pod. + + If CACertificateRefs is empty or unspecified, then WellKnownCACertificates must be + specified. Only one of CACertificateRefs or WellKnownCACertificates may be specified, + not both. If CACertificateRefs is empty or unspecified, the configuration for + WellKnownCACertificates MUST be honored instead if supported by the implementation. + + A CACertificateRef is invalid if: + + * It refers to a resource that cannot be resolved (e.g., the referenced resource + does not exist) or is misconfigured (e.g., a ConfigMap does not contain a key + named `ca.crt`). In this case, the Reason must be set to `InvalidCACertificateRef` + and the Message of the Condition must indicate which reference is invalid and why. + + * It refers to an unknown or unsupported kind of resource. In this case, the Reason + must be set to `InvalidKind` and the Message of the Condition must explain which + kind of resource is unknown or unsupported. + + * It refers to a resource in another namespace. This may change in future + spec updates. + + Implementations MAY choose to perform further validation of the certificate + content (e.g., checking expiry or enforcing specific formats). In such cases, + an implementation-specific Reason and Message must be set for the invalid reference. + + In all cases, the implementation MUST ensure the `ResolvedRefs` Condition on + the BackendTLSPolicy is set to `status: False`, with a Reason and Message + that indicate the cause of the error. Connections using an invalid + CACertificateRef MUST fail, and the client MUST receive an HTTP 5xx error + response. If ALL CACertificateRefs are invalid, the implementation MUST also + ensure the `Accepted` Condition on the BackendTLSPolicy is set to + `status: False`, with a Reason `NoValidCACertificate`. + + A single CACertificateRef to a Kubernetes ConfigMap kind has "Core" support. + Implementations MAY choose to support attaching multiple certificates to + a backend, but this behavior is implementation-specific. + + Support: Core - An optional single reference to a Kubernetes ConfigMap, + with the CA certificate in a key named `ca.crt`. + + Support: Implementation-specific - More than one reference, other kinds + of resources, or a single reference that includes multiple certificates. + items: + description: |- + LocalObjectReference identifies an API object within the namespace of the + referrer. + 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. + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For example "HTTPRoute" + or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + maxItems: 8 + type: array + x-kubernetes-list-type: atomic + hostname: + description: |- + Hostname is used for two purposes in the connection between Gateways and + backends: + + 1. Hostname MUST be used as the SNI to connect to the backend (RFC 6066). + 2. Hostname MUST be used for authentication and MUST match the certificate + served by the matching backend, unless SubjectAltNames is specified. + 3. If SubjectAltNames are specified, Hostname can be used for certificate selection + but MUST NOT be used for authentication. If you want to use the value + of the Hostname field for authentication, you MUST add it to the SubjectAltNames list. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + subjectAltNames: + description: |- + SubjectAltNames contains one or more Subject Alternative Names. + When specified the certificate served from the backend MUST + have at least one Subject Alternate Name matching one of the specified SubjectAltNames. + + Support: Extended + items: + description: SubjectAltName represents Subject Alternative Name. + properties: + hostname: + description: |- + Hostname contains Subject Alternative Name specified in DNS name format. + Required when Type is set to Hostname, ignored otherwise. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + type: + description: |- + Type determines the format of the Subject Alternative Name. Always required. + + Support: Core + enum: + - Hostname + - URI + type: string + uri: + description: |- + URI contains Subject Alternative Name specified in a full URI format. + It MUST include both a scheme (e.g., "http" or "ftp") and a scheme-specific-part. + Common values include SPIFFE IDs like "spiffe://mycluster.example.com/ns/myns/sa/svc1sa". + Required when Type is set to URI, ignored otherwise. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))? + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: SubjectAltName element must contain Hostname, if + Type is set to Hostname + rule: '!(self.type == "Hostname" && (!has(self.hostname) || + self.hostname == ""))' + - message: SubjectAltName element must not contain Hostname, + if Type is not set to Hostname + rule: '!(self.type != "Hostname" && has(self.hostname) && + self.hostname != "")' + - message: SubjectAltName element must contain URI, if Type + is set to URI + rule: '!(self.type == "URI" && (!has(self.uri) || self.uri + == ""))' + - message: SubjectAltName element must not contain URI, if Type + is not set to URI + rule: '!(self.type != "URI" && has(self.uri) && self.uri != + "")' + maxItems: 5 + type: array + x-kubernetes-list-type: atomic + wellKnownCACertificates: + description: |- + WellKnownCACertificates specifies whether system CA certificates may be used in + the TLS handshake between the gateway and backend pod. + + If WellKnownCACertificates is unspecified or empty (""), then CACertificateRefs + must be specified with at least one entry for a valid configuration. Only one of + CACertificateRefs or WellKnownCACertificates may be specified, not both. + If an implementation does not support the WellKnownCACertificates field, or + the supplied value is not recognized, the implementation MUST ensure the + `Accepted` Condition on the BackendTLSPolicy is set to `status: False`, with + a Reason `Invalid`. + + Support: Implementation-specific + enum: + - System + type: string + required: + - hostname + type: object + x-kubernetes-validations: + - message: must not contain both CACertificateRefs and WellKnownCACertificates + rule: '!(has(self.caCertificateRefs) && size(self.caCertificateRefs) + > 0 && has(self.wellKnownCACertificates) && self.wellKnownCACertificates + != "")' + - message: must specify either CACertificateRefs or WellKnownCACertificates + rule: (has(self.caCertificateRefs) && size(self.caCertificateRefs) + > 0 || has(self.wellKnownCACertificates) && self.wellKnownCACertificates + != "") + required: + - targetRefs + - validation + type: object + status: + description: Status defines the current state of BackendTLSPolicy. + properties: + ancestors: + description: |- + Ancestors is a list of ancestor resources (usually Gateways) that are + associated with the policy, and the status of the policy with respect to + each ancestor. When this policy attaches to a parent, the controller that + manages the parent and the ancestors MUST add an entry to this list when + the controller first sees the policy and SHOULD update the entry as + appropriate when the relevant ancestor is modified. + + Note that choosing the relevant ancestor is left to the Policy designers; + an important part of Policy design is designing the right object level at + which to namespace this status. + + Note also that implementations MUST ONLY populate ancestor status for + the Ancestor resources they are responsible for. Implementations MUST + use the ControllerName field to uniquely identify the entries in this list + that they are responsible for. + + Note that to achieve this, the list of PolicyAncestorStatus structs + MUST be treated as a map with a composite key, made up of the AncestorRef + and ControllerName fields combined. + + A maximum of 16 ancestors will be represented in this list. An empty list + means the Policy is not relevant for any ancestors. + + If this slice is full, implementations MUST NOT add further entries. + Instead they MUST consider the policy unimplementable and signal that + on any related resources such as the ancestor that would be referenced + here. For example, if this list was full on BackendTLSPolicy, no + additional Gateways would be able to reference the Service targeted by + the BackendTLSPolicy. + items: + description: |- + PolicyAncestorStatus describes the status of a route with respect to an + associated Ancestor. + + Ancestors refer to objects that are either the Target of a policy or above it + in terms of object hierarchy. For example, if a policy targets a Service, the + Policy's Ancestors are, in order, the Service, the HTTPRoute, the Gateway, and + the GatewayClass. Almost always, in this hierarchy, the Gateway will be the most + useful object to place Policy status on, so we recommend that implementations + SHOULD use Gateway as the PolicyAncestorStatus object unless the designers + have a _very_ good reason otherwise. + + In the context of policy attachment, the Ancestor is used to distinguish which + resource results in a distinct application of this policy. For example, if a policy + targets a Service, it may have a distinct result per attached Gateway. + + Policies targeting the same resource may have different effects depending on the + ancestors of those resources. For example, different Gateways targeting the same + Service may have different capabilities, especially if they have different underlying + implementations. + + For example, in BackendTLSPolicy, the Policy attaches to a Service that is + used as a backend in a HTTPRoute that is itself attached to a Gateway. + In this case, the relevant object for status is the Gateway, and that is the + ancestor object referred to in this status. + + Note that a parent is also an ancestor, so for objects where the parent is the + relevant object for status, this struct SHOULD still be used. + + This struct is intended to be used in a slice that's effectively a map, + with a composite key made up of the AncestorRef and the ControllerName. + properties: + ancestorRef: + description: |- + AncestorRef corresponds with a ParentRef in the spec that this + PolicyAncestorStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + When the parent resource is a Service, this targets a specific port in the + Service spec. When both Port (experimental) and SectionName are specified, + the name and port of the selected port must match both specified values. + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + 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 + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + 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 + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + conditions: + description: Conditions describes the status of the Policy with + respect to the given Ancestor. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: |- + ControllerName is a domain/path string that indicates the name of the + controller that wrote this status. This corresponds with the + controllerName field on GatewayClass. + + Example: "example.net/gateway-controller". + + The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are + valid Kubernetes names + (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + + 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. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + required: + - ancestorRef + - conditions + - controllerName + type: object + maxItems: 16 + type: array + x-kubernetes-list-type: atomic + required: + - ancestors + type: object + required: + - spec + type: object + served: true + storage: false status: acceptedNames: kind: "" @@ -1154,9 +1382,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.2.1 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null name: gatewayclasses.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -1389,7 +1616,7 @@ spec: - type x-kubernetes-list-type: map supportedFeatures: - description: | + description: |- SupportedFeatures is the set of features the GatewayClass support. It MUST be sorted in ascending alphabetical order by the Name key. items: @@ -1633,7 +1860,7 @@ spec: - type x-kubernetes-list-type: map supportedFeatures: - description: | + description: |- SupportedFeatures is the set of features the GatewayClass support. It MUST be sorted in ascending alphabetical order by the Name key. items: @@ -1674,9 +1901,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.2.1 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null name: gateways.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -1732,11 +1958,11 @@ spec: description: Spec defines the desired state of Gateway. properties: addresses: - description: |+ + description: |- Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST - indicate this in the associated entry in GatewayStatus.Addresses. + indicate this in an associated entry in GatewayStatus.Conditions. The Addresses field represents a request for the address(es) on the "outside of the Gateway", that traffic bound for this Gateway will use. @@ -1753,10 +1979,9 @@ spec: GatewayStatus.Addresses. Support: Extended - items: - description: GatewayAddress describes an address that can be bound - to a Gateway. + description: GatewaySpecAddress describes an address that can be + bound to a Gateway. oneOf: - properties: type: @@ -1781,96 +2006,137 @@ spec: type: string value: description: |- - Value of the address. The validity of the values will depend - on the type and support by the controller. + When a value is unspecified, an implementation SHOULD automatically + assign an address matching the requested type if possible. + + If an implementation does not support an empty value, they MUST set the + "Programmed" condition in status to False with a reason of "AddressNotAssigned". Examples: `1.2.3.4`, `128::1`, `my-ip-address`. maxLength: 253 - minLength: 1 type: string - required: - - value type: object x-kubernetes-validations: - - message: Hostname value must only contain valid characters (matching - ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) - rule: 'self.type == ''Hostname'' ? self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"""): + - message: Hostname value must be empty or contain only valid characters + (matching ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) + rule: 'self.type == ''Hostname'' ? (!has(self.value) || self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$""")): true' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: IPAddress values must be unique - rule: 'self.all(a1, a1.type == ''IPAddress'' ? self.exists_one(a2, - a2.type == a1.type && a2.value == a1.value) : true )' + rule: 'self.all(a1, a1.type == ''IPAddress'' && has(a1.value) ? + self.exists_one(a2, a2.type == a1.type && has(a2.value) && a2.value + == a1.value) : true )' - message: Hostname values must be unique - rule: 'self.all(a1, a1.type == ''Hostname'' ? self.exists_one(a2, - a2.type == a1.type && a2.value == a1.value) : true )' - backendTLS: - description: |+ - BackendTLS configures TLS settings for when this Gateway is connecting to - backends with TLS. - - Support: Core - + rule: 'self.all(a1, a1.type == ''Hostname'' && has(a1.value) ? + self.exists_one(a2, a2.type == a1.type && has(a2.value) && a2.value + == a1.value) : true )' + allowedListeners: + description: |- + AllowedListeners defines which ListenerSets can be attached to this Gateway. + While this feature is experimental, the default value is to allow no ListenerSets. properties: - clientCertificateRef: - description: |+ - ClientCertificateRef is a reference to an object that contains a Client - Certificate and the associated private key. - - References to a resource in different namespace are invalid UNLESS there - is a ReferenceGrant in the target namespace that allows the certificate - to be attached. If a ReferenceGrant does not allow this reference, the - "ResolvedRefs" condition MUST be set to False for this listener with the - "RefNotPermitted" reason. - - ClientCertificateRef can reference to standard Kubernetes resources, i.e. - Secret, or implementation-specific custom resources. - - This setting can be overridden on the service level by use of BackendTLSPolicy. - - Support: Core - + namespaces: + default: + from: None + description: |- + Namespaces defines which namespaces ListenerSets can be attached to this Gateway. + While this feature is experimental, the default value is to allow no ListenerSets. properties: - group: - default: "" + from: + default: None description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + From indicates where ListenerSets can attach to this Gateway. Possible + values are: + + * Same: Only ListenerSets in the same namespace may be attached to this Gateway. + * Selector: ListenerSets in namespaces selected by the selector may be attached to this Gateway. + * All: ListenerSets in all namespaces may be attached to this Gateway. + * None: Only listeners defined in the Gateway's spec are allowed + + While this feature is experimental, the default value None + enum: + - All + - Selector + - Same + - None type: string - kind: - default: Secret - description: Kind is kind of the referent. For example "Secret". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: + selector: description: |- - 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 - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - name + Selector must be specified when From is set to "Selector". In that case, + only ListenerSets in Namespaces matching this Selector will be selected by this + Gateway. This field is ignored for other values of "From". + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object type: object + defaultScope: + description: |- + DefaultScope, when set, configures the Gateway as a default Gateway, + meaning it will dynamically and implicitly have Routes (e.g. HTTPRoute) + attached to it, according to the scope configured here. + + If unset (the default) or set to None, the Gateway will not act as a + default Gateway; if set, the Gateway will claim any Route with a + matching scope set in its UseDefaultGateway field, subject to the usual + rules about which routes the Gateway can attach to. + + Think carefully before using this functionality! While the normal rules + about which Route can apply are still enforced, it is simply easier for + the wrong Route to be accidentally attached to this Gateway in this + configuration. If the Gateway operator is not also the operator in + control of the scope (e.g. namespace) with tight controls and checks on + what kind of workloads and Routes get added in that scope, we strongly + recommend not using this just because it seems convenient, and instead + stick to direct Route attachment. + enum: + - All + - None + type: string gatewayClassName: description: |- GatewayClassName used for this Gateway. This is the name of a @@ -1965,6 +2231,11 @@ spec: the merging behavior is implementation specific. It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. + If the referent cannot be found, refers to an unsupported kind, or when + the data within that resource is malformed, the Gateway SHOULD be + rejected with the "Accepted" status condition set to "False" and an + "InvalidParameters" reason. + Support: Implementation-specific properties: group: @@ -1995,6 +2266,8 @@ spec: logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. + ## Distinct Listeners + Each Listener in a set of Listeners (for example, in a single Gateway) MUST be _distinct_, in that a traffic flow MUST be able to be assigned to exactly one listener. (This section uses "set of Listeners" rather than @@ -2006,55 +2279,76 @@ spec: combination of Port, Protocol, and, if supported by the protocol, Hostname. Some combinations of port, protocol, and TLS settings are considered - Core support and MUST be supported by implementations based on their - targeted conformance profile: + Core support and MUST be supported by implementations based on the objects + they support: - HTTP Profile + HTTPRoute 1. HTTPRoute, Port: 80, Protocol: HTTP 2. HTTPRoute, Port: 443, Protocol: HTTPS, TLS Mode: Terminate, TLS keypair provided - TLS Profile + TLSRoute 1. TLSRoute, Port: 443, Protocol: TLS, TLS Mode: Passthrough "Distinct" Listeners have the following property: - The implementation can match inbound requests to a single distinct - Listener. When multiple Listeners share values for fields (for + **The implementation can match inbound requests to a single distinct + Listener**. + + When multiple Listeners share values for fields (for example, two Listeners with the same Port value), the implementation can match requests to only one of the Listeners using other Listener fields. - For example, the following Listener scenarios are distinct: + When multiple listeners have the same value for the Protocol field, then + each of the Listeners with matching Protocol values MUST have different + values for other fields. - 1. Multiple Listeners with the same Port that all use the "HTTP" - Protocol that all have unique Hostname values. - 2. Multiple Listeners with the same Port that use either the "HTTPS" or - "TLS" Protocol that all have unique Hostname values. - 3. A mixture of "TCP" and "UDP" Protocol Listeners, where no Listener - with the same Protocol has the same Port value. + The set of fields that MUST be different for a Listener differs per protocol. + The following rules define the rules for what fields MUST be considered for + Listeners to be distinct with each protocol currently defined in the + Gateway API spec. - Some fields in the Listener struct have possible values that affect - whether the Listener is distinct. Hostname is particularly relevant - for HTTP or HTTPS protocols. + The set of listeners that all share a protocol value MUST have _different_ + values for _at least one_ of these fields to be distinct: - When using the Hostname value to select between same-Port, same-Protocol - Listeners, the Hostname value must be different on each Listener for the - Listener to be distinct. + * **HTTP, HTTPS, TLS**: Port, Hostname + * **TCP, UDP**: Port - When the Listeners are distinct based on Hostname, inbound request + One **very** important rule to call out involves what happens when an + implementation: + + * Supports TCP protocol Listeners, as well as HTTP, HTTPS, or TLS protocol + Listeners, and + * sees HTTP, HTTPS, or TLS protocols with the same `port` as one with TCP + Protocol. + + In this case all the Listeners that share a port with the + TCP Listener are not distinct and so MUST NOT be accepted. + + If an implementation does not support TCP Protocol Listeners, then the + previous rule does not apply, and the TCP Listeners SHOULD NOT be + accepted. + + Note that the `tls` field is not used for determining if a listener is distinct, because + Listeners that _only_ differ on TLS config will still conflict in all cases. + + ### Listeners that are distinct only by Hostname + + When the Listeners are distinct based only on Hostname, inbound request hostnames MUST match from the most specific to least specific Hostname values to choose the correct Listener and its associated set of Routes. - Exact matches must be processed before wildcard matches, and wildcard - matches must be processed before fallback (empty Hostname value) + Exact matches MUST be processed before wildcard matches, and wildcard + matches MUST be processed before fallback (empty Hostname value) matches. For example, `"foo.example.com"` takes precedence over `"*.example.com"`, and `"*.example.com"` takes precedence over `""`. Additionally, if there are multiple wildcard entries, more specific wildcard entries must be processed before less specific wildcard entries. For example, `"*.foo.example.com"` takes precedence over `"*.example.com"`. + The precise definition here is that the higher the number of dots in the hostname to the right of the wildcard character, the higher the precedence. @@ -2062,18 +2356,26 @@ spec: the left, however, so `"*.example.com"` will match both `"foo.bar.example.com"` _and_ `"bar.example.com"`. + ## Handling indistinct Listeners + If a set of Listeners contains Listeners that are not distinct, then those - Listeners are Conflicted, and the implementation MUST set the "Conflicted" + Listeners are _Conflicted_, and the implementation MUST set the "Conflicted" condition in the Listener Status to "True". + The words "indistinct" and "conflicted" are considered equivalent for the + purpose of this documentation. + Implementations MAY choose to accept a Gateway with some Conflicted Listeners only if they only accept the partial Listener set that contains - no Conflicted Listeners. To put this another way, implementations may - accept a partial Listener set only if they throw out *all* the conflicting - Listeners. No picking one of the conflicting listeners as the winner. - This also means that the Gateway must have at least one non-conflicting - Listener in this case, otherwise it violates the requirement that at - least one Listener must be present. + no Conflicted Listeners. + + Specifically, an implementation MAY accept a partial Listener set subject to + the following rules: + + * The implementation MUST NOT pick one conflicting Listener as the winner. + ALL indistinct Listeners must not be accepted for processing. + * At least one distinct Listener MUST be present, or else the Gateway effectively + contains _no_ Listeners, and must be rejected from processing as a whole. The implementation MUST set a "ListenersNotValid" condition on the Gateway Status when the Gateway contains Conflicted Listeners whether or @@ -2082,7 +2384,25 @@ spec: Accepted. Additionally, the Listener status for those listeners SHOULD indicate which Listeners are conflicted and not Accepted. - A Gateway's Listeners are considered "compatible" if: + ## General Listener behavior + + Note that, for all distinct Listeners, requests SHOULD match at most one Listener. + For example, if Listeners are defined for "foo.example.com" and "*.example.com", a + request to "foo.example.com" SHOULD only be routed using routes attached + to the "foo.example.com" Listener (and not the "*.example.com" Listener). + + This concept is known as "Listener Isolation", and it is an Extended feature + of Gateway API. Implementations that do not support Listener Isolation MUST + clearly document this, and MUST NOT claim support for the + `GatewayHTTPListenerIsolation` feature. + + Implementations that _do_ support Listener Isolation SHOULD claim support + for the Extended `GatewayHTTPListenerIsolation` feature and pass the associated + conformance tests. + + ## Compatible Listeners + + A Gateway's Listeners are considered _compatible_ if: 1. They are distinct. 2. The implementation can serve them in compliance with the Addresses @@ -2097,16 +2417,11 @@ spec: on the same address, or cannot mix HTTPS and generic TLS listens on the same port would not consider those cases compatible, even though they are distinct. - Note that requests SHOULD match at most one Listener. For example, if - Listeners are defined for "foo.example.com" and "*.example.com", a - request to "foo.example.com" SHOULD only be routed using routes attached - to the "foo.example.com" Listener (and not the "*.example.com" Listener). - This concept is known as "Listener Isolation". Implementations that do - not support Listener Isolation MUST clearly document this. - Implementations MAY merge separate Gateways onto a single set of Addresses if all Listeners across all Gateways are compatible. + In a future release the MinItems=1 requirement MAY be dropped. + Support: Core items: description: |- @@ -2177,6 +2492,7 @@ spec: type: object maxItems: 8 type: array + x-kubernetes-list-type: atomic namespaces: default: from: Same @@ -2268,10 +2584,31 @@ spec: * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. - * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP - protocol layers as described above. If an implementation does not - ensure that both the SNI and Host header match the Listener hostname, - it MUST clearly document that. + * HTTPS: The Listener Hostname SHOULD match both the SNI and Host header. + Note that this does not require the SNI and Host header to be the same. + The semantics of this are described in more detail below. + + To ensure security, Section 11.1 of RFC-6066 emphasizes that server + implementations that rely on SNI hostname matching MUST also verify + hostnames within the application protocol. + + Section 9.1.2 of RFC-7540 provides a mechanism for servers to reject the + reuse of a connection by responding with the HTTP 421 Misdirected Request + status code. This indicates that the origin server has rejected the + request because it appears to have been misdirected. + + To detect misdirected requests, Gateways SHOULD match the authority of + the requests with all the SNI hostname(s) configured across all the + Gateway Listeners on the same port and protocol: + + * If another Listener has an exact match or more specific wildcard entry, + the Gateway SHOULD return a 421. + * If the current Listener (selected by SNI matching during ClientHello) + does not match the Host: + * If another Listener does match the Host the Gateway SHOULD return a + 421. + * If no other Listener matches the Host, the Gateway MUST return a + 404. For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, @@ -2323,7 +2660,7 @@ spec: the Protocol field is "HTTPS" or "TLS". It is invalid to set this field if the Protocol field is "HTTP", "TCP", or "UDP". - The association of SNIs to Certificate defined in GatewayTLSConfig is + The association of SNIs to Certificate defined in ListenerTLSConfig is defined based on the Hostname field for this listener. The GatewayClass MUST use the longest matching SNI out of all @@ -2410,94 +2747,7 @@ spec: type: object maxItems: 64 type: array - frontendValidation: - description: |+ - FrontendValidation holds configuration information for validating the frontend (client). - Setting this field will require clients to send a client certificate - required for validation during the TLS handshake. In browsers this may result in a dialog appearing - that requests a user to specify the client certificate. - The maximum depth of a certificate chain accepted in verification is Implementation specific. - - Support: Extended - - properties: - caCertificateRefs: - description: |- - CACertificateRefs contains one or more references to - Kubernetes objects that contain TLS certificates of - the Certificate Authorities that can be used - as a trust anchor to validate the certificates presented by the client. - - A single CA certificate reference to a Kubernetes ConfigMap - has "Core" support. - Implementations MAY choose to support attaching multiple CA certificates to - a Listener, but this behavior is implementation-specific. - - Support: Core - A single reference to a Kubernetes ConfigMap - with the CA certificate in a key named `ca.crt`. - - Support: Implementation-specific (More than one reference, or other kinds - of resources). - - References to a resource in a different namespace are invalid UNLESS there - is a ReferenceGrant in the target namespace that allows the certificate - to be attached. If a ReferenceGrant does not allow this reference, the - "ResolvedRefs" condition MUST be set to False for this listener with the - "RefNotPermitted" reason. - items: - description: |- - 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. - properties: - group: - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - description: Kind is kind of the referent. For - example "ConfigMap" or "Service". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - 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 - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - group - - kind - - name - type: object - maxItems: 8 - minItems: 1 - type: array - type: object + x-kubernetes-list-type: atomic mode: default: Terminate description: |- @@ -2576,6 +2826,366 @@ spec: rule: 'self.all(l1, self.exists_one(l2, l1.port == l2.port && l1.protocol == l2.protocol && (has(l1.hostname) && has(l2.hostname) ? l1.hostname == l2.hostname : !has(l1.hostname) && !has(l2.hostname))))' + tls: + description: |- + TLS specifies frontend and backend tls configuration for entire gateway. + + Support: Extended + properties: + backend: + description: |- + Backend describes TLS configuration for gateway when connecting + to backends. + + Note that this contains only details for the Gateway as a TLS client, + and does _not_ imply behavior about how to choose which backend should + get a TLS connection. That is determined by the presence of a BackendTLSPolicy. + + Support: Core + properties: + clientCertificateRef: + description: |- + ClientCertificateRef is a reference to an object that contains a Client + Certificate and the associated private key. + + References to a resource in different namespace are invalid UNLESS there + is a ReferenceGrant in the target namespace that allows the certificate + to be attached. If a ReferenceGrant does not allow this reference, the + "ResolvedRefs" condition MUST be set to False for this listener with the + "RefNotPermitted" reason. + + ClientCertificateRef can reference to standard Kubernetes resources, i.e. + Secret, or implementation-specific custom resources. + + Support: Core + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Secret + description: Kind is kind of the referent. For example + "Secret". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - name + type: object + type: object + frontend: + description: |- + Frontend describes TLS config when client connects to Gateway. + Support: Core + properties: + default: + description: |- + Default specifies the default client certificate validation configuration + for all Listeners handling HTTPS traffic, unless a per-port configuration + is defined. + + support: Core + properties: + validation: + description: |- + Validation holds configuration information for validating the frontend (client). + Setting this field will result in mutual authentication when connecting to the gateway. + In browsers this may result in a dialog appearing + that requests a user to specify the client certificate. + The maximum depth of a certificate chain accepted in verification is Implementation specific. + + Support: Core + properties: + caCertificateRefs: + description: |- + CACertificateRefs contains one or more references to + Kubernetes objects that contain TLS certificates of + the Certificate Authorities that can be used + as a trust anchor to validate the certificates presented by the client. + + A single CA certificate reference to a Kubernetes ConfigMap + has "Core" support. + Implementations MAY choose to support attaching multiple CA certificates to + a Listener, but this behavior is implementation-specific. + + Support: Core - A single reference to a Kubernetes ConfigMap + with the CA certificate in a key named `ca.crt`. + + Support: Implementation-specific (More than one certificate in a ConfigMap + with different keys or more than one reference, or other kinds of resources). + + References to a resource in a different namespace are invalid UNLESS there + is a ReferenceGrant in the target namespace that allows the certificate + to be attached. If a ReferenceGrant does not allow this reference, the + "ResolvedRefs" condition MUST be set to False for this listener with the + "RefNotPermitted" reason. + items: + description: |- + 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. + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When set to the empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For + example "ConfigMap" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + mode: + default: AllowValidOnly + description: |- + FrontendValidationMode defines the mode for validating the client certificate. + There are two possible modes: + + - AllowValidOnly: In this mode, the gateway will accept connections only if + the client presents a valid certificate. This certificate must successfully + pass validation against the CA certificates specified in `CACertificateRefs`. + - AllowInsecureFallback: In this mode, the gateway will accept connections + even if the client certificate is not presented or fails verification. + + This approach delegates client authorization to the backend and introduce + a significant security risk. It should be used in testing environments or + on a temporary basis in non-testing environments. + + Defaults to AllowValidOnly. + + Support: Core + enum: + - AllowValidOnly + - AllowInsecureFallback + type: string + required: + - caCertificateRefs + type: object + type: object + perPort: + description: |- + PerPort specifies tls configuration assigned per port. + Per port configuration is optional. Once set this configuration overrides + the default configuration for all Listeners handling HTTPS traffic + that match this port. + Each override port requires a unique TLS configuration. + + support: Core + items: + properties: + port: + description: |- + The Port indicates the Port Number to which the TLS configuration will be + applied. This configuration will be applied to all Listeners handling HTTPS + traffic that match this port. + + Support: Core + format: int32 + maximum: 65535 + minimum: 1 + type: integer + tls: + description: |- + TLS store the configuration that will be applied to all Listeners handling + HTTPS traffic and matching given port. + + Support: Core + properties: + validation: + description: |- + Validation holds configuration information for validating the frontend (client). + Setting this field will result in mutual authentication when connecting to the gateway. + In browsers this may result in a dialog appearing + that requests a user to specify the client certificate. + The maximum depth of a certificate chain accepted in verification is Implementation specific. + + Support: Core + properties: + caCertificateRefs: + description: |- + CACertificateRefs contains one or more references to + Kubernetes objects that contain TLS certificates of + the Certificate Authorities that can be used + as a trust anchor to validate the certificates presented by the client. + + A single CA certificate reference to a Kubernetes ConfigMap + has "Core" support. + Implementations MAY choose to support attaching multiple CA certificates to + a Listener, but this behavior is implementation-specific. + + Support: Core - A single reference to a Kubernetes ConfigMap + with the CA certificate in a key named `ca.crt`. + + Support: Implementation-specific (More than one certificate in a ConfigMap + with different keys or more than one reference, or other kinds of resources). + + References to a resource in a different namespace are invalid UNLESS there + is a ReferenceGrant in the target namespace that allows the certificate + to be attached. If a ReferenceGrant does not allow this reference, the + "ResolvedRefs" condition MUST be set to False for this listener with the + "RefNotPermitted" reason. + items: + description: |- + 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. + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When set to the empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. + For example "ConfigMap" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + mode: + default: AllowValidOnly + description: |- + FrontendValidationMode defines the mode for validating the client certificate. + There are two possible modes: + + - AllowValidOnly: In this mode, the gateway will accept connections only if + the client presents a valid certificate. This certificate must successfully + pass validation against the CA certificates specified in `CACertificateRefs`. + - AllowInsecureFallback: In this mode, the gateway will accept connections + even if the client certificate is not presented or fails verification. + + This approach delegates client authorization to the backend and introduce + a significant security risk. It should be used in testing environments or + on a temporary basis in non-testing environments. + + Defaults to AllowValidOnly. + + Support: Core + enum: + - AllowValidOnly + - AllowInsecureFallback + type: string + required: + - caCertificateRefs + type: object + type: object + required: + - port + - tls + type: object + maxItems: 64 + type: array + x-kubernetes-list-map-keys: + - port + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: Port for TLS configuration must be unique within + the Gateway + rule: self.all(t1, self.exists_one(t2, t1.port == t2.port)) + required: + - default + type: object + type: object required: - gatewayClassName - listeners @@ -2596,7 +3206,7 @@ spec: description: Status defines the current state of Gateway. properties: addresses: - description: |+ + description: |- Addresses lists the network addresses that have been bound to the Gateway. @@ -2606,7 +3216,6 @@ spec: * no addresses are specified, all addresses are dynamically assigned * a combination of specified and dynamic addresses are assigned * a specified address was unusable (e.g. already in use) - items: description: GatewayStatusAddress describes a network address that is bound to a Gateway. @@ -2651,6 +3260,7 @@ spec: true' maxItems: 16 type: array + x-kubernetes-list-type: atomic conditions: default: - lastTransitionTime: "1970-01-01T00:00:00Z" @@ -2864,6 +3474,7 @@ spec: type: object maxItems: 8 type: array + x-kubernetes-list-type: atomic required: - attachedRoutes - conditions @@ -2924,11 +3535,11 @@ spec: description: Spec defines the desired state of Gateway. properties: addresses: - description: |+ + description: |- Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST - indicate this in the associated entry in GatewayStatus.Addresses. + indicate this in an associated entry in GatewayStatus.Conditions. The Addresses field represents a request for the address(es) on the "outside of the Gateway", that traffic bound for this Gateway will use. @@ -2945,10 +3556,9 @@ spec: GatewayStatus.Addresses. Support: Extended - items: - description: GatewayAddress describes an address that can be bound - to a Gateway. + description: GatewaySpecAddress describes an address that can be + bound to a Gateway. oneOf: - properties: type: @@ -2973,96 +3583,137 @@ spec: type: string value: description: |- - Value of the address. The validity of the values will depend - on the type and support by the controller. + When a value is unspecified, an implementation SHOULD automatically + assign an address matching the requested type if possible. + + If an implementation does not support an empty value, they MUST set the + "Programmed" condition in status to False with a reason of "AddressNotAssigned". Examples: `1.2.3.4`, `128::1`, `my-ip-address`. maxLength: 253 - minLength: 1 type: string - required: - - value type: object x-kubernetes-validations: - - message: Hostname value must only contain valid characters (matching - ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) - rule: 'self.type == ''Hostname'' ? self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"""): + - message: Hostname value must be empty or contain only valid characters + (matching ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) + rule: 'self.type == ''Hostname'' ? (!has(self.value) || self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$""")): true' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: IPAddress values must be unique - rule: 'self.all(a1, a1.type == ''IPAddress'' ? self.exists_one(a2, - a2.type == a1.type && a2.value == a1.value) : true )' + rule: 'self.all(a1, a1.type == ''IPAddress'' && has(a1.value) ? + self.exists_one(a2, a2.type == a1.type && has(a2.value) && a2.value + == a1.value) : true )' - message: Hostname values must be unique - rule: 'self.all(a1, a1.type == ''Hostname'' ? self.exists_one(a2, - a2.type == a1.type && a2.value == a1.value) : true )' - backendTLS: - description: |+ - BackendTLS configures TLS settings for when this Gateway is connecting to - backends with TLS. - - Support: Core - + rule: 'self.all(a1, a1.type == ''Hostname'' && has(a1.value) ? + self.exists_one(a2, a2.type == a1.type && has(a2.value) && a2.value + == a1.value) : true )' + allowedListeners: + description: |- + AllowedListeners defines which ListenerSets can be attached to this Gateway. + While this feature is experimental, the default value is to allow no ListenerSets. properties: - clientCertificateRef: - description: |+ - ClientCertificateRef is a reference to an object that contains a Client - Certificate and the associated private key. - - References to a resource in different namespace are invalid UNLESS there - is a ReferenceGrant in the target namespace that allows the certificate - to be attached. If a ReferenceGrant does not allow this reference, the - "ResolvedRefs" condition MUST be set to False for this listener with the - "RefNotPermitted" reason. - - ClientCertificateRef can reference to standard Kubernetes resources, i.e. - Secret, or implementation-specific custom resources. - - This setting can be overridden on the service level by use of BackendTLSPolicy. - - Support: Core - + namespaces: + default: + from: None + description: |- + Namespaces defines which namespaces ListenerSets can be attached to this Gateway. + While this feature is experimental, the default value is to allow no ListenerSets. properties: - group: - default: "" + from: + default: None description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + From indicates where ListenerSets can attach to this Gateway. Possible + values are: + + * Same: Only ListenerSets in the same namespace may be attached to this Gateway. + * Selector: ListenerSets in namespaces selected by the selector may be attached to this Gateway. + * All: ListenerSets in all namespaces may be attached to this Gateway. + * None: Only listeners defined in the Gateway's spec are allowed + + While this feature is experimental, the default value None + enum: + - All + - Selector + - Same + - None type: string - kind: - default: Secret - description: Kind is kind of the referent. For example "Secret". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: + selector: description: |- - 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 - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - name + Selector must be specified when From is set to "Selector". In that case, + only ListenerSets in Namespaces matching this Selector will be selected by this + Gateway. This field is ignored for other values of "From". + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic type: object type: object + defaultScope: + description: |- + DefaultScope, when set, configures the Gateway as a default Gateway, + meaning it will dynamically and implicitly have Routes (e.g. HTTPRoute) + attached to it, according to the scope configured here. + + If unset (the default) or set to None, the Gateway will not act as a + default Gateway; if set, the Gateway will claim any Route with a + matching scope set in its UseDefaultGateway field, subject to the usual + rules about which routes the Gateway can attach to. + + Think carefully before using this functionality! While the normal rules + about which Route can apply are still enforced, it is simply easier for + the wrong Route to be accidentally attached to this Gateway in this + configuration. If the Gateway operator is not also the operator in + control of the scope (e.g. namespace) with tight controls and checks on + what kind of workloads and Routes get added in that scope, we strongly + recommend not using this just because it seems convenient, and instead + stick to direct Route attachment. + enum: + - All + - None + type: string gatewayClassName: description: |- GatewayClassName used for this Gateway. This is the name of a @@ -3157,6 +3808,11 @@ spec: the merging behavior is implementation specific. It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. + If the referent cannot be found, refers to an unsupported kind, or when + the data within that resource is malformed, the Gateway SHOULD be + rejected with the "Accepted" status condition set to "False" and an + "InvalidParameters" reason. + Support: Implementation-specific properties: group: @@ -3187,6 +3843,8 @@ spec: logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. + ## Distinct Listeners + Each Listener in a set of Listeners (for example, in a single Gateway) MUST be _distinct_, in that a traffic flow MUST be able to be assigned to exactly one listener. (This section uses "set of Listeners" rather than @@ -3198,55 +3856,76 @@ spec: combination of Port, Protocol, and, if supported by the protocol, Hostname. Some combinations of port, protocol, and TLS settings are considered - Core support and MUST be supported by implementations based on their - targeted conformance profile: + Core support and MUST be supported by implementations based on the objects + they support: - HTTP Profile + HTTPRoute 1. HTTPRoute, Port: 80, Protocol: HTTP 2. HTTPRoute, Port: 443, Protocol: HTTPS, TLS Mode: Terminate, TLS keypair provided - TLS Profile + TLSRoute 1. TLSRoute, Port: 443, Protocol: TLS, TLS Mode: Passthrough "Distinct" Listeners have the following property: - The implementation can match inbound requests to a single distinct - Listener. When multiple Listeners share values for fields (for + **The implementation can match inbound requests to a single distinct + Listener**. + + When multiple Listeners share values for fields (for example, two Listeners with the same Port value), the implementation can match requests to only one of the Listeners using other Listener fields. - For example, the following Listener scenarios are distinct: + When multiple listeners have the same value for the Protocol field, then + each of the Listeners with matching Protocol values MUST have different + values for other fields. - 1. Multiple Listeners with the same Port that all use the "HTTP" - Protocol that all have unique Hostname values. - 2. Multiple Listeners with the same Port that use either the "HTTPS" or - "TLS" Protocol that all have unique Hostname values. - 3. A mixture of "TCP" and "UDP" Protocol Listeners, where no Listener - with the same Protocol has the same Port value. + The set of fields that MUST be different for a Listener differs per protocol. + The following rules define the rules for what fields MUST be considered for + Listeners to be distinct with each protocol currently defined in the + Gateway API spec. - Some fields in the Listener struct have possible values that affect - whether the Listener is distinct. Hostname is particularly relevant - for HTTP or HTTPS protocols. + The set of listeners that all share a protocol value MUST have _different_ + values for _at least one_ of these fields to be distinct: - When using the Hostname value to select between same-Port, same-Protocol - Listeners, the Hostname value must be different on each Listener for the - Listener to be distinct. + * **HTTP, HTTPS, TLS**: Port, Hostname + * **TCP, UDP**: Port - When the Listeners are distinct based on Hostname, inbound request + One **very** important rule to call out involves what happens when an + implementation: + + * Supports TCP protocol Listeners, as well as HTTP, HTTPS, or TLS protocol + Listeners, and + * sees HTTP, HTTPS, or TLS protocols with the same `port` as one with TCP + Protocol. + + In this case all the Listeners that share a port with the + TCP Listener are not distinct and so MUST NOT be accepted. + + If an implementation does not support TCP Protocol Listeners, then the + previous rule does not apply, and the TCP Listeners SHOULD NOT be + accepted. + + Note that the `tls` field is not used for determining if a listener is distinct, because + Listeners that _only_ differ on TLS config will still conflict in all cases. + + ### Listeners that are distinct only by Hostname + + When the Listeners are distinct based only on Hostname, inbound request hostnames MUST match from the most specific to least specific Hostname values to choose the correct Listener and its associated set of Routes. - Exact matches must be processed before wildcard matches, and wildcard - matches must be processed before fallback (empty Hostname value) + Exact matches MUST be processed before wildcard matches, and wildcard + matches MUST be processed before fallback (empty Hostname value) matches. For example, `"foo.example.com"` takes precedence over `"*.example.com"`, and `"*.example.com"` takes precedence over `""`. Additionally, if there are multiple wildcard entries, more specific wildcard entries must be processed before less specific wildcard entries. For example, `"*.foo.example.com"` takes precedence over `"*.example.com"`. + The precise definition here is that the higher the number of dots in the hostname to the right of the wildcard character, the higher the precedence. @@ -3254,18 +3933,26 @@ spec: the left, however, so `"*.example.com"` will match both `"foo.bar.example.com"` _and_ `"bar.example.com"`. + ## Handling indistinct Listeners + If a set of Listeners contains Listeners that are not distinct, then those - Listeners are Conflicted, and the implementation MUST set the "Conflicted" + Listeners are _Conflicted_, and the implementation MUST set the "Conflicted" condition in the Listener Status to "True". + The words "indistinct" and "conflicted" are considered equivalent for the + purpose of this documentation. + Implementations MAY choose to accept a Gateway with some Conflicted Listeners only if they only accept the partial Listener set that contains - no Conflicted Listeners. To put this another way, implementations may - accept a partial Listener set only if they throw out *all* the conflicting - Listeners. No picking one of the conflicting listeners as the winner. - This also means that the Gateway must have at least one non-conflicting - Listener in this case, otherwise it violates the requirement that at - least one Listener must be present. + no Conflicted Listeners. + + Specifically, an implementation MAY accept a partial Listener set subject to + the following rules: + + * The implementation MUST NOT pick one conflicting Listener as the winner. + ALL indistinct Listeners must not be accepted for processing. + * At least one distinct Listener MUST be present, or else the Gateway effectively + contains _no_ Listeners, and must be rejected from processing as a whole. The implementation MUST set a "ListenersNotValid" condition on the Gateway Status when the Gateway contains Conflicted Listeners whether or @@ -3274,7 +3961,25 @@ spec: Accepted. Additionally, the Listener status for those listeners SHOULD indicate which Listeners are conflicted and not Accepted. - A Gateway's Listeners are considered "compatible" if: + ## General Listener behavior + + Note that, for all distinct Listeners, requests SHOULD match at most one Listener. + For example, if Listeners are defined for "foo.example.com" and "*.example.com", a + request to "foo.example.com" SHOULD only be routed using routes attached + to the "foo.example.com" Listener (and not the "*.example.com" Listener). + + This concept is known as "Listener Isolation", and it is an Extended feature + of Gateway API. Implementations that do not support Listener Isolation MUST + clearly document this, and MUST NOT claim support for the + `GatewayHTTPListenerIsolation` feature. + + Implementations that _do_ support Listener Isolation SHOULD claim support + for the Extended `GatewayHTTPListenerIsolation` feature and pass the associated + conformance tests. + + ## Compatible Listeners + + A Gateway's Listeners are considered _compatible_ if: 1. They are distinct. 2. The implementation can serve them in compliance with the Addresses @@ -3289,16 +3994,11 @@ spec: on the same address, or cannot mix HTTPS and generic TLS listens on the same port would not consider those cases compatible, even though they are distinct. - Note that requests SHOULD match at most one Listener. For example, if - Listeners are defined for "foo.example.com" and "*.example.com", a - request to "foo.example.com" SHOULD only be routed using routes attached - to the "foo.example.com" Listener (and not the "*.example.com" Listener). - This concept is known as "Listener Isolation". Implementations that do - not support Listener Isolation MUST clearly document this. - Implementations MAY merge separate Gateways onto a single set of Addresses if all Listeners across all Gateways are compatible. + In a future release the MinItems=1 requirement MAY be dropped. + Support: Core items: description: |- @@ -3369,6 +4069,7 @@ spec: type: object maxItems: 8 type: array + x-kubernetes-list-type: atomic namespaces: default: from: Same @@ -3460,10 +4161,31 @@ spec: * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. - * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP - protocol layers as described above. If an implementation does not - ensure that both the SNI and Host header match the Listener hostname, - it MUST clearly document that. + * HTTPS: The Listener Hostname SHOULD match both the SNI and Host header. + Note that this does not require the SNI and Host header to be the same. + The semantics of this are described in more detail below. + + To ensure security, Section 11.1 of RFC-6066 emphasizes that server + implementations that rely on SNI hostname matching MUST also verify + hostnames within the application protocol. + + Section 9.1.2 of RFC-7540 provides a mechanism for servers to reject the + reuse of a connection by responding with the HTTP 421 Misdirected Request + status code. This indicates that the origin server has rejected the + request because it appears to have been misdirected. + + To detect misdirected requests, Gateways SHOULD match the authority of + the requests with all the SNI hostname(s) configured across all the + Gateway Listeners on the same port and protocol: + + * If another Listener has an exact match or more specific wildcard entry, + the Gateway SHOULD return a 421. + * If the current Listener (selected by SNI matching during ClientHello) + does not match the Host: + * If another Listener does match the Host the Gateway SHOULD return a + 421. + * If no other Listener matches the Host, the Gateway MUST return a + 404. For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, @@ -3515,7 +4237,7 @@ spec: the Protocol field is "HTTPS" or "TLS". It is invalid to set this field if the Protocol field is "HTTP", "TCP", or "UDP". - The association of SNIs to Certificate defined in GatewayTLSConfig is + The association of SNIs to Certificate defined in ListenerTLSConfig is defined based on the Hostname field for this listener. The GatewayClass MUST use the longest matching SNI out of all @@ -3602,94 +4324,7 @@ spec: type: object maxItems: 64 type: array - frontendValidation: - description: |+ - FrontendValidation holds configuration information for validating the frontend (client). - Setting this field will require clients to send a client certificate - required for validation during the TLS handshake. In browsers this may result in a dialog appearing - that requests a user to specify the client certificate. - The maximum depth of a certificate chain accepted in verification is Implementation specific. - - Support: Extended - - properties: - caCertificateRefs: - description: |- - CACertificateRefs contains one or more references to - Kubernetes objects that contain TLS certificates of - the Certificate Authorities that can be used - as a trust anchor to validate the certificates presented by the client. - - A single CA certificate reference to a Kubernetes ConfigMap - has "Core" support. - Implementations MAY choose to support attaching multiple CA certificates to - a Listener, but this behavior is implementation-specific. - - Support: Core - A single reference to a Kubernetes ConfigMap - with the CA certificate in a key named `ca.crt`. - - Support: Implementation-specific (More than one reference, or other kinds - of resources). - - References to a resource in a different namespace are invalid UNLESS there - is a ReferenceGrant in the target namespace that allows the certificate - to be attached. If a ReferenceGrant does not allow this reference, the - "ResolvedRefs" condition MUST be set to False for this listener with the - "RefNotPermitted" reason. - items: - description: |- - 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. - properties: - group: - description: |- - Group is the group of the referent. For example, "gateway.networking.k8s.io". - When unspecified or empty string, core API group is inferred. - maxLength: 253 - pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ - type: string - kind: - description: Kind is kind of the referent. For - example "ConfigMap" or "Service". - maxLength: 63 - minLength: 1 - pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ - type: string - name: - description: Name is the name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - 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 - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - group - - kind - - name - type: object - maxItems: 8 - minItems: 1 - type: array - type: object + x-kubernetes-list-type: atomic mode: default: Terminate description: |- @@ -3768,6 +4403,366 @@ spec: rule: 'self.all(l1, self.exists_one(l2, l1.port == l2.port && l1.protocol == l2.protocol && (has(l1.hostname) && has(l2.hostname) ? l1.hostname == l2.hostname : !has(l1.hostname) && !has(l2.hostname))))' + tls: + description: |- + TLS specifies frontend and backend tls configuration for entire gateway. + + Support: Extended + properties: + backend: + description: |- + Backend describes TLS configuration for gateway when connecting + to backends. + + Note that this contains only details for the Gateway as a TLS client, + and does _not_ imply behavior about how to choose which backend should + get a TLS connection. That is determined by the presence of a BackendTLSPolicy. + + Support: Core + properties: + clientCertificateRef: + description: |- + ClientCertificateRef is a reference to an object that contains a Client + Certificate and the associated private key. + + References to a resource in different namespace are invalid UNLESS there + is a ReferenceGrant in the target namespace that allows the certificate + to be attached. If a ReferenceGrant does not allow this reference, the + "ResolvedRefs" condition MUST be set to False for this listener with the + "RefNotPermitted" reason. + + ClientCertificateRef can reference to standard Kubernetes resources, i.e. + Secret, or implementation-specific custom resources. + + Support: Core + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Secret + description: Kind is kind of the referent. For example + "Secret". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - name + type: object + type: object + frontend: + description: |- + Frontend describes TLS config when client connects to Gateway. + Support: Core + properties: + default: + description: |- + Default specifies the default client certificate validation configuration + for all Listeners handling HTTPS traffic, unless a per-port configuration + is defined. + + support: Core + properties: + validation: + description: |- + Validation holds configuration information for validating the frontend (client). + Setting this field will result in mutual authentication when connecting to the gateway. + In browsers this may result in a dialog appearing + that requests a user to specify the client certificate. + The maximum depth of a certificate chain accepted in verification is Implementation specific. + + Support: Core + properties: + caCertificateRefs: + description: |- + CACertificateRefs contains one or more references to + Kubernetes objects that contain TLS certificates of + the Certificate Authorities that can be used + as a trust anchor to validate the certificates presented by the client. + + A single CA certificate reference to a Kubernetes ConfigMap + has "Core" support. + Implementations MAY choose to support attaching multiple CA certificates to + a Listener, but this behavior is implementation-specific. + + Support: Core - A single reference to a Kubernetes ConfigMap + with the CA certificate in a key named `ca.crt`. + + Support: Implementation-specific (More than one certificate in a ConfigMap + with different keys or more than one reference, or other kinds of resources). + + References to a resource in a different namespace are invalid UNLESS there + is a ReferenceGrant in the target namespace that allows the certificate + to be attached. If a ReferenceGrant does not allow this reference, the + "ResolvedRefs" condition MUST be set to False for this listener with the + "RefNotPermitted" reason. + items: + description: |- + 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. + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When set to the empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. For + example "ConfigMap" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + mode: + default: AllowValidOnly + description: |- + FrontendValidationMode defines the mode for validating the client certificate. + There are two possible modes: + + - AllowValidOnly: In this mode, the gateway will accept connections only if + the client presents a valid certificate. This certificate must successfully + pass validation against the CA certificates specified in `CACertificateRefs`. + - AllowInsecureFallback: In this mode, the gateway will accept connections + even if the client certificate is not presented or fails verification. + + This approach delegates client authorization to the backend and introduce + a significant security risk. It should be used in testing environments or + on a temporary basis in non-testing environments. + + Defaults to AllowValidOnly. + + Support: Core + enum: + - AllowValidOnly + - AllowInsecureFallback + type: string + required: + - caCertificateRefs + type: object + type: object + perPort: + description: |- + PerPort specifies tls configuration assigned per port. + Per port configuration is optional. Once set this configuration overrides + the default configuration for all Listeners handling HTTPS traffic + that match this port. + Each override port requires a unique TLS configuration. + + support: Core + items: + properties: + port: + description: |- + The Port indicates the Port Number to which the TLS configuration will be + applied. This configuration will be applied to all Listeners handling HTTPS + traffic that match this port. + + Support: Core + format: int32 + maximum: 65535 + minimum: 1 + type: integer + tls: + description: |- + TLS store the configuration that will be applied to all Listeners handling + HTTPS traffic and matching given port. + + Support: Core + properties: + validation: + description: |- + Validation holds configuration information for validating the frontend (client). + Setting this field will result in mutual authentication when connecting to the gateway. + In browsers this may result in a dialog appearing + that requests a user to specify the client certificate. + The maximum depth of a certificate chain accepted in verification is Implementation specific. + + Support: Core + properties: + caCertificateRefs: + description: |- + CACertificateRefs contains one or more references to + Kubernetes objects that contain TLS certificates of + the Certificate Authorities that can be used + as a trust anchor to validate the certificates presented by the client. + + A single CA certificate reference to a Kubernetes ConfigMap + has "Core" support. + Implementations MAY choose to support attaching multiple CA certificates to + a Listener, but this behavior is implementation-specific. + + Support: Core - A single reference to a Kubernetes ConfigMap + with the CA certificate in a key named `ca.crt`. + + Support: Implementation-specific (More than one certificate in a ConfigMap + with different keys or more than one reference, or other kinds of resources). + + References to a resource in a different namespace are invalid UNLESS there + is a ReferenceGrant in the target namespace that allows the certificate + to be attached. If a ReferenceGrant does not allow this reference, the + "ResolvedRefs" condition MUST be set to False for this listener with the + "RefNotPermitted" reason. + items: + description: |- + 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. + properties: + group: + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When set to the empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. + For example "ConfigMap" or "Service". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + mode: + default: AllowValidOnly + description: |- + FrontendValidationMode defines the mode for validating the client certificate. + There are two possible modes: + + - AllowValidOnly: In this mode, the gateway will accept connections only if + the client presents a valid certificate. This certificate must successfully + pass validation against the CA certificates specified in `CACertificateRefs`. + - AllowInsecureFallback: In this mode, the gateway will accept connections + even if the client certificate is not presented or fails verification. + + This approach delegates client authorization to the backend and introduce + a significant security risk. It should be used in testing environments or + on a temporary basis in non-testing environments. + + Defaults to AllowValidOnly. + + Support: Core + enum: + - AllowValidOnly + - AllowInsecureFallback + type: string + required: + - caCertificateRefs + type: object + type: object + required: + - port + - tls + type: object + maxItems: 64 + type: array + x-kubernetes-list-map-keys: + - port + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: Port for TLS configuration must be unique within + the Gateway + rule: self.all(t1, self.exists_one(t2, t1.port == t2.port)) + required: + - default + type: object + type: object required: - gatewayClassName - listeners @@ -3788,7 +4783,7 @@ spec: description: Status defines the current state of Gateway. properties: addresses: - description: |+ + description: |- Addresses lists the network addresses that have been bound to the Gateway. @@ -3798,7 +4793,6 @@ spec: * no addresses are specified, all addresses are dynamically assigned * a combination of specified and dynamic addresses are assigned * a specified address was unusable (e.g. already in use) - items: description: GatewayStatusAddress describes a network address that is bound to a Gateway. @@ -3843,6 +4837,7 @@ spec: true' maxItems: 16 type: array + x-kubernetes-list-type: atomic conditions: default: - lastTransitionTime: "1970-01-01T00:00:00Z" @@ -4056,6 +5051,7 @@ spec: type: object maxItems: 8 type: array + x-kubernetes-list-type: atomic required: - attachedRoutes - conditions @@ -4090,9 +5086,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.2.1 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null name: grpcroutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -4238,8 +5233,9 @@ spec: type: string maxItems: 16 type: array + x-kubernetes-list-type: atomic parentRefs: - description: |+ + description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means @@ -4301,11 +5297,6 @@ spec: connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. - - - - - items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered @@ -4455,6 +5446,7 @@ spec: type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent @@ -4479,9 +5471,7 @@ spec: || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port)))) rules: - description: |+ - Rules are a list of GRPC matchers, filters and actions. - + description: Rules are a list of GRPC matchers, filters and actions. items: description: |- GRPCRouteRule defines the semantics for matching a gRPC request based on @@ -4527,7 +5517,6 @@ spec: namespace's owner to accept the reference. See the ReferenceGrant documentation for details. - When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. @@ -4542,8 +5531,6 @@ spec: If a Route is not able to send traffic to the backend using the specified protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. - - properties: filters: description: |- @@ -4629,7 +5616,7 @@ spec: name: description: |- 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 @@ -4704,7 +5691,7 @@ spec: name: description: |- 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 @@ -4732,7 +5719,7 @@ spec: x-kubernetes-list-type: map type: object requestMirror: - description: |+ + description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. @@ -4742,7 +5729,6 @@ spec: backends. Support: Extended - properties: backendRef: description: |- @@ -4838,13 +5824,12 @@ spec: rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: - description: |+ + description: |- 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. - properties: denominator: default: 100 @@ -4863,14 +5848,13 @@ spec: to denominator rule: self.numerator <= self.denominator percent: - description: |+ + description: |- 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. - format: int32 maximum: 100 minimum: 0 @@ -4915,7 +5899,7 @@ spec: name: description: |- 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 @@ -4990,7 +5974,7 @@ spec: name: description: |- 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 @@ -5018,7 +6002,7 @@ spec: x-kubernetes-list-type: map type: object type: - description: |+ + description: |- Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: @@ -5043,7 +6027,6 @@ spec: If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. - enum: - ResponseHeaderModifier - RequestHeaderModifier @@ -5085,6 +6068,7 @@ spec: rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: RequestHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'RequestHeaderModifier').size() @@ -5181,6 +6165,7 @@ spec: ? has(self.port) : true' maxItems: 16 type: array + x-kubernetes-list-type: atomic filters: description: |- Filters define the filters that are applied to requests that match @@ -5200,7 +6185,7 @@ spec: Specifying the same filter multiple times is not supported unless explicitly indicated in the filter. - If an implementation can not support a combination of filters, it must clearly + If an implementation cannot support a combination of filters, it 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 @@ -5283,7 +6268,7 @@ spec: name: description: |- 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 @@ -5357,7 +6342,7 @@ spec: name: description: |- 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 @@ -5385,7 +6370,7 @@ spec: x-kubernetes-list-type: map type: object requestMirror: - description: |+ + description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. @@ -5395,7 +6380,6 @@ spec: backends. Support: Extended - properties: backendRef: description: |- @@ -5491,13 +6475,12 @@ spec: rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: - description: |+ + description: |- 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. - properties: denominator: default: 100 @@ -5516,14 +6499,13 @@ spec: denominator rule: self.numerator <= self.denominator percent: - description: |+ + description: |- 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. - format: int32 maximum: 100 minimum: 0 @@ -5567,7 +6549,7 @@ spec: name: description: |- 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 @@ -5641,7 +6623,7 @@ spec: name: description: |- 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 @@ -5669,7 +6651,7 @@ spec: x-kubernetes-list-type: map type: object type: - description: |+ + description: |- Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: @@ -5694,7 +6676,6 @@ spec: If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. - enum: - ResponseHeaderModifier - RequestHeaderModifier @@ -5735,6 +6716,7 @@ spec: rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: RequestHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'RequestHeaderModifier').size() @@ -5910,10 +6892,11 @@ spec: has(self.method) ? self.method.matches(r"""^[A-Za-z_][A-Za-z_0-9]*$"""): true' type: object - maxItems: 8 + maxItems: 64 type: array + x-kubernetes-list-type: atomic name: - description: | + description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. Support: Extended @@ -5922,12 +6905,11 @@ spec: pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string sessionPersistence: - description: |+ + description: |- SessionPersistence defines and configures session persistence for the route rule. Support: Extended - properties: absoluteTimeout: description: |- @@ -5962,6 +6944,8 @@ spec: 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 @@ -6012,6 +6996,7 @@ spec: type: object maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - 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 @@ -6036,6 +7021,24 @@ spec: - 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)) + useDefaultGateways: + description: |- + 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. + enum: + - All + - None + type: string type: object status: description: Status defines the current state of GRPCRoute. @@ -6079,7 +7082,7 @@ spec: 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. items: @@ -6300,14 +7303,18 @@ spec: - name type: object required: + - conditions - controllerName - parentRef type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic required: - parents type: object + required: + - spec type: object served: true storage: true @@ -6328,9 +7335,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.2.1 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null name: httproutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -6456,8 +7462,9 @@ spec: type: string maxItems: 16 type: array + x-kubernetes-list-type: atomic parentRefs: - description: |+ + description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means @@ -6519,11 +7526,6 @@ spec: connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. - - - - - items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered @@ -6673,6 +7675,7 @@ spec: type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent @@ -6702,9 +7705,7 @@ spec: - path: type: PathPrefix value: / - description: |+ - Rules are a list of HTTP matchers, filters and actions. - + description: Rules are a list of HTTP matchers, filters and actions. items: description: |- HTTPRouteRule defines semantics for matching an HTTP request based on @@ -6757,7 +7758,6 @@ spec: namespace's owner to accept the reference. See the ReferenceGrant documentation for details. - When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. @@ -6772,8 +7772,6 @@ spec: If a Route is not able to send traffic to the backend using the specified protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. - - properties: filters: description: |- @@ -6791,6 +7789,290 @@ spec: authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: + cors: + description: |- + CORS defines a schema for a filter that responds to the + cross-origin request based on HTTP response header. + + Support: Extended + properties: + allowCredentials: + description: |- + 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 + type: boolean + allowHeaders: + description: |- + 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 + items: + description: |- + HTTPHeaderName is the name of an HTTP header. + + Valid values include: + + * "Authorization" + * "Set-Cookie" + + Invalid values include: + + - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo + headers are not currently supported by this type. + - "/invalid" - "/ " is an invalid character + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + allowMethods: + description: |- + 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 + items: + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + - '*' + type: string + maxItems: 9 + type: array + x-kubernetes-list-type: set + x-kubernetes-validations: + - message: AllowMethods cannot contain '*' alongside + other methods + rule: '!(''*'' in self && self.size() > 1)' + allowOrigins: + description: |- + 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 `://(:)`. + + 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 + items: + description: |- + 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. + maxLength: 253 + minLength: 1 + pattern: (^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$) + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + x-kubernetes-validations: + - message: AllowOrigins cannot contain '*' alongside + other origins + rule: '!(''*'' in self && self.size() > 1)' + exposeHeaders: + description: |- + 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 + items: + description: |- + HTTPHeaderName is the name of an HTTP header. + + Valid values include: + + * "Authorization" + * "Set-Cookie" + + Invalid values include: + + - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo + headers are not currently supported by this type. + - "/invalid" - "/ " is an invalid character + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + maxAge: + default: 5 + description: |- + 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). + format: int32 + minimum: 1 + type: integer + type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the @@ -6826,6 +8108,253 @@ spec: - kind - name type: object + externalAuth: + description: |- + 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 + properties: + backendRef: + description: |- + 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. + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + 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) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind + == ''Service'') ? has(self.port) : true' + forwardBody: + description: |- + 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 + properties: + maxSize: + description: |- + 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. + type: integer + type: object + grpc: + description: |- + 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. + properties: + allowedHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + type: object + http: + description: |- + HTTPAuthConfig contains configuration for communication with HTTP-speaking + backends. + + If unset, implementations must assume the default behavior for each + included field is intended. + properties: + allowedHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + allowedResponseHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + path: + description: |- + 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. + maxLength: 1024 + pattern: ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$ + type: string + type: object + protocol: + description: |- + 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 + enum: + - HTTP + - GRPC + type: string + required: + - backendRef + - protocol + type: object + x-kubernetes-validations: + - message: grpc must be specified when protocol + is set to 'GRPC' + rule: 'self.protocol == ''GRPC'' ? has(self.grpc) + : true' + - message: protocol must be 'GRPC' when grpc is + set + rule: 'has(self.grpc) ? self.protocol == ''GRPC'' + : true' + - message: http must be specified when protocol + is set to 'HTTP' + rule: 'self.protocol == ''HTTP'' ? has(self.http) + : true' + - message: protocol must be 'HTTP' when http is + set + rule: 'has(self.http) ? self.protocol == ''HTTP'' + : true' requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request @@ -6859,7 +8388,7 @@ spec: name: description: |- 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 @@ -6934,7 +8463,7 @@ spec: name: description: |- 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 @@ -6962,7 +8491,7 @@ spec: x-kubernetes-list-type: map type: object requestMirror: - description: |+ + description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. @@ -6972,7 +8501,6 @@ spec: backends. Support: Extended - properties: backendRef: description: |- @@ -7068,13 +8596,12 @@ spec: rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: - description: |+ + description: |- 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. - properties: denominator: default: 100 @@ -7093,14 +8620,13 @@ spec: to denominator rule: self.numerator <= self.denominator percent: - description: |+ + description: |- 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. - format: int32 maximum: 100 minimum: 0 @@ -7298,7 +8824,7 @@ spec: name: description: |- 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 @@ -7373,7 +8899,7 @@ spec: name: description: |- 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 @@ -7441,6 +8967,8 @@ spec: - RequestRedirect - URLRewrite - ExtensionRef + - CORS + - ExternalAuth type: string urlRewrite: description: |- @@ -7573,13 +9101,21 @@ spec: - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' + - message: filter.cors must be nil if the filter.type + is not CORS + rule: '!(has(self.cors) && self.type != ''CORS'')' + - message: filter.cors must be specified for CORS filter.type + rule: '!(!has(self.cors) && self.type == ''CORS'')' + - message: filter.externalAuth must be nil if the filter.type + is not ExternalAuth + rule: '!(has(self.externalAuth) && self.type != ''ExternalAuth'')' + - message: filter.externalAuth must be specified for + ExternalAuth filter.type + rule: '!(!has(self.externalAuth) && self.type == ''ExternalAuth'')' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - - message: May specify either httpRouteFilterRequestRedirect - or httpRouteFilterRequestRewrite, but not both - rule: '!(self.exists(f, f.type == ''RequestRedirect'') - && self.exists(f, f.type == ''URLRewrite''))' - message: May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both rule: '!(self.exists(f, f.type == ''RequestRedirect'') @@ -7685,6 +9221,7 @@ spec: ? has(self.port) : true' maxItems: 16 type: array + x-kubernetes-list-type: atomic filters: description: |- Filters define the filters that are applied to requests that match @@ -7694,7 +9231,7 @@ spec: they are specified. Implementations MAY choose to implement this ordering strictly, rejecting - any combination or order of filters that can not be supported. If implementations + 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. @@ -7716,7 +9253,7 @@ spec: 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 @@ -7732,6 +9269,290 @@ spec: authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: + cors: + description: |- + CORS defines a schema for a filter that responds to the + cross-origin request based on HTTP response header. + + Support: Extended + properties: + allowCredentials: + description: |- + 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 + type: boolean + allowHeaders: + description: |- + 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 + items: + description: |- + HTTPHeaderName is the name of an HTTP header. + + Valid values include: + + * "Authorization" + * "Set-Cookie" + + Invalid values include: + + - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo + headers are not currently supported by this type. + - "/invalid" - "/ " is an invalid character + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + allowMethods: + description: |- + 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 + items: + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + - '*' + type: string + maxItems: 9 + type: array + x-kubernetes-list-type: set + x-kubernetes-validations: + - message: AllowMethods cannot contain '*' alongside + other methods + rule: '!(''*'' in self && self.size() > 1)' + allowOrigins: + description: |- + 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 `://(:)`. + + 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 + items: + description: |- + 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. + maxLength: 253 + minLength: 1 + pattern: (^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$) + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + x-kubernetes-validations: + - message: AllowOrigins cannot contain '*' alongside + other origins + rule: '!(''*'' in self && self.size() > 1)' + exposeHeaders: + description: |- + 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 + items: + description: |- + HTTPHeaderName is the name of an HTTP header. + + Valid values include: + + * "Authorization" + * "Set-Cookie" + + Invalid values include: + + - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo + headers are not currently supported by this type. + - "/invalid" - "/ " is an invalid character + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + maxAge: + default: 5 + description: |- + 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). + format: int32 + minimum: 1 + type: integer + type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the @@ -7767,6 +9588,251 @@ spec: - kind - name type: object + externalAuth: + description: |- + 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 + properties: + backendRef: + description: |- + 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. + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + 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) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + forwardBody: + description: |- + 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 + properties: + maxSize: + description: |- + 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. + type: integer + type: object + grpc: + description: |- + 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. + properties: + allowedHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + type: object + http: + description: |- + HTTPAuthConfig contains configuration for communication with HTTP-speaking + backends. + + If unset, implementations must assume the default behavior for each + included field is intended. + properties: + allowedHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + allowedResponseHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + path: + description: |- + 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. + maxLength: 1024 + pattern: ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$ + type: string + type: object + protocol: + description: |- + 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 + enum: + - HTTP + - GRPC + type: string + required: + - backendRef + - protocol + type: object + x-kubernetes-validations: + - message: grpc must be specified when protocol is set + to 'GRPC' + rule: 'self.protocol == ''GRPC'' ? has(self.grpc) : + true' + - message: protocol must be 'GRPC' when grpc is set + rule: 'has(self.grpc) ? self.protocol == ''GRPC'' : + true' + - message: http must be specified when protocol is set + to 'HTTP' + rule: 'self.protocol == ''HTTP'' ? has(self.http) : + true' + - message: protocol must be 'HTTP' when http is set + rule: 'has(self.http) ? self.protocol == ''HTTP'' : + true' requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request @@ -7799,7 +9865,7 @@ spec: name: description: |- 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 @@ -7873,7 +9939,7 @@ spec: name: description: |- 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 @@ -7901,7 +9967,7 @@ spec: x-kubernetes-list-type: map type: object requestMirror: - description: |+ + description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. @@ -7911,7 +9977,6 @@ spec: backends. Support: Extended - properties: backendRef: description: |- @@ -8007,13 +10072,12 @@ spec: rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: - description: |+ + description: |- 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. - properties: denominator: default: 100 @@ -8032,14 +10096,13 @@ spec: denominator rule: self.numerator <= self.denominator percent: - description: |+ + description: |- 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. - format: int32 maximum: 100 minimum: 0 @@ -8236,7 +10299,7 @@ spec: name: description: |- 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 @@ -8310,7 +10373,7 @@ spec: name: description: |- 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 @@ -8378,6 +10441,8 @@ spec: - RequestRedirect - URLRewrite - ExtensionRef + - CORS + - ExternalAuth type: string urlRewrite: description: |- @@ -8507,8 +10572,20 @@ spec: - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' + - message: filter.cors must be nil if the filter.type is not + CORS + rule: '!(has(self.cors) && self.type != ''CORS'')' + - message: filter.cors must be specified for CORS filter.type + rule: '!(!has(self.cors) && self.type == ''CORS'')' + - message: filter.externalAuth must be nil if the filter.type + is not ExternalAuth + rule: '!(has(self.externalAuth) && self.type != ''ExternalAuth'')' + - message: filter.externalAuth must be specified for ExternalAuth + filter.type + rule: '!(!has(self.externalAuth) && self.type == ''ExternalAuth'')' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both @@ -8610,7 +10687,7 @@ spec: name: description: |- 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 @@ -8820,8 +10897,9 @@ spec: type: object maxItems: 64 type: array + x-kubernetes-list-type: atomic name: - description: | + description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. Support: Extended @@ -8830,15 +10908,14 @@ spec: pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string retry: - description: |+ + description: |- Retry defines the configuration for when to retry an HTTP request. Support: Extended - properties: attempts: description: |- - Attempts specifies the maxmimum number of times an individual request + 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 @@ -8912,20 +10989,18 @@ spec: Implementations MAY support specifying discrete values in the 400-499 range, which are often inadvisable to retry. - - maximum: 599 minimum: 400 type: integer type: array + x-kubernetes-list-type: atomic type: object sessionPersistence: - description: |+ + description: |- SessionPersistence defines and configures session persistence for the route rule. Support: Extended - properties: absoluteTimeout: description: |- @@ -8960,6 +11035,8 @@ spec: 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 @@ -9112,6 +11189,7 @@ spec: != ''PathPrefix'') ? false : true) : true' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - 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 @@ -9130,6 +11208,24 @@ spec: - 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)) + useDefaultGateways: + description: |- + 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. + enum: + - All + - None + type: string type: object status: description: Status defines the current state of HTTPRoute. @@ -9173,7 +11269,7 @@ spec: 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. items: @@ -9394,11 +11490,13 @@ spec: - name type: object required: + - conditions - controllerName - parentRef type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic required: - parents type: object @@ -9522,8 +11620,9 @@ spec: type: string maxItems: 16 type: array + x-kubernetes-list-type: atomic parentRefs: - description: |+ + description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means @@ -9585,11 +11684,6 @@ spec: connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. - - - - - items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered @@ -9739,6 +11833,7 @@ spec: type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent @@ -9768,9 +11863,7 @@ spec: - path: type: PathPrefix value: / - description: |+ - Rules are a list of HTTP matchers, filters and actions. - + description: Rules are a list of HTTP matchers, filters and actions. items: description: |- HTTPRouteRule defines semantics for matching an HTTP request based on @@ -9823,7 +11916,6 @@ spec: namespace's owner to accept the reference. See the ReferenceGrant documentation for details. - When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. @@ -9838,8 +11930,6 @@ spec: If a Route is not able to send traffic to the backend using the specified protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. - - properties: filters: description: |- @@ -9857,6 +11947,290 @@ spec: authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: + cors: + description: |- + CORS defines a schema for a filter that responds to the + cross-origin request based on HTTP response header. + + Support: Extended + properties: + allowCredentials: + description: |- + 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 + type: boolean + allowHeaders: + description: |- + 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 + items: + description: |- + HTTPHeaderName is the name of an HTTP header. + + Valid values include: + + * "Authorization" + * "Set-Cookie" + + Invalid values include: + + - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo + headers are not currently supported by this type. + - "/invalid" - "/ " is an invalid character + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + allowMethods: + description: |- + 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 + items: + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + - '*' + type: string + maxItems: 9 + type: array + x-kubernetes-list-type: set + x-kubernetes-validations: + - message: AllowMethods cannot contain '*' alongside + other methods + rule: '!(''*'' in self && self.size() > 1)' + allowOrigins: + description: |- + 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 `://(:)`. + + 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 + items: + description: |- + 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. + maxLength: 253 + minLength: 1 + pattern: (^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$) + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + x-kubernetes-validations: + - message: AllowOrigins cannot contain '*' alongside + other origins + rule: '!(''*'' in self && self.size() > 1)' + exposeHeaders: + description: |- + 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 + items: + description: |- + HTTPHeaderName is the name of an HTTP header. + + Valid values include: + + * "Authorization" + * "Set-Cookie" + + Invalid values include: + + - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo + headers are not currently supported by this type. + - "/invalid" - "/ " is an invalid character + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + maxAge: + default: 5 + description: |- + 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). + format: int32 + minimum: 1 + type: integer + type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the @@ -9892,6 +12266,253 @@ spec: - kind - name type: object + externalAuth: + description: |- + 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 + properties: + backendRef: + description: |- + 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. + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + 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) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind + == ''Service'') ? has(self.port) : true' + forwardBody: + description: |- + 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 + properties: + maxSize: + description: |- + 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. + type: integer + type: object + grpc: + description: |- + 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. + properties: + allowedHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + type: object + http: + description: |- + HTTPAuthConfig contains configuration for communication with HTTP-speaking + backends. + + If unset, implementations must assume the default behavior for each + included field is intended. + properties: + allowedHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + allowedResponseHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + path: + description: |- + 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. + maxLength: 1024 + pattern: ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$ + type: string + type: object + protocol: + description: |- + 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 + enum: + - HTTP + - GRPC + type: string + required: + - backendRef + - protocol + type: object + x-kubernetes-validations: + - message: grpc must be specified when protocol + is set to 'GRPC' + rule: 'self.protocol == ''GRPC'' ? has(self.grpc) + : true' + - message: protocol must be 'GRPC' when grpc is + set + rule: 'has(self.grpc) ? self.protocol == ''GRPC'' + : true' + - message: http must be specified when protocol + is set to 'HTTP' + rule: 'self.protocol == ''HTTP'' ? has(self.http) + : true' + - message: protocol must be 'HTTP' when http is + set + rule: 'has(self.http) ? self.protocol == ''HTTP'' + : true' requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request @@ -9925,7 +12546,7 @@ spec: name: description: |- 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 @@ -10000,7 +12621,7 @@ spec: name: description: |- 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 @@ -10028,7 +12649,7 @@ spec: x-kubernetes-list-type: map type: object requestMirror: - description: |+ + description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. @@ -10038,7 +12659,6 @@ spec: backends. Support: Extended - properties: backendRef: description: |- @@ -10134,13 +12754,12 @@ spec: rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: - description: |+ + description: |- 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. - properties: denominator: default: 100 @@ -10159,14 +12778,13 @@ spec: to denominator rule: self.numerator <= self.denominator percent: - description: |+ + description: |- 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. - format: int32 maximum: 100 minimum: 0 @@ -10364,7 +12982,7 @@ spec: name: description: |- 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 @@ -10439,7 +13057,7 @@ spec: name: description: |- 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 @@ -10507,6 +13125,8 @@ spec: - RequestRedirect - URLRewrite - ExtensionRef + - CORS + - ExternalAuth type: string urlRewrite: description: |- @@ -10639,13 +13259,21 @@ spec: - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' + - message: filter.cors must be nil if the filter.type + is not CORS + rule: '!(has(self.cors) && self.type != ''CORS'')' + - message: filter.cors must be specified for CORS filter.type + rule: '!(!has(self.cors) && self.type == ''CORS'')' + - message: filter.externalAuth must be nil if the filter.type + is not ExternalAuth + rule: '!(has(self.externalAuth) && self.type != ''ExternalAuth'')' + - message: filter.externalAuth must be specified for + ExternalAuth filter.type + rule: '!(!has(self.externalAuth) && self.type == ''ExternalAuth'')' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - - message: May specify either httpRouteFilterRequestRedirect - or httpRouteFilterRequestRewrite, but not both - rule: '!(self.exists(f, f.type == ''RequestRedirect'') - && self.exists(f, f.type == ''URLRewrite''))' - message: May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both rule: '!(self.exists(f, f.type == ''RequestRedirect'') @@ -10751,6 +13379,7 @@ spec: ? has(self.port) : true' maxItems: 16 type: array + x-kubernetes-list-type: atomic filters: description: |- Filters define the filters that are applied to requests that match @@ -10760,7 +13389,7 @@ spec: they are specified. Implementations MAY choose to implement this ordering strictly, rejecting - any combination or order of filters that can not be supported. If implementations + 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. @@ -10782,7 +13411,7 @@ spec: 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 @@ -10798,6 +13427,290 @@ spec: authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: + cors: + description: |- + CORS defines a schema for a filter that responds to the + cross-origin request based on HTTP response header. + + Support: Extended + properties: + allowCredentials: + description: |- + 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 + type: boolean + allowHeaders: + description: |- + 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 + items: + description: |- + HTTPHeaderName is the name of an HTTP header. + + Valid values include: + + * "Authorization" + * "Set-Cookie" + + Invalid values include: + + - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo + headers are not currently supported by this type. + - "/invalid" - "/ " is an invalid character + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + allowMethods: + description: |- + 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 + items: + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + - '*' + type: string + maxItems: 9 + type: array + x-kubernetes-list-type: set + x-kubernetes-validations: + - message: AllowMethods cannot contain '*' alongside + other methods + rule: '!(''*'' in self && self.size() > 1)' + allowOrigins: + description: |- + 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 `://(:)`. + + 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 + items: + description: |- + 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. + maxLength: 253 + minLength: 1 + pattern: (^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$) + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + x-kubernetes-validations: + - message: AllowOrigins cannot contain '*' alongside + other origins + rule: '!(''*'' in self && self.size() > 1)' + exposeHeaders: + description: |- + 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 + items: + description: |- + HTTPHeaderName is the name of an HTTP header. + + Valid values include: + + * "Authorization" + * "Set-Cookie" + + Invalid values include: + + - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo + headers are not currently supported by this type. + - "/invalid" - "/ " is an invalid character + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + maxAge: + default: 5 + description: |- + 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). + format: int32 + minimum: 1 + type: integer + type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the @@ -10833,6 +13746,251 @@ spec: - kind - name type: object + externalAuth: + description: |- + 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 + properties: + backendRef: + description: |- + 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. + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + 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) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + forwardBody: + description: |- + 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 + properties: + maxSize: + description: |- + 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. + type: integer + type: object + grpc: + description: |- + 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. + properties: + allowedHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + type: object + http: + description: |- + HTTPAuthConfig contains configuration for communication with HTTP-speaking + backends. + + If unset, implementations must assume the default behavior for each + included field is intended. + properties: + allowedHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + allowedResponseHeaders: + description: |- + 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. + items: + type: string + type: array + x-kubernetes-list-type: set + path: + description: |- + 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. + maxLength: 1024 + pattern: ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$ + type: string + type: object + protocol: + description: |- + 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 + enum: + - HTTP + - GRPC + type: string + required: + - backendRef + - protocol + type: object + x-kubernetes-validations: + - message: grpc must be specified when protocol is set + to 'GRPC' + rule: 'self.protocol == ''GRPC'' ? has(self.grpc) : + true' + - message: protocol must be 'GRPC' when grpc is set + rule: 'has(self.grpc) ? self.protocol == ''GRPC'' : + true' + - message: http must be specified when protocol is set + to 'HTTP' + rule: 'self.protocol == ''HTTP'' ? has(self.http) : + true' + - message: protocol must be 'HTTP' when http is set + rule: 'has(self.http) ? self.protocol == ''HTTP'' : + true' requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request @@ -10865,7 +14023,7 @@ spec: name: description: |- 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 @@ -10939,7 +14097,7 @@ spec: name: description: |- 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 @@ -10967,7 +14125,7 @@ spec: x-kubernetes-list-type: map type: object requestMirror: - description: |+ + description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. @@ -10977,7 +14135,6 @@ spec: backends. Support: Extended - properties: backendRef: description: |- @@ -11073,13 +14230,12 @@ spec: rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: - description: |+ + description: |- 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. - properties: denominator: default: 100 @@ -11098,14 +14254,13 @@ spec: denominator rule: self.numerator <= self.denominator percent: - description: |+ + description: |- 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. - format: int32 maximum: 100 minimum: 0 @@ -11302,7 +14457,7 @@ spec: name: description: |- 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 @@ -11376,7 +14531,7 @@ spec: name: description: |- 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 @@ -11444,6 +14599,8 @@ spec: - RequestRedirect - URLRewrite - ExtensionRef + - CORS + - ExternalAuth type: string urlRewrite: description: |- @@ -11573,8 +14730,20 @@ spec: - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' + - message: filter.cors must be nil if the filter.type is not + CORS + rule: '!(has(self.cors) && self.type != ''CORS'')' + - message: filter.cors must be specified for CORS filter.type + rule: '!(!has(self.cors) && self.type == ''CORS'')' + - message: filter.externalAuth must be nil if the filter.type + is not ExternalAuth + rule: '!(has(self.externalAuth) && self.type != ''ExternalAuth'')' + - message: filter.externalAuth must be specified for ExternalAuth + filter.type + rule: '!(!has(self.externalAuth) && self.type == ''ExternalAuth'')' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both @@ -11676,7 +14845,7 @@ spec: name: description: |- 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 @@ -11886,8 +15055,9 @@ spec: type: object maxItems: 64 type: array + x-kubernetes-list-type: atomic name: - description: | + description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. Support: Extended @@ -11896,15 +15066,14 @@ spec: pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string retry: - description: |+ + description: |- Retry defines the configuration for when to retry an HTTP request. Support: Extended - properties: attempts: description: |- - Attempts specifies the maxmimum number of times an individual request + 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 @@ -11978,20 +15147,18 @@ spec: Implementations MAY support specifying discrete values in the 400-499 range, which are often inadvisable to retry. - - maximum: 599 minimum: 400 type: integer type: array + x-kubernetes-list-type: atomic type: object sessionPersistence: - description: |+ + description: |- SessionPersistence defines and configures session persistence for the route rule. Support: Extended - properties: absoluteTimeout: description: |- @@ -12026,6 +15193,8 @@ spec: 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 @@ -12178,6 +15347,7 @@ spec: != ''PathPrefix'') ? false : true) : true' maxItems: 16 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - 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 @@ -12196,6 +15366,24 @@ spec: - 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)) + useDefaultGateways: + description: |- + 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. + enum: + - All + - None + type: string type: object status: description: Status defines the current state of HTTPRoute. @@ -12239,7 +15427,7 @@ spec: 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. items: @@ -12460,11 +15648,13 @@ spec: - name type: object required: + - conditions - controllerName - parentRef type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic required: - parents type: object @@ -12490,9 +15680,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.2.1 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null name: referencegrants.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -12611,6 +15800,7 @@ spec: maxItems: 16 minItems: 1 type: array + x-kubernetes-list-type: atomic to: description: |- To describes the resources that may be referenced by the resources @@ -12660,6 +15850,7 @@ spec: maxItems: 16 minItems: 1 type: array + x-kubernetes-list-type: atomic required: - from - to @@ -12683,9 +15874,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.2.1 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null name: tcproutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -12731,7 +15921,7 @@ spec: description: Spec defines the desired state of TCPRoute. properties: parentRefs: - description: |+ + description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means @@ -12793,11 +15983,6 @@ spec: connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. - - - - - items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered @@ -12947,6 +16132,7 @@ spec: type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent @@ -12971,16 +16157,14 @@ spec: || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port)))) rules: - description: |+ - Rules are a list of TCP matchers and actions. - + description: Rules are a list of TCP matchers and actions. items: description: TCPRouteRule is the configuration for a given rule. properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be - sent. If unspecified or invalid (refers to a non-existent resource or a + sent. If unspecified or invalid (refers to a nonexistent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Connection rejections must respect weight; if an invalid backend is requested to have 80% of @@ -13003,7 +16187,6 @@ spec: namespace's owner to accept the reference. See the ReferenceGrant documentation for details. - When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. @@ -13019,7 +16202,6 @@ spec: protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. - Note that when the BackendTLSPolicy object is enabled by the implementation, there are some extra rules about validity to consider here. See the fields @@ -13115,6 +16297,7 @@ spec: maxItems: 16 minItems: 1 type: array + x-kubernetes-list-type: atomic name: description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. @@ -13124,14 +16307,35 @@ spec: minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string + required: + - backendRefs type: object maxItems: 16 minItems: 1 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - 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)) + useDefaultGateways: + description: |- + 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. + enum: + - All + - None + type: string required: - rules type: object @@ -13177,7 +16381,7 @@ spec: 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. items: @@ -13398,11 +16602,13 @@ spec: - name type: object required: + - conditions - controllerName - parentRef type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic required: - parents type: object @@ -13428,9 +16634,8 @@ kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.2.1 + gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental - creationTimestamp: null name: tlsroutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io @@ -13535,8 +16740,9 @@ spec: type: string maxItems: 16 type: array + x-kubernetes-list-type: atomic parentRefs: - description: |+ + description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means @@ -13598,11 +16804,6 @@ spec: connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. - - - - - items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered @@ -13752,6 +16953,7 @@ spec: type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent @@ -13776,16 +16978,14 @@ spec: || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port)))) rules: - description: |+ - Rules are a list of TLS matchers and actions. - + description: Rules are a list of TLS matchers and actions. items: description: TLSRouteRule is the configuration for a given rule. properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be - sent. If unspecified or invalid (refers to a non-existent resource or + sent. If unspecified or invalid (refers to a nonexistent resource or a Service with no endpoints), the rule performs no forwarding; if no filters are specified that would result in a response being sent, the underlying implementation must actively reject request attempts to this @@ -13811,7 +17011,6 @@ spec: namespace's owner to accept the reference. See the ReferenceGrant documentation for details. - When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. @@ -13827,7 +17026,6 @@ spec: protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. - Note that when the BackendTLSPolicy object is enabled by the implementation, there are some extra rules about validity to consider here. See the fields @@ -13923,6 +17121,7 @@ spec: maxItems: 16 minItems: 1 type: array + x-kubernetes-list-type: atomic name: description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. @@ -13932,14 +17131,35 @@ spec: minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string + required: + - backendRefs type: object maxItems: 16 minItems: 1 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - 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)) + useDefaultGateways: + description: |- + 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. + enum: + - All + - None + type: string required: - rules type: object @@ -13985,7 +17205,7 @@ spec: 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. items: @@ -14206,11 +17426,13 @@ spec: - name type: object required: + - conditions - controllerName - parentRef type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic required: - parents type: object @@ -14218,50 +17440,23 @@ spec: - spec type: object served: true - storage: true + storage: false subresources: status: {} -status: - acceptedNames: - kind: "" - plural: "" - conditions: null - storedVersions: null ---- -# -# config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml -# -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 - gateway.networking.k8s.io/bundle-version: v1.2.1 - gateway.networking.k8s.io/channel: experimental - creationTimestamp: null - name: udproutes.gateway.networking.k8s.io -spec: - group: gateway.networking.k8s.io - names: - categories: - - gateway-api - kind: UDPRoute - listKind: UDPRouteList - plural: udproutes - singular: udproute - scope: Namespaced - versions: - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date - name: v1alpha2 + name: v1alpha3 schema: openAPIV3Schema: description: |- - UDPRoute provides a way to route UDP traffic. When combined with a Gateway - listener, it can be used to forward traffic on the port specified by the - listener to a set of backends specified by the UDPRoute. + The TLSRoute resource is similar to TCPRoute, but can be configured + to match against TLS-specific metadata. This allows more flexibility + in matching streams for a given TLS listener. + + If you need to forward traffic to a single target for a TLS listener, you + could choose to use a TCPRoute with a TLS listener. properties: apiVersion: description: |- @@ -14281,10 +17476,69 @@ spec: metadata: type: object spec: - description: Spec defines the desired state of UDPRoute. + description: Spec defines the desired state of TLSRoute. properties: + hostnames: + description: |- + Hostnames defines a set of SNI hostnames that should match against the + SNI attribute of TLS ClientHello message in TLS handshake. This matches + the RFC 1123 definition of a hostname with 2 notable exceptions: + + 1. IPs are not allowed in SNI hostnames per RFC 6066. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + If a hostname is specified by both the Listener and TLSRoute, there + must be at least one intersecting hostname for the TLSRoute to be + attached to the Listener. For example: + + * A Listener with `test.example.com` as the hostname matches TLSRoutes + that have specified at least one of `test.example.com` or + `*.example.com`. + * A Listener with `*.example.com` as the hostname matches TLSRoutes + that have specified at least one hostname that matches the Listener + hostname. For example, `test.example.com` and `*.example.com` would both + match. On the other hand, `example.com` and `test.example.net` would not + match. + + If both the Listener and TLSRoute have specified hostnames, any + TLSRoute hostnames that do not match the Listener hostname MUST be + ignored. For example, if a Listener specified `*.example.com`, and the + TLSRoute specified `test.example.com` and `test.example.net`, + `test.example.net` must not be considered for a match. + + If both the Listener and TLSRoute have specified hostnames, and none + match with the criteria above, then the TLSRoute is not accepted. The + implementation must raise an 'Accepted' Condition with a status of + `False` in the corresponding RouteParentStatus. + + Support: Core + items: + description: |- + Hostname is the fully qualified domain name of a network host. This matches + the RFC 1123 definition of a hostname with 2 notable exceptions: + + 1. IPs are not allowed. + 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard + label must appear by itself as the first label. + + Hostname can be "precise" which is a domain name without the terminating + dot of a network host (e.g. "foo.example.com") or "wildcard", which is a + domain name prefixed with a single wildcard label (e.g. `*.example.com`). + + Note that as per RFC1035 and RFC1123, a *label* must consist of lower case + alphanumeric characters or '-', and must start and end with an alphanumeric + character. No other punctuation is allowed. + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + maxItems: 16 + minItems: 1 + type: array + x-kubernetes-list-type: atomic parentRefs: - description: |+ + description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means @@ -14346,11 +17600,6 @@ spec: connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. - - - - - items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered @@ -14500,6 +17749,7 @@ spec: type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent @@ -14524,20 +17774,21 @@ spec: || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port)))) rules: - description: |+ - Rules are a list of UDP matchers and actions. - + description: Rules are a list of actions. items: - description: UDPRouteRule is the configuration for a given rule. + description: TLSRouteRule is the configuration for a given rule. properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be - sent. If unspecified or invalid (refers to a non-existent resource or a - Service with no endpoints), the underlying implementation MUST actively - reject connection attempts to this backend. Packet drops must - respect weight; if an invalid backend is requested to have 80% of - the packets, then 80% of packets must be dropped instead. + sent. If unspecified or invalid (refers to a nonexistent resource or + a Service with no endpoints), the rule performs no forwarding; if no + filters are specified that would result in a response being sent, the + underlying implementation must actively reject request attempts to this + backend, by rejecting the connection or returning a 500 status code. + Request rejections must respect weight; if an invalid backend is + requested to have 80% of requests, then 80% of requests must be rejected + instead. Support: Core for Kubernetes Service @@ -14556,7 +17807,6 @@ spec: namespace's owner to accept the reference. See the ReferenceGrant documentation for details. - When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. @@ -14572,7 +17822,6 @@ spec: protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. - Note that when the BackendTLSPolicy object is enabled by the implementation, there are some extra rules about validity to consider here. See the fields @@ -14668,6 +17917,7 @@ spec: maxItems: 16 minItems: 1 type: array + x-kubernetes-list-type: atomic name: description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. @@ -14677,19 +17927,41 @@ spec: minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string + required: + - backendRefs type: object - maxItems: 16 + maxItems: 1 minItems: 1 type: array + x-kubernetes-list-type: atomic x-kubernetes-validations: - 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)) + useDefaultGateways: + description: |- + 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. + enum: + - All + - None + type: string required: + - hostnames - rules type: object status: - description: Status defines the current state of UDPRoute. + description: Status defines the current state of TLSRoute. properties: parents: description: |- @@ -14730,7 +18002,7 @@ spec: 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. items: @@ -14951,11 +18223,13 @@ spec: - name type: object required: + - conditions - controllerName - parentRef type: object maxItems: 32 type: array + x-kubernetes-list-type: atomic required: - parents type: object @@ -14972,3 +18246,2416 @@ status: plural: "" conditions: null storedVersions: null +--- +# +# config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.4.0 + gateway.networking.k8s.io/channel: experimental + name: udproutes.gateway.networking.k8s.io +spec: + group: gateway.networking.k8s.io + names: + categories: + - gateway-api + kind: UDPRoute + listKind: UDPRouteList + plural: udproutes + singular: udproute + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha2 + schema: + openAPIV3Schema: + description: |- + UDPRoute provides a way to route UDP traffic. When combined with a Gateway + listener, it can be used to forward traffic on the port specified by the + listener to a set of backends specified by the UDPRoute. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of UDPRoute. + properties: + parentRefs: + description: |- + ParentRefs references the resources (usually Gateways) that a Route wants + to be attached to. Note that the referenced parent resource needs to + allow this for the attachment to be complete. For Gateways, that means + the Gateway needs to allow attachment from Routes of this kind and + namespace. For Services, that means the Service must either be in the same + namespace for a "producer" route, or the mesh implementation must support + and allow "consumer" routes for the referenced Service. ReferenceGrant is + not applicable for governing ParentRefs to Services - it is not possible to + create a "producer" route for a Service in a different namespace from the + Route. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + This API may be extended in the future to support additional kinds of parent + resources. + + ParentRefs must be _distinct_. This means either that: + + * They select different objects. If this is the case, then parentRef + entries are distinct. In terms of fields, this means that the + multi-part key defined by `group`, `kind`, `namespace`, and `name` must + be unique across all parentRef entries in the Route. + * They do not select different objects, but for each optional field used, + each ParentRef that selects the same object must set the same set of + optional fields to different values. If one ParentRef sets a + combination of optional fields, all must set the same combination. + + Some examples: + + * If one ParentRef sets `sectionName`, all ParentRefs referencing the + same object must also set `sectionName`. + * If one ParentRef sets `port`, all ParentRefs referencing the same + object must also set `port`. + * If one ParentRef sets `sectionName` and `port`, all ParentRefs + referencing the same object must also set `sectionName` and `port`. + + It is possible to separately reference multiple distinct objects that may + be collapsed by an implementation. For example, some implementations may + choose to merge compatible Gateway Listeners together. If that is the + case, the list of routes attached to those resources should also be + merged. + + Note that for ParentRefs that cross namespace boundaries, there are specific + rules. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example, + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable other kinds of cross-namespace reference. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + items: + description: |- + ParentReference identifies an API object (usually a Gateway) that can be considered + a parent of this resource (usually a route). There are two kinds of parent resources + with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + This API may be extended in the future to support additional kinds of parent + resources. + + 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. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + When the parent resource is a Service, this targets a specific port in the + Service spec. When both Port (experimental) and SectionName are specified, + the name and port of the selected port must match both specified values. + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + 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 + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + 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 + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + maxItems: 32 + type: array + x-kubernetes-list-type: atomic + x-kubernetes-validations: + - 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))' + - 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)))) + rules: + description: Rules are a list of UDP matchers and actions. + items: + description: UDPRouteRule is the configuration for a given rule. + properties: + backendRefs: + description: |- + BackendRefs defines the backend(s) where matching requests should be + sent. If unspecified or invalid (refers to a nonexistent resource or a + Service with no endpoints), the underlying implementation MUST actively + reject connection attempts to this backend. Packet drops must + respect weight; if an invalid backend is requested to have 80% of + the packets, then 80% of packets must be dropped instead. + + Support: Core for Kubernetes Service + + Support: Extended for Kubernetes ServiceImport + + Support: Implementation-specific for any other resource + + Support for weight: Extended + items: + description: |- + BackendRef defines how a Route should forward a request to a Kubernetes + resource. + + 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. + + + When the BackendRef points to a Kubernetes Service, implementations SHOULD + honor the appProtocol field if it is set for the target Service Port. + + Implementations supporting appProtocol SHOULD recognize the Kubernetes + Standard Application Protocols defined in KEP-3726. + + If a Service appProtocol isn't specified, an implementation MAY infer the + backend protocol through its own means. Implementations MAY infer the + protocol from the Route type referring to the backend Service. + + If a Route is not able to send traffic to the backend using the specified + protocol then the backend is considered invalid. Implementations MUST set the + "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. + + + Note that when the BackendTLSPolicy object is enabled by the implementation, + there are some extra rules about validity to consider here. See the fields + where this struct is used for more information about the exact behavior. + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Service + description: |- + 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) + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the backend. 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port specifies the destination port number to use for this resource. + Port is required when the referent is a Kubernetes Service. In this + case, the port number is the service port number, not the target port. + For other resources, destination port might be derived from the referent + resource or this field. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + weight: + default: 1 + description: |- + Weight specifies the proportion of requests forwarded to the referenced + backend. This is computed as weight/(sum of all weights in this + BackendRefs list). For non-zero values, there may be some epsilon from + the exact proportion defined here depending on the precision an + implementation supports. Weight is not a percentage and the sum of + weights does not need to equal 100. + + If only one backend is specified and it has a weight greater than 0, 100% + of the traffic is forwarded to that backend. If weight is set to 0, no + traffic should be forwarded for this entry. If unspecified, weight + defaults to 1. + + Support for this field varies based on the context where used. + format: int32 + maximum: 1000000 + minimum: 0 + type: integer + required: + - name + type: object + x-kubernetes-validations: + - message: Must have port for Service reference + rule: '(size(self.group) == 0 && self.kind == ''Service'') + ? has(self.port) : true' + maxItems: 16 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + name: + description: |- + Name is the name of the route rule. This name MUST be unique within a Route if it is set. + + Support: Extended + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - backendRefs + type: object + maxItems: 16 + minItems: 1 + type: array + x-kubernetes-list-type: atomic + x-kubernetes-validations: + - 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)) + useDefaultGateways: + description: |- + 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. + enum: + - All + - None + type: string + required: + - rules + type: object + status: + description: Status defines the current state of UDPRoute. + properties: + parents: + description: |- + Parents is a list of parent resources (usually Gateways) that are + associated with the route, and the status of the route with respect to + each parent. When this route attaches to a parent, the controller that + manages the parent must add an entry to this list when the controller + first sees the route and should update the entry as appropriate when the + route or gateway is modified. + + Note that parent references that cannot be resolved by an implementation + of this API will not be added to this list. Implementations of this API + can only populate Route status for the Gateways/parent resources they are + responsible for. + + A maximum of 32 Gateways will be represented in this list. An empty list + means the route has not been attached to any Gateway. + items: + description: |- + RouteParentStatus describes the status of a route with respect to an + associated Parent. + properties: + conditions: + description: |- + Conditions describes the status of the route with respect to the Gateway. + Note that the route's availability is also subject to the Gateway's own + status conditions and listener status. + + If the Route's ParentRef specifies an existing Gateway that supports + Routes of this kind AND that Gateway's controller has sufficient access, + then that Gateway's controller MUST set the "Accepted" condition on the + Route, to indicate whether the route has been accepted or rejected by the + Gateway, and why. + + A Route MUST be considered "Accepted" if at least one of the Route's + rules is implemented by the Gateway. + + 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 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. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: |- + ControllerName is a domain/path string that indicates the name of the + controller that wrote this status. This corresponds with the + controllerName field on GatewayClass. + + Example: "example.net/gateway-controller". + + The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are + valid Kubernetes names + (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + + 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. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + parentRef: + description: |- + ParentRef corresponds with a ParentRef in the spec that this + RouteParentStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + When the parent resource is a Service, this targets a specific port in the + Service spec. When both Port (experimental) and SectionName are specified, + the name and port of the selected port must match both specified values. + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + 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 + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + 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 + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + required: + - conditions + - controllerName + - parentRef + type: object + maxItems: 32 + type: array + x-kubernetes-list-type: atomic + required: + - parents + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null +--- +# +# config/crd/experimental/gateway.networking.x-k8s.io_xbackendtrafficpolicies.yaml +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.4.0 + gateway.networking.k8s.io/channel: experimental + labels: + gateway.networking.k8s.io/policy: Direct + name: xbackendtrafficpolicies.gateway.networking.x-k8s.io +spec: + group: gateway.networking.x-k8s.io + names: + categories: + - gateway-api + kind: XBackendTrafficPolicy + listKind: XBackendTrafficPolicyList + plural: xbackendtrafficpolicies + shortNames: + - xbtrafficpolicy + singular: xbackendtrafficpolicy + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + XBackendTrafficPolicy defines the configuration for how traffic to a + target backend should be handled. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of BackendTrafficPolicy. + properties: + retryConstraint: + description: |- + RetryConstraint defines the configuration for when to allow or prevent + further retries to a target backend, by dynamically calculating a 'retry + budget'. This budget is calculated based on the percentage of incoming + traffic composed of retries over a given time interval. Once the budget + is exceeded, additional retries will be rejected. + + For example, if the retry budget interval is 10 seconds, there have been + 1000 active requests in the past 10 seconds, and the allowed percentage + of requests that can be retried is 20% (the default), then 200 of those + requests may be composed of retries. Active requests will only be + considered for the duration of the interval when calculating the retry + budget. Retrying the same original request multiple times within the + retry budget interval will lead to each retry being counted towards + calculating the budget. + + Configuring a RetryConstraint in BackendTrafficPolicy is compatible with + HTTPRoute Retry settings for each HTTPRouteRule that targets the same + backend. While the HTTPRouteRule Retry stanza can specify whether a + request will be retried, and the number of retry attempts each client + may perform, RetryConstraint helps prevent cascading failures such as + retry storms during periods of consistent failures. + + After the retry budget has been exceeded, additional retries to the + backend MUST return a 503 response to the client. + + Additional configurations for defining a constraint on retries MAY be + defined in the future. + + Support: Extended + properties: + budget: + default: + interval: 10s + percent: 20 + description: Budget holds the details of the retry budget configuration. + properties: + interval: + default: 10s + description: |- + Interval defines the duration in which requests will be considered + for calculating the budget for retries. + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + x-kubernetes-validations: + - message: interval can not be greater than one hour or less + than one second + rule: '!(duration(self) < duration(''1s'') || duration(self) + > duration(''1h''))' + percent: + default: 20 + description: |- + Percent defines the maximum percentage of active requests that may + be made up of retries. + + Support: Extended + maximum: 100 + minimum: 0 + type: integer + type: object + minRetryRate: + default: + count: 10 + interval: 1s + description: |- + MinRetryRate defines the minimum rate of retries that will be allowable + over a specified duration of time. + + The effective overall minimum rate of retries targeting the backend + service may be much higher, as there can be any number of clients which + are applying this setting locally. + + This ensures that requests can still be retried during periods of low + traffic, where the budget for retries may be calculated as a very low + value. + + Support: Extended + properties: + count: + description: |- + Count specifies the number of requests per time interval. + + Support: Extended + maximum: 1000000 + minimum: 1 + type: integer + interval: + description: |- + Interval specifies the divisor of the rate of requests, the amount of + time during which the given count of requests occur. + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + x-kubernetes-validations: + - message: interval can not be greater than one hour + rule: '!(duration(self) == duration(''0s'') || duration(self) + > duration(''1h''))' + type: object + type: object + sessionPersistence: + description: |- + SessionPersistence defines and configures session persistence + for the backend. + + Support: Extended + properties: + absoluteTimeout: + description: |- + AbsoluteTimeout defines the absolute timeout of the persistent + session. Once the AbsoluteTimeout duration has elapsed, the + session becomes invalid. + + Support: Extended + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + cookieConfig: + description: |- + CookieConfig provides configuration settings that are specific + to cookie-based session persistence. + + Support: Core + properties: + lifetimeType: + default: Session + description: |- + 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 + enum: + - Permanent + - Session + type: string + type: object + idleTimeout: + description: |- + 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 + pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ + type: string + sessionName: + description: |- + 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 + maxLength: 128 + type: string + type: + default: Cookie + description: |- + 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 + enum: + - Cookie + - Header + type: string + type: object + x-kubernetes-validations: + - 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)' + targetRefs: + description: |- + TargetRefs identifies API object(s) to apply this policy to. + Currently, Backends (A grouping of like endpoints such as Service, + ServiceImport, or any implementation-specific backendRef) are the only + valid API target references. + + Currently, a TargetRef can not be scoped to a specific port on a + Service. + items: + description: |- + LocalPolicyTargetReference identifies an API object to apply a direct or + inherited policy to. This should be used as part of Policy resources + that can target Gateway API resources. For more information on how this + policy attachment model works, and a sample Policy resource, refer to + the policy attachment documentation for Gateway API. + properties: + group: + description: Group is the group of the target resource. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the target resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the target resource. + maxLength: 253 + minLength: 1 + type: string + required: + - group + - kind + - name + type: object + maxItems: 16 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - group + - kind + - name + x-kubernetes-list-type: map + required: + - targetRefs + type: object + status: + description: Status defines the current state of BackendTrafficPolicy. + properties: + ancestors: + description: |- + Ancestors is a list of ancestor resources (usually Gateways) that are + associated with the policy, and the status of the policy with respect to + each ancestor. When this policy attaches to a parent, the controller that + manages the parent and the ancestors MUST add an entry to this list when + the controller first sees the policy and SHOULD update the entry as + appropriate when the relevant ancestor is modified. + + Note that choosing the relevant ancestor is left to the Policy designers; + an important part of Policy design is designing the right object level at + which to namespace this status. + + Note also that implementations MUST ONLY populate ancestor status for + the Ancestor resources they are responsible for. Implementations MUST + use the ControllerName field to uniquely identify the entries in this list + that they are responsible for. + + Note that to achieve this, the list of PolicyAncestorStatus structs + MUST be treated as a map with a composite key, made up of the AncestorRef + and ControllerName fields combined. + + A maximum of 16 ancestors will be represented in this list. An empty list + means the Policy is not relevant for any ancestors. + + If this slice is full, implementations MUST NOT add further entries. + Instead they MUST consider the policy unimplementable and signal that + on any related resources such as the ancestor that would be referenced + here. For example, if this list was full on BackendTLSPolicy, no + additional Gateways would be able to reference the Service targeted by + the BackendTLSPolicy. + items: + description: |- + PolicyAncestorStatus describes the status of a route with respect to an + associated Ancestor. + + Ancestors refer to objects that are either the Target of a policy or above it + in terms of object hierarchy. For example, if a policy targets a Service, the + Policy's Ancestors are, in order, the Service, the HTTPRoute, the Gateway, and + the GatewayClass. Almost always, in this hierarchy, the Gateway will be the most + useful object to place Policy status on, so we recommend that implementations + SHOULD use Gateway as the PolicyAncestorStatus object unless the designers + have a _very_ good reason otherwise. + + In the context of policy attachment, the Ancestor is used to distinguish which + resource results in a distinct application of this policy. For example, if a policy + targets a Service, it may have a distinct result per attached Gateway. + + Policies targeting the same resource may have different effects depending on the + ancestors of those resources. For example, different Gateways targeting the same + Service may have different capabilities, especially if they have different underlying + implementations. + + For example, in BackendTLSPolicy, the Policy attaches to a Service that is + used as a backend in a HTTPRoute that is itself attached to a Gateway. + In this case, the relevant object for status is the Gateway, and that is the + ancestor object referred to in this status. + + Note that a parent is also an ancestor, so for objects where the parent is the + relevant object for status, this struct SHOULD still be used. + + This struct is intended to be used in a slice that's effectively a map, + with a composite key made up of the AncestorRef and the ControllerName. + properties: + ancestorRef: + description: |- + AncestorRef corresponds with a ParentRef in the spec that this + PolicyAncestorStatus struct describes the status of. + properties: + group: + default: gateway.networking.k8s.io + description: |- + Group is the group of the referent. + When unspecified, "gateway.networking.k8s.io" is inferred. + To set the core API group (such as for a "Service" kind referent), + Group must be explicitly set to "" (empty string). + + Support: Core + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: |- + Kind is kind of the referent. + + There are two kinds of parent resources with "Core" support: + + * Gateway (Gateway conformance profile) + * Service (Mesh conformance profile, ClusterIP Services only) + + Support for other resources is Implementation-Specific. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: |- + Name is the name of the referent. + + Support: Core + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. When unspecified, this refers + to the local namespace of the Route. + + Note that there are specific rules for ParentRefs which cross namespace + boundaries. Cross-namespace references are only valid if they are explicitly + allowed by something in the namespace they are referring to. For example: + Gateway has the AllowedRoutes field, and ReferenceGrant provides a + generic way to enable any other kind of cross-namespace reference. + + + ParentRefs from a Route to a Service in the same namespace are "producer" + routes, which apply default routing rules to inbound connections from + any namespace to the Service. + + ParentRefs from a Route to a Service in a different namespace are + "consumer" routes, and these routing rules are only applied to outbound + connections originating from the same namespace as the Route, for which + the intended destination of the connections are a Service targeted as a + ParentRef of the Route. + + + Support: Core + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + port: + description: |- + Port is the network port this Route targets. It can be interpreted + differently based on the type of parent resource. + + When the parent resource is a Gateway, this targets all listeners + listening on the specified port that also support this kind of Route(and + select this Route). It's not recommended to set `Port` unless the + networking behaviors specified in a Route must apply to a specific port + as opposed to a listener(s) whose port(s) may be changed. When both Port + and SectionName are specified, the name and port of the selected listener + must match both specified values. + + + When the parent resource is a Service, this targets a specific port in the + Service spec. When both Port (experimental) and SectionName are specified, + the name and port of the selected port must match both specified values. + + + Implementations MAY choose to support other parent resources. + Implementations supporting other types of parent resources MUST clearly + document how/if Port is interpreted. + + For the purpose of status, an attachment is considered successful as + long as the parent resource accepts it partially. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment + from the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, + the Route MUST be considered detached from the Gateway. + + Support: Extended + format: int32 + maximum: 65535 + minimum: 1 + type: integer + sectionName: + description: |- + 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 + are specified, the name and port of the selected listener must match + both specified values. + * Service: Port name. When both Port (experimental) and SectionName + are specified, the name and port of the selected listener must match + 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 + interpreted. + + When unspecified (empty string), this will reference the entire resource. + For the purpose of status, an attachment is considered successful if at + least one section in the parent resource accepts it. For example, Gateway + listeners can restrict which Routes can attach to them by Route kind, + namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from + the referencing Route, the Route MUST be considered successfully + attached. If no Gateway listeners accept attachment from this Route, the + Route MUST be considered detached from the Gateway. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + required: + - name + type: object + conditions: + description: Conditions describes the status of the Policy with + respect to the given Ancestor. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + controllerName: + description: |- + ControllerName is a domain/path string that indicates the name of the + controller that wrote this status. This corresponds with the + controllerName field on GatewayClass. + + Example: "example.net/gateway-controller". + + The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are + valid Kubernetes names + (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). + + 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. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + required: + - ancestorRef + - conditions + - controllerName + type: object + maxItems: 16 + type: array + x-kubernetes-list-type: atomic + required: + - ancestors + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null +--- +# +# config/crd/experimental/gateway.networking.x-k8s.io_xlistenersets.yaml +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.4.0 + gateway.networking.k8s.io/channel: experimental + name: xlistenersets.gateway.networking.x-k8s.io +spec: + group: gateway.networking.x-k8s.io + names: + categories: + - gateway-api + kind: XListenerSet + listKind: XListenerSetList + plural: xlistenersets + shortNames: + - lset + singular: xlistenerset + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Accepted")].status + name: Accepted + type: string + - jsonPath: .status.conditions[?(@.type=="Programmed")].status + name: Programmed + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + XListenerSet defines a set of additional listeners to attach to an existing Gateway. + This resource provides a mechanism to merge multiple listeners into a single Gateway. + + The parent Gateway must explicitly allow ListenerSet attachment through its + AllowedListeners configuration. By default, Gateways do not allow ListenerSet + attachment. + + Routes can attach to a ListenerSet by specifying it as a parentRef, and can + optionally target specific listeners using the sectionName field. + + Policy Attachment: + - Policies that attach to a ListenerSet apply to all listeners defined in that resource + - Policies do not impact listeners in the parent Gateway + - Different ListenerSets attached to the same Gateway can have different policies + - If an implementation cannot apply a policy to specific listeners, it should reject the policy + + ReferenceGrant Semantics: + - ReferenceGrants applied to a Gateway are not inherited by child ListenerSets + - ReferenceGrants applied to a ListenerSet do not grant permission to the parent Gateway's listeners + - A ListenerSet can reference secrets/backends in its own namespace without a ReferenceGrant + + Gateway Integration: + - The parent Gateway's status will include an "AttachedListenerSets" condition + - This condition will be: + - True: when AllowedListeners is set and at least one child ListenerSet is attached + - False: when AllowedListeners is set but no valid listeners are attached, or when AllowedListeners is not set or false + - Unknown: when no AllowedListeners config is present + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of ListenerSet. + properties: + listeners: + description: |- + Listeners associated with this ListenerSet. Listeners define + logical endpoints that are bound on this referenced parent Gateway's addresses. + + Listeners in a `Gateway` and their attached `ListenerSets` are concatenated + as a list when programming the underlying infrastructure. Each listener + name does not need to be unique across the Gateway and ListenerSets. + See ListenerEntry.Name for more details. + + Implementations MUST treat the parent Gateway as having the merged + list of all listeners from itself and attached ListenerSets using + the following precedence: + + 1. "parent" Gateway + 2. ListenerSet ordered by creation time (oldest first) + 3. ListenerSet ordered alphabetically by "{namespace}/{name}". + + An implementation MAY reject listeners by setting the ListenerEntryStatus + `Accepted` condition to False with the Reason `TooManyListeners` + + If a listener has a conflict, this will be reported in the + Status.ListenerEntryStatus setting the `Conflicted` condition to True. + + Implementations SHOULD be cautious about what information from the + parent or siblings are reported to avoid accidentally leaking + sensitive information that the child would not otherwise have access + to. This can include contents of secrets etc. + items: + properties: + allowedRoutes: + default: + namespaces: + from: Same + description: |- + AllowedRoutes defines the types of routes that MAY be attached to a + Listener and the trusted namespaces where those Route resources MAY be + present. + + Although a client request may match multiple route rules, only one rule + may ultimately receive the request. Matching precedence MUST be + determined in order of the following criteria: + + * The most specific match as defined by the Route type. + * The oldest Route based on creation timestamp. For example, a Route with + a creation timestamp of "2020-09-08 01:02:03" is given precedence over + a Route with a creation timestamp of "2020-09-08 01:02:04". + * If everything else is equivalent, the Route appearing first in + alphabetical order (namespace/name) should be given precedence. For + example, foo/bar is given precedence over foo/baz. + + All valid rules within a Route attached to this Listener should be + implemented. Invalid Route rules can be ignored (sometimes that will mean + the full Route). If a Route rule transitions from valid to invalid, + support for that Route rule should be dropped to ensure consistency. For + example, even if a filter specified by a Route rule is invalid, the rest + of the rules within that Route should still be supported. + properties: + kinds: + description: |- + Kinds specifies the groups and kinds of Routes that are allowed to bind + to this Gateway Listener. When unspecified or empty, the kinds of Routes + selected are determined using the Listener protocol. + + A RouteGroupKind MUST correspond to kinds of Routes that are compatible + with the application protocol specified in the Listener's Protocol field. + If an implementation does not support or recognize this resource type, it + MUST set the "ResolvedRefs" condition to False for this Listener with the + "InvalidRouteKinds" reason. + + Support: Core + items: + description: RouteGroupKind indicates the group and kind + of a Route resource. + properties: + group: + default: gateway.networking.k8s.io + description: Group is the group of the Route. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is the kind of the Route. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + required: + - kind + type: object + maxItems: 8 + type: array + x-kubernetes-list-type: atomic + namespaces: + default: + from: Same + description: |- + Namespaces indicates namespaces from which Routes may be attached to this + Listener. This is restricted to the namespace of this Gateway by default. + + Support: Core + properties: + from: + default: Same + description: |- + From indicates where Routes will be selected for this Gateway. Possible + values are: + + * All: Routes in all namespaces may be used by this Gateway. + * Selector: Routes in namespaces selected by the selector may be used by + this Gateway. + * Same: Only Routes in the same namespace may be used by this Gateway. + + Support: Core + enum: + - All + - Selector + - Same + type: string + selector: + description: |- + Selector must be specified when From is set to "Selector". In that case, + only Routes in Namespaces matching this Selector will be selected by this + Gateway. This field is ignored for other values of "From". + + Support: Core + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: object + type: object + hostname: + description: |- + Hostname specifies the virtual hostname to match for protocol types that + define this concept. When unspecified, all hostnames are matched. This + field is ignored for protocols that don't require hostname based + matching. + + Implementations MUST apply Hostname matching appropriately for each of + the following protocols: + + * TLS: The Listener Hostname MUST match the SNI. + * HTTP: The Listener Hostname MUST match the Host header of the request. + * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP + protocol layers as described above. If an implementation does not + ensure that both the SNI and Host header match the Listener hostname, + it MUST clearly document that. + + For HTTPRoute and TLSRoute resources, there is an interaction with the + `spec.hostnames` array. When both listener and route specify hostnames, + there MUST be an intersection between the values for a Route to be + accepted. For more information, refer to the Route specific Hostnames + documentation. + + Hostnames that are prefixed with a wildcard label (`*.`) are interpreted + as a suffix match. That means that a match for `*.example.com` would match + both `test.example.com`, and `foo.test.example.com`, but not `example.com`. + maxLength: 253 + minLength: 1 + pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + name: + description: |- + Name is the name of the Listener. This name MUST be unique within a + ListenerSet. + + Name is not required to be unique across a Gateway and ListenerSets. + Routes can attach to a Listener by having a ListenerSet as a parentRef + and setting the SectionName + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + port: + default: 0 + description: |- + Port is the network port. Multiple listeners may use the + same port, subject to the Listener compatibility rules. + + If the port is not set or specified as zero, the implementation will assign + a unique port. If the implementation does not support dynamic port + assignment, it MUST set `Accepted` condition to `False` with the + `UnsupportedPort` reason. + format: int32 + maximum: 65535 + minimum: 0 + type: integer + protocol: + description: Protocol specifies the network protocol this listener + expects to receive. + maxLength: 255 + minLength: 1 + pattern: ^[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ + type: string + tls: + description: |- + TLS is the TLS configuration for the Listener. This field is required if + the Protocol field is "HTTPS" or "TLS". It is invalid to set this field + if the Protocol field is "HTTP", "TCP", or "UDP". + + The association of SNIs to Certificate defined in ListenerTLSConfig is + defined based on the Hostname field for this listener. + + The GatewayClass MUST use the longest matching SNI out of all + available certificates for any TLS handshake. + properties: + certificateRefs: + description: |- + CertificateRefs contains a series of references to Kubernetes objects that + contains TLS certificates and private keys. These certificates are used to + establish a TLS handshake for requests that match the hostname of the + associated listener. + + A single CertificateRef to a Kubernetes Secret has "Core" support. + Implementations MAY choose to support attaching multiple certificates to + a Listener, but this behavior is implementation-specific. + + References to a resource in different namespace are invalid UNLESS there + is a ReferenceGrant in the target namespace that allows the certificate + to be attached. If a ReferenceGrant does not allow this reference, the + "ResolvedRefs" condition MUST be set to False for this listener with the + "RefNotPermitted" reason. + + This field is required to have at least one element when the mode is set + to "Terminate" (default) and is optional otherwise. + + CertificateRefs can reference to standard Kubernetes resources, i.e. + Secret, or implementation-specific custom resources. + + Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls + + Support: Implementation-specific (More than one reference or other resource types) + items: + description: |- + SecretObjectReference identifies an API object including its namespace, + defaulting to Secret. + + 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. + properties: + group: + default: "" + description: |- + Group is the group of the referent. For example, "gateway.networking.k8s.io". + When unspecified or empty string, core API group is inferred. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Secret + description: Kind is kind of the referent. For example + "Secret". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + 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 + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - name + type: object + maxItems: 64 + type: array + x-kubernetes-list-type: atomic + mode: + default: Terminate + description: |- + Mode defines the TLS behavior for the TLS session initiated by the client. + There are two possible modes: + + - Terminate: The TLS session between the downstream client and the + Gateway is terminated at the Gateway. This mode requires certificates + to be specified in some way, such as populating the certificateRefs + field. + - Passthrough: The TLS session is NOT terminated by the Gateway. This + implies that the Gateway can't decipher the TLS stream except for + the ClientHello message of the TLS protocol. The certificateRefs field + is ignored in this mode. + + Support: Core + enum: + - Terminate + - Passthrough + type: string + options: + additionalProperties: + description: |- + AnnotationValue is the value of an annotation in Gateway API. This is used + for validation of maps such as TLS options. This roughly matches Kubernetes + annotation validation, although the length validation in that case is based + on the entire size of the annotations struct. + maxLength: 4096 + minLength: 0 + type: string + description: |- + Options are a list of key/value pairs to enable extended TLS + configuration for each implementation. For example, configuring the + minimum TLS version or supported cipher suites. + + A set of common keys MAY be defined by the API in the future. To avoid + any ambiguity, implementation-specific definitions MUST use + domain-prefixed names, such as `example.com/my-custom-option`. + Un-prefixed names are reserved for key names defined by Gateway API. + + Support: Implementation-specific + maxProperties: 16 + type: object + type: object + x-kubernetes-validations: + - message: certificateRefs or options must be specified when + mode is Terminate + rule: 'self.mode == ''Terminate'' ? size(self.certificateRefs) + > 0 || size(self.options) > 0 : true' + required: + - name + - protocol + type: object + maxItems: 64 + minItems: 1 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + x-kubernetes-validations: + - message: tls must not be specified for protocols ['HTTP', 'TCP', + 'UDP'] + rule: 'self.all(l, l.protocol in [''HTTP'', ''TCP'', ''UDP''] ? + !has(l.tls) : true)' + - message: tls mode must be Terminate for protocol HTTPS + rule: 'self.all(l, (l.protocol == ''HTTPS'' && has(l.tls)) ? (l.tls.mode + == '''' || l.tls.mode == ''Terminate'') : true)' + - message: hostname must not be specified for protocols ['TCP', 'UDP'] + rule: 'self.all(l, l.protocol in [''TCP'', ''UDP''] ? (!has(l.hostname) + || l.hostname == '''') : true)' + - message: Listener name must be unique within the Gateway + rule: self.all(l1, self.exists_one(l2, l1.name == l2.name)) + - message: Combination of port, protocol and hostname must be unique + for each listener + rule: 'self.all(l1, !has(l1.port) || self.exists_one(l2, has(l2.port) + && l1.port == l2.port && l1.protocol == l2.protocol && (has(l1.hostname) + && has(l2.hostname) ? l1.hostname == l2.hostname : !has(l1.hostname) + && !has(l2.hostname))))' + parentRef: + description: ParentRef references the Gateway that the listeners are + attached to. + properties: + group: + default: gateway.networking.k8s.io + description: Group is the group of the referent. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + default: Gateway + description: Kind is kind of the referent. For example "Gateway". + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. If not present, + the namespace of the referent is assumed to be the same as + the namespace of the referring object. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - name + type: object + required: + - listeners + - parentRef + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Programmed + description: Status defines the current state of ListenerSet. + properties: + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Programmed + description: |- + Conditions describe the current conditions of the ListenerSet. + + Implementations MUST express ListenerSet conditions using the + `ListenerSetConditionType` and `ListenerSetConditionReason` + constants so that operators and tools can converge on a common + vocabulary to describe ListenerSet state. + + Known condition types are: + + * "Accepted" + * "Programmed" + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + listeners: + description: Listeners provide status for each unique listener port + defined in the Spec. + items: + description: ListenerStatus is the status associated with a Listener. + properties: + attachedRoutes: + description: |- + AttachedRoutes represents the total number of Routes that have been + successfully attached to this Listener. + + Successful attachment of a Route to a Listener is based solely on the + combination of the AllowedRoutes field on the corresponding Listener + and the Route's ParentRefs field. A Route is successfully attached to + a Listener when it is selected by the Listener's AllowedRoutes field + AND the Route has a valid ParentRef selecting the whole Gateway + resource or a specific Listener as a parent resource (more detail on + attachment semantics can be found in the documentation on the various + Route kinds ParentRefs fields). Listener or Route status does not impact + successful attachment, i.e. the AttachedRoutes field count MUST be set + for Listeners with condition Accepted: false and MUST count successfully + attached Routes that may themselves have Accepted: false conditions. + + Uses for this field include troubleshooting Route attachment and + measuring blast radius/impact of changes to a Listener. + format: int32 + type: integer + conditions: + description: Conditions describe the current condition of this + listener. + items: + description: Condition contains details for one aspect of + the current state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, + Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + name: + description: Name is the name of the Listener that this status + corresponds to. + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + port: + description: Port is the network port the listener is configured + to listen on. + format: int32 + maximum: 65535 + minimum: 1 + type: integer + supportedKinds: + description: |- + SupportedKinds is the list indicating the Kinds supported by this + listener. This MUST represent the kinds an implementation supports for + that Listener configuration. + + If kinds are specified in Spec that are not supported, they MUST NOT + appear in this list and an implementation MUST set the "ResolvedRefs" + condition to "False" with the "InvalidRouteKinds" reason. If both valid + and invalid Route kinds are specified, the implementation MUST + reference the valid Route kinds that have been specified. + items: + description: RouteGroupKind indicates the group and kind of + a Route resource. + properties: + group: + default: gateway.networking.k8s.io + description: Group is the group of the Route. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is the kind of the Route. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + required: + - kind + type: object + maxItems: 8 + type: array + x-kubernetes-list-type: atomic + required: + - attachedRoutes + - conditions + - name + - port + - supportedKinds + type: object + maxItems: 64 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null +--- +# +# config/crd/experimental/gateway.networking.x-k8s.io_xmeshes.yaml +# +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 + gateway.networking.k8s.io/bundle-version: v1.4.0 + gateway.networking.k8s.io/channel: experimental + name: xmeshes.gateway.networking.x-k8s.io +spec: + group: gateway.networking.x-k8s.io + names: + categories: + - gateway-api + kind: XMesh + listKind: XMeshList + plural: xmeshes + shortNames: + - mesh + singular: xmesh + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=="Accepted")].status + name: Accepted + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: XMesh defines mesh-wide characteristics of a GAMMA-compliant + service mesh. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec defines the desired state of XMesh. + properties: + controllerName: + description: |- + ControllerName is the name of a controller that is managing Gateway API + resources for mesh traffic management. The value of this field MUST be a + domain prefixed path. + + Example: "example.com/awesome-mesh". + + This field is not mutable and cannot be empty. + + Support: Core + maxLength: 253 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + description: + description: Description optionally provides a human-readable description + of a Mesh. + maxLength: 64 + type: string + parametersRef: + description: |- + ParametersRef is an optional reference to a resource that contains + implementation-specific configuration for this Mesh. If no + implementation-specific parameters are needed, this field MUST be + omitted. + + ParametersRef can reference a standard Kubernetes resource, i.e. + ConfigMap, or an implementation-specific custom resource. The resource + can be cluster-scoped or namespace-scoped. + + If the referent cannot be found, refers to an unsupported kind, or when + the data within that resource is malformed, the Mesh MUST be rejected + with the "Accepted" status condition set to "False" and an + "InvalidParameters" reason. + + Support: Implementation-specific + properties: + group: + description: Group is the group of the referent. + maxLength: 253 + pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ + type: string + kind: + description: Kind is kind of the referent. + maxLength: 63 + minLength: 1 + pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ + type: string + name: + description: Name is the name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace is the namespace of the referent. + This field is required when referring to a Namespace-scoped resource and + MUST be unset when referring to a Cluster-scoped resource. + maxLength: 63 + minLength: 1 + pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ + type: string + required: + - group + - kind + - name + type: object + required: + - controllerName + type: object + status: + default: + conditions: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + description: Status defines the current state of XMesh. + properties: + conditions: + default: + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Accepted + - lastTransitionTime: "1970-01-01T00:00:00Z" + message: Waiting for controller + reason: Pending + status: Unknown + type: Programmed + description: |- + Conditions is the current status from the controller for + this Mesh. + + Controllers should prefer to publish conditions using values + of MeshConditionType for the type of each Condition. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + maxItems: 8 + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + supportedFeatures: + description: |- + SupportedFeatures is the set of features the Mesh support. + It MUST be sorted in ascending alphabetical order by the Name key. + items: + properties: + name: + description: |- + FeatureName is used to describe distinct features that are covered by + conformance tests. + type: string + required: + - name + type: object + maxItems: 64 + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: null + storedVersions: null diff --git a/integration/fixtures/k8s/01-traefik-crd.yml b/integration/fixtures/k8s/01-traefik-crd.yml index ea2618637..bbf30fb63 100644 --- a/integration/fixtures/k8s/01-traefik-crd.yml +++ b/integration/fixtures/k8s/01-traefik-crd.yml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: ingressroutes.traefik.io spec: group: traefik.io @@ -43,11 +43,31 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/entrypoints/ Default: all. items: type: string type: array + parentRefs: + description: |- + ParentRefs defines references to parent IngressRoute resources for multi-layer routing. + When set, this IngressRoute's routers will be children of the referenced parent IngressRoute's routers. + More info: https://doc.traefik.io/traefik/v3.6/routing/routers/#parentrefs + items: + description: IngressRouteRef is a reference to an IngressRoute resource. + properties: + name: + description: Name defines the name of the referenced IngressRoute + resource. + type: string + namespace: + description: Namespace defines the namespace of the referenced + IngressRoute resource. + type: string + required: + - name + type: object + type: array routes: description: Routes defines the list of routes. items: @@ -64,12 +84,12 @@ spec: match: description: |- Match defines the router's rule. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/ type: string middlewares: description: |- Middlewares defines the list of references to Middleware resources. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/middleware/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/middleware/ items: description: MiddlewareRef is a reference to a Middleware resource. @@ -89,7 +109,7 @@ spec: observability: description: |- Observability defines the observability configuration for a router. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/observability/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/observability/ properties: accessLogs: description: AccessLogs enables access logs for this router. @@ -112,7 +132,7 @@ spec: priority: description: |- Priority defines the router's priority. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/#priority + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/#priority maximum: 9223372036854775000 type: integer services: @@ -227,6 +247,25 @@ spec: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, passHostHeader is true. type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health + checks for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window + during which the failed attempts must occur for + the server to be marked as unhealthy. It also defines + for how long the server will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window + before marking the server as unhealthy. + type: integer + type: object port: anyOf: - type: integer @@ -263,7 +302,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -312,11 +351,13 @@ spec: strategy: description: |- Strategy defines the load balancing strategy between the servers. - Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). RoundRobin value is deprecated and supported for backward compatibility. enum: - wrr - p2c + - hrw + - leasttime - RoundRobin type: string weight: @@ -332,7 +373,7 @@ spec: syntax: description: |- Syntax defines the router's rule syntax. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. type: string required: @@ -342,18 +383,18 @@ spec: tls: description: |- TLS defines the TLS configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/router/#tls + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/router/#tls properties: certResolver: description: |- CertResolver defines the name of the certificate resolver to use. Cert resolvers have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/tls/certificate-resolvers/acme/ + More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/tls/certificate-resolvers/acme/ type: string domains: description: |- Domains defines the list of domains that will be used to issue certificates. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#domains + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#domains items: description: Domain holds a domain name with SANs. properties: @@ -372,17 +413,17 @@ spec: description: |- Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. If not defined, the `default` TLSOption is used. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-options/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-options/ properties: name: description: |- Name defines the name of the referenced TLSOption. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsoption/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/tlsoption/ type: string namespace: description: |- Namespace defines the namespace of the referenced TLSOption. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsoption/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/tlsoption/ type: string required: - name @@ -399,12 +440,12 @@ spec: name: description: |- Name defines the name of the referenced TLSStore. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsstore/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/tlsstore/ type: string namespace: description: |- Namespace defines the namespace of the referenced TLSStore. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsstore/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/tlsstore/ type: string required: - name @@ -424,7 +465,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: ingressroutetcps.traefik.io spec: group: traefik.io @@ -464,7 +505,7 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/entrypoints/ Default: all. items: type: string @@ -477,7 +518,7 @@ spec: match: description: |- Match defines the router's rule. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/ type: string middlewares: description: Middlewares defines the list of references to MiddlewareTCP @@ -501,7 +542,7 @@ spec: priority: description: |- Priority defines the router's priority. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/#priority + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/#priority maximum: 9223372036854775000 type: integer services: @@ -543,7 +584,7 @@ spec: proxyProtocol: description: |- ProxyProtocol defines the PROXY protocol configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/service/#proxy-protocol + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/service/#proxy-protocol Deprecated: ProxyProtocol will not be supported in future APIVersions, please use ServersTransport to configure ProxyProtocol instead. properties: version: @@ -585,7 +626,7 @@ spec: syntax: description: |- Syntax defines the router's rule syntax. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. enum: - v3 @@ -598,18 +639,18 @@ spec: tls: description: |- TLS defines the TLS configuration on a layer 4 / TCP Route. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/router/#tls + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/router/#tls properties: certResolver: description: |- CertResolver defines the name of the certificate resolver to use. Cert resolvers have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/tls/certificate-resolvers/acme/ + More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/tls/certificate-resolvers/acme/ type: string domains: description: |- Domains defines the list of domains that will be used to issue certificates. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/#domains + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/tls/#domains items: description: Domain holds a domain name with SANs. properties: @@ -628,7 +669,7 @@ spec: description: |- Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. If not defined, the `default` TLSOption is used. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/#tls-options + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/tls/#tls-options properties: name: description: Name defines the name of the referenced Traefik @@ -680,7 +721,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: ingressrouteudps.traefik.io spec: group: traefik.io @@ -720,7 +761,7 @@ spec: description: |- EntryPoints defines the list of entry point names to bind to. Entry points have to be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/entrypoints/ Default: all. items: type: string @@ -792,7 +833,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: middlewares.traefik.io spec: group: traefik.io @@ -808,7 +849,7 @@ spec: openAPIV3Schema: description: |- Middleware is the CRD implementation of a Traefik Middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/overview/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/overview/ properties: apiVersion: description: |- @@ -834,7 +875,7 @@ spec: description: |- AddPrefix holds the add prefix middleware configuration. This middleware updates the path of a request before forwarding it. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/addprefix/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/addprefix/ properties: prefix: description: |- @@ -849,12 +890,12 @@ spec: description: |- BasicAuth holds the basic auth middleware configuration. This middleware restricts access to your services to known users. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/basicauth/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/basicauth/ properties: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/basicauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/basicauth/#headerfield type: string realm: description: |- @@ -875,7 +916,7 @@ spec: description: |- Buffering holds the buffering middleware configuration. This middleware retries or limits the size of requests that can be forwarded to backends. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/buffering/#maxrequestbodybytes + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/buffering/#maxrequestbodybytes properties: maxRequestBodyBytes: description: |- @@ -907,14 +948,14 @@ spec: description: |- RetryExpression defines the retry conditions. It is a logical combination of functions with operators AND (&&) and OR (||). - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/buffering/#retryexpression + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/buffering/#retryexpression type: string type: object chain: description: |- Chain holds the configuration of the chain middleware. This middleware enables to define reusable combinations of other pieces of middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/chain/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/chain/ properties: middlewares: description: Middlewares is the list of MiddlewareRef which composes @@ -977,7 +1018,7 @@ spec: description: |- Compress holds the compress middleware configuration. This middleware compresses responses before sending them to the client, using gzip, brotli, or zstd compression. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/compress/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/compress/ properties: defaultEncoding: description: DefaultEncoding specifies the default encoding if @@ -1027,12 +1068,12 @@ spec: description: |- DigestAuth holds the digest auth middleware configuration. This middleware restricts access to your services to known users. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/digestauth/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/digestauth/ properties: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/digestauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/digestauth/#headerfield type: string realm: description: |- @@ -1052,7 +1093,7 @@ spec: description: |- ErrorPage holds the custom error middleware configuration. This middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/errorpages/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/errorpages/ properties: query: description: |- @@ -1064,7 +1105,7 @@ spec: service: description: |- Service defines the reference to a Kubernetes Service that will serve the error page. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/errorpages/#service + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/errorpages/#service properties: healthCheck: description: Healthcheck defines health checks for ExternalName @@ -1170,6 +1211,25 @@ spec: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, passHostHeader is true. type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health checks + for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window during + which the failed attempts must occur for the server + to be marked as unhealthy. It also defines for how long + the server will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window before + marking the server as unhealthy. + type: integer + type: object port: anyOf: - type: integer @@ -1206,7 +1266,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -1254,11 +1314,13 @@ spec: strategy: description: |- Strategy defines the load balancing strategy between the servers. - Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). RoundRobin value is deprecated and supported for backward compatibility. enum: - wrr - p2c + - hrw + - leasttime - RoundRobin type: string weight: @@ -1293,7 +1355,7 @@ spec: description: |- ForwardAuth holds the forward auth middleware configuration. This middleware delegates the request authentication to a Service. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/forwardauth/ properties: addAuthCookiesToResponse: description: AddAuthCookiesToResponse defines the list of cookies @@ -1321,7 +1383,7 @@ spec: authResponseHeadersRegex: description: |- AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/#authresponseheadersregex + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/forwardauth/#authresponseheadersregex type: string forwardBody: description: ForwardBody defines whether to send the request body @@ -1330,7 +1392,7 @@ spec: headerField: description: |- HeaderField defines a header field to store the authenticated user. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/#headerfield + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/forwardauth/#headerfield type: string maxBodySize: description: MaxBodySize defines the maximum body size in bytes @@ -1392,7 +1454,7 @@ spec: description: |- Headers holds the headers middleware configuration. This middleware manages the requests and responses headers. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/headers/#customrequestheaders + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/headers/#customrequestheaders properties: accessControlAllowCredentials: description: AccessControlAllowCredentials defines whether the @@ -1564,7 +1626,7 @@ spec: description: |- InFlightReq holds the in-flight request middleware configuration. This middleware limits the number of requests being processed and served concurrently. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/inflightreq/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/inflightreq/ properties: amount: description: |- @@ -1578,12 +1640,12 @@ spec: SourceCriterion defines what criterion is used to group requests as originating from a common source. If several strategies are defined at the same time, an error will be raised. If none are set, the default is to use the requestHost. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/inflightreq/#sourcecriterion + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/inflightreq/#sourcecriterion properties: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1619,12 +1681,12 @@ spec: description: |- IPAllowList holds the IP allowlist middleware configuration. This middleware limits allowed requests based on the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipallowlist/ properties: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1662,7 +1724,7 @@ spec: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1693,7 +1755,7 @@ spec: description: |- PassTLSClientCert holds the pass TLS client cert middleware configuration. This middleware adds the selected data from the passed client TLS certificate to a header. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/passtlsclientcert/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/passtlsclientcert/ properties: info: description: Info selects the specific client certificate details @@ -1796,13 +1858,13 @@ spec: x-kubernetes-preserve-unknown-fields: true description: |- Plugin defines the middleware plugin configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/overview/#community-middlewares + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/overview/#community-middlewares type: object rateLimit: description: |- RateLimit holds the rate limit configuration. This middleware ensures that services will receive a fair amount of requests, and allows one to define what fair is. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/ratelimit/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/ratelimit/ properties: average: description: |- @@ -1921,7 +1983,7 @@ spec: ipStrategy: description: |- IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipallowlist/#ipstrategy properties: depth: description: Depth tells Traefik to use the X-Forwarded-For @@ -1957,7 +2019,7 @@ spec: description: |- RedirectRegex holds the redirect regex middleware configuration. This middleware redirects a request using regex matching and replacement. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/redirectregex/#regex + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/redirectregex/#regex properties: permanent: description: Permanent defines whether the redirection is permanent @@ -1976,11 +2038,12 @@ spec: description: |- RedirectScheme holds the redirect scheme middleware configuration. This middleware redirects requests from a scheme/port to another. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/redirectscheme/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/redirectscheme/ properties: permanent: - description: Permanent defines whether the redirection is permanent - (308). + description: |- + Permanent defines whether the redirection is permanent. + For HTTP GET requests a 301 is returned, otherwise a 308 is returned. type: boolean port: description: Port defines the port of the new URL. @@ -1993,7 +2056,7 @@ spec: description: |- ReplacePath holds the replace path middleware configuration. This middleware replaces the path of the request URL and store the original path in an X-Replaced-Path header. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/replacepath/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/replacepath/ properties: path: description: Path defines the path to use as replacement in the @@ -2004,7 +2067,7 @@ spec: description: |- ReplacePathRegex holds the replace path regex middleware configuration. This middleware replaces the path of a URL using regex matching and replacement. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/replacepathregex/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/replacepathregex/ properties: regex: description: Regex defines the regular expression used to match @@ -2020,7 +2083,7 @@ spec: Retry holds the retry middleware configuration. This middleware reissues requests a given number of times to a backend server if that server does not reply. As soon as the server answers, the middleware stops retrying, regardless of the response status. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/retry/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/retry/ properties: attempts: description: Attempts defines how many times the request should @@ -2044,7 +2107,7 @@ spec: description: |- StripPrefix holds the strip prefix middleware configuration. This middleware removes the specified prefixes from the URL path. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/stripprefix/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/stripprefix/ properties: forceSlash: description: |- @@ -2063,7 +2126,7 @@ spec: description: |- StripPrefixRegex holds the strip prefix regex middleware configuration. This middleware removes the matching prefixes from the URL path. - More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/stripprefixregex/ + More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/stripprefixregex/ properties: regex: description: Regex defines the regular expression to match the @@ -2084,7 +2147,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: middlewaretcps.traefik.io spec: group: traefik.io @@ -2100,7 +2163,7 @@ spec: openAPIV3Schema: description: |- MiddlewareTCP is the CRD implementation of a Traefik TCP middleware. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/overview/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/overview/ properties: apiVersion: description: |- @@ -2137,7 +2200,7 @@ spec: description: |- IPAllowList defines the IPAllowList middleware configuration. This middleware accepts/refuses connections based on the client IP. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/ipallowlist/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/ipallowlist/ properties: sourceRange: description: SourceRange defines the allowed IPs (or ranges of @@ -2151,7 +2214,7 @@ spec: IPWhiteList defines the IPWhiteList middleware configuration. This middleware accepts/refuses connections based on the client IP. Deprecated: please use IPAllowList instead. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/ipwhitelist/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/ipwhitelist/ properties: sourceRange: description: SourceRange defines the allowed IPs (or ranges of @@ -2172,7 +2235,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: serverstransports.traefik.io spec: group: traefik.io @@ -2190,7 +2253,7 @@ spec: ServersTransport is the CRD implementation of a ServersTransport. If no serversTransport is specified, the default@internal will be used. The default@internal serversTransport is created from the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/serverstransport/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/serverstransport/ properties: apiVersion: description: |- @@ -2341,7 +2404,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: serverstransporttcps.traefik.io spec: group: traefik.io @@ -2359,7 +2422,7 @@ spec: ServersTransportTCP is the CRD implementation of a TCPServersTransport. If no tcpServersTransport is specified, a default one named default@internal will be used. The default@internal tcpServersTransport can be configured in the static configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/serverstransport/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/serverstransport/ properties: apiVersion: description: |- @@ -2497,7 +2560,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: tlsoptions.traefik.io spec: group: traefik.io @@ -2513,7 +2576,7 @@ spec: openAPIV3Schema: description: |- TLSOption is the CRD implementation of a Traefik TLS Option, allowing to configure some parameters of the TLS connection. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#tls-options + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#tls-options properties: apiVersion: description: |- @@ -2538,14 +2601,14 @@ spec: alpnProtocols: description: |- ALPNProtocols defines the list of supported application level protocols for the TLS handshake, in order of preference. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#alpn-protocols + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#alpn-protocols items: type: string type: array cipherSuites: description: |- CipherSuites defines the list of supported cipher suites for TLS versions up to TLS 1.2. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#cipher-suites + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#cipher-suites items: type: string type: array @@ -2573,7 +2636,7 @@ spec: curvePreferences: description: |- CurvePreferences defines the preferred elliptic curves. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#curve-preferences + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#curve-preferences items: type: string type: array @@ -2615,7 +2678,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: tlsstores.traefik.io spec: group: traefik.io @@ -2633,7 +2696,7 @@ spec: TLSStore is the CRD implementation of a Traefik TLS Store. For the time being, only the TLSStore named default is supported. This means that you cannot have two stores that are named default in different Kubernetes namespaces. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#certificates-stores + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#certificates-stores properties: apiVersion: description: |- @@ -2712,7 +2775,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.16.1 + controller-gen.kubebuilder.io/version: v0.19.0 name: traefikservices.traefik.io spec: group: traefik.io @@ -2731,7 +2794,7 @@ spec: TraefikService object allows to: - Apply weight to Services on load-balancing - Mirror traffic on services - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/traefikservice/ + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/traefikservice/ properties: apiVersion: description: |- @@ -2753,6 +2816,244 @@ spec: spec: description: TraefikServiceSpec defines the desired state of a TraefikService. properties: + highestRandomWeight: + description: HighestRandomWeight defines the highest random weight + service configuration. + properties: + services: + description: Services defines the list of Kubernetes Service and/or + TraefikService to load-balance, with weight. + items: + description: Service defines an upstream HTTP service to proxy + traffic to. + properties: + healthCheck: + description: Healthcheck defines health checks for ExternalName + services. + properties: + followRedirects: + description: |- + FollowRedirects defines whether redirects should be followed during the health check calls. + Default: true + type: boolean + headers: + additionalProperties: + type: string + description: Headers defines custom headers to be sent + to the health check endpoint. + type: object + hostname: + description: Hostname defines the value of hostname + in the Host header of the health check request. + type: string + interval: + anyOf: + - type: integer + - type: string + description: |- + Interval defines the frequency of the health check calls for healthy targets. + Default: 30s + x-kubernetes-int-or-string: true + method: + description: Method defines the healthcheck method. + type: string + mode: + description: |- + Mode defines the health check mode. + If defined to grpc, will use the gRPC health check protocol to probe the server. + Default: http + type: string + path: + description: Path defines the server URL path for the + health check endpoint. + type: string + port: + description: Port defines the server URL port for the + health check endpoint. + type: integer + scheme: + description: Scheme replaces the server URL scheme for + the health check endpoint. + type: string + status: + description: Status defines the expected HTTP status + code of the response to the health check request. + type: integer + timeout: + anyOf: + - type: integer + - type: string + description: |- + Timeout defines the maximum duration Traefik will wait for a health check request before considering the server unhealthy. + Default: 5s + x-kubernetes-int-or-string: true + unhealthyInterval: + anyOf: + - type: integer + - type: string + description: |- + UnhealthyInterval defines the frequency of the health check calls for unhealthy targets. + When UnhealthyInterval is not defined, it defaults to the Interval value. + Default: 30s + x-kubernetes-int-or-string: true + type: object + kind: + description: Kind defines the kind of the Service. + enum: + - Service + - TraefikService + type: string + name: + description: |- + Name defines the name of the referenced Kubernetes Service or TraefikService. + The differentiation between the two is specified in the Kind field. + type: string + namespace: + description: Namespace defines the namespace of the referenced + Kubernetes Service or TraefikService. + type: string + nativeLB: + description: |- + NativeLB controls, when creating the load-balancer, + whether the LB's children are directly the pods IPs or if the only child is the Kubernetes Service clusterIP. + The Kubernetes Service itself does load-balance to the pods. + By default, NativeLB is false. + type: boolean + nodePortLB: + description: |- + NodePortLB controls, when creating the load-balancer, + whether the LB's children are directly the nodes internal IPs using the nodePort when the service type is NodePort. + It allows services to be reachable when Traefik runs externally from the Kubernetes cluster but within the same network of the nodes. + By default, NodePortLB is false. + type: boolean + passHostHeader: + description: |- + PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. + By default, passHostHeader is true. + type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health checks + for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window during + which the failed attempts must occur for the server + to be marked as unhealthy. It also defines for how + long the server will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window + before marking the server as unhealthy. + type: integer + type: object + port: + anyOf: + - type: integer + - type: string + description: |- + Port defines the port of a Kubernetes Service. + This can be a reference to a named port. + x-kubernetes-int-or-string: true + responseForwarding: + description: ResponseForwarding defines how Traefik forwards + the response from the upstream Kubernetes Service to the + client. + properties: + flushInterval: + description: |- + FlushInterval defines the interval, in milliseconds, in between flushes to the client while copying the response body. + A negative value means to flush immediately after each write to the client. + This configuration is ignored when ReverseProxy recognizes a response as a streaming response; + for such responses, writes are flushed to the client immediately. + Default: 100ms + type: string + type: object + scheme: + description: |- + Scheme defines the scheme to use for the request to the upstream Kubernetes Service. + It defaults to https when Kubernetes Service port is 443, http otherwise. + type: string + serversTransport: + description: |- + ServersTransport defines the name of ServersTransport resource to use. + It allows to configure the transport between Traefik and your servers. + Can only be used on a Kubernetes Service. + type: string + sticky: + description: |- + Sticky defines the sticky sessions configuration. + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + properties: + cookie: + description: Cookie defines the sticky cookie configuration. + properties: + domain: + description: |- + Domain defines the host to which the cookie will be sent. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#domaindomain-value + type: string + httpOnly: + description: HTTPOnly defines whether the cookie + can be accessed by client-side APIs, such as JavaScript. + type: boolean + maxAge: + description: |- + MaxAge defines the number of seconds until the cookie expires. + When set to a negative number, the cookie expires immediately. + When set to zero, the cookie never expires. + type: integer + name: + description: Name defines the Cookie name. + type: string + path: + description: |- + Path defines the path that must exist in the requested URL for the browser to send the Cookie header. + When not provided the cookie will be sent on every request to the domain. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#pathpath-value + type: string + sameSite: + description: |- + SameSite defines the same site policy. + More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite + enum: + - none + - lax + - strict + type: string + secure: + description: Secure defines whether the cookie can + only be transmitted over an encrypted connection + (i.e. HTTPS). + type: boolean + type: object + type: object + strategy: + description: |- + Strategy defines the load balancing strategy between the servers. + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). + RoundRobin value is deprecated and supported for backward compatibility. + enum: + - wrr + - p2c + - hrw + - leasttime + - RoundRobin + type: string + weight: + description: |- + Weight defines the weight and should only be specified when Name references a TraefikService object + (and to be precise, one that embeds a Weighted Round Robin). + minimum: 0 + type: integer + required: + - name + type: object + type: array + type: object mirroring: description: Mirroring defines the Mirroring service configuration. properties: @@ -2954,6 +3255,25 @@ spec: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, passHostHeader is true. type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health checks + for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window during + which the failed attempts must occur for the server + to be marked as unhealthy. It also defines for how + long the server will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window + before marking the server as unhealthy. + type: integer + type: object percent: description: |- Percent defines the part of the traffic to mirror. @@ -2995,7 +3315,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -3043,11 +3363,13 @@ spec: strategy: description: |- Strategy defines the load balancing strategy between the servers. - Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). RoundRobin value is deprecated and supported for backward compatibility. enum: - wrr - p2c + - hrw + - leasttime - RoundRobin type: string weight: @@ -3088,6 +3410,25 @@ spec: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, passHostHeader is true. type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health checks + for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window during + which the failed attempts must occur for the server to be + marked as unhealthy. It also defines for how long the server + will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window before + marking the server as unhealthy. + type: integer + type: object port: anyOf: - type: integer @@ -3123,7 +3464,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -3170,11 +3511,13 @@ spec: strategy: description: |- Strategy defines the load balancing strategy between the servers. - Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). RoundRobin value is deprecated and supported for backward compatibility. enum: - wrr - p2c + - hrw + - leasttime - RoundRobin type: string weight: @@ -3300,6 +3643,25 @@ spec: PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. By default, passHostHeader is true. type: boolean + passiveHealthCheck: + description: PassiveHealthCheck defines passive health checks + for ExternalName services. + properties: + failureWindow: + anyOf: + - type: integer + - type: string + description: FailureWindow defines the time window during + which the failed attempts must occur for the server + to be marked as unhealthy. It also defines for how + long the server will be considered unhealthy. + x-kubernetes-int-or-string: true + maxFailedAttempts: + description: MaxFailedAttempts is the number of consecutive + failed attempts allowed within the failure window + before marking the server as unhealthy. + type: integer + type: object port: anyOf: - type: integer @@ -3336,7 +3698,7 @@ spec: sticky: description: |- Sticky defines the sticky sessions configuration. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions properties: cookie: description: Cookie defines the sticky cookie configuration. @@ -3384,11 +3746,13 @@ spec: strategy: description: |- Strategy defines the load balancing strategy between the servers. - Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). RoundRobin value is deprecated and supported for backward compatibility. enum: - wrr - p2c + - hrw + - leasttime - RoundRobin type: string weight: @@ -3404,7 +3768,7 @@ spec: sticky: description: |- Sticky defines whether sticky sessions are enabled. - More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/traefikservice/#stickiness-and-load-balancing + More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/traefikservice/#stickiness-and-load-balancing properties: cookie: description: Cookie defines the sticky cookie configuration. diff --git a/integration/fixtures/k8s/06-ingressroute-traefikservices.yml b/integration/fixtures/k8s/06-ingressroute-traefikservices.yml index d979bc425..b1ce5660b 100644 --- a/integration/fixtures/k8s/06-ingressroute-traefikservices.yml +++ b/integration/fixtures/k8s/06-ingressroute-traefikservices.yml @@ -28,6 +28,23 @@ spec: - name: whoami port: 80 +--- +apiVersion: traefik.io/v1alpha1 +kind: TraefikService +metadata: + name: hrw1 + namespace: default + +spec: + highestRandomWeight: + services: + - name: whoami + port: 80 + weight: 10 + - name: whoami + port: 80 + weight: 20 + --- apiVersion: traefik.io/v1alpha1 kind: IngressRoute @@ -47,6 +64,22 @@ spec: --- apiVersion: traefik.io/v1alpha1 kind: IngressRoute +metadata: + name: test4.route + namespace: default + +spec: + entryPoints: + - web + routes: + - match: Host(`foo.com`) && PathPrefix(`/hrw1`) + kind: Rule + services: + - name: hrw1 + kind: TraefikService +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute metadata: name: api.route namespace: default diff --git a/integration/fixtures/k8s_gateway_conformance.toml b/integration/fixtures/k8s_gateway_conformance.toml deleted file mode 100644 index 5114f2dfb..000000000 --- a/integration/fixtures/k8s_gateway_conformance.toml +++ /dev/null @@ -1,21 +0,0 @@ -[global] - checkNewVersion = false - sendAnonymousUsage = false - -[log] - level = "DEBUG" - noColor = true - -[api] - insecure = true - -[experimental] - kubernetesGateway = true - -[entryPoints] - [entryPoints.web] - address = ":80" - [entryPoints.websecure] - address = ":443" - -[providers.kubernetesGateway] diff --git a/integration/fixtures/knative/00-knative-crd-v1.19.0.yml b/integration/fixtures/knative/00-knative-crd-v1.19.0.yml new file mode 100644 index 000000000..82a700136 --- /dev/null +++ b/integration/fixtures/knative/00-knative-crd-v1.19.0.yml @@ -0,0 +1,6692 @@ +# Copyright 2020 The Knative 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 +# +# https://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. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: certificates.networking.internal.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: networking + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: networking.internal.knative.dev + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + description: |- + Certificate is responsible for provisioning a SSL certificate for the + given hosts. It is a Knative abstraction for various SSL certificate + provisioning solutions (such as cert-manager or self-signed SSL certificate). + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Spec is the desired state of the Certificate. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + required: + - dnsNames + - secretName + properties: + dnsNames: + description: |- + DNSNames is a list of DNS names the Certificate could support. + The wildcard format of DNSNames (e.g. *.default.example.com) is supported. + type: array + items: + type: string + domain: + description: Domain is the top level domain of the values for DNSNames. + type: string + secretName: + description: SecretName is the name of the secret resource to store the SSL certificate in. + type: string + status: + description: |- + Status is the current state of the Certificate. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + properties: + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + http01Challenges: + description: |- + HTTP01Challenges is a list of HTTP01 challenges that need to be fulfilled + in order to get the TLS certificate.. + type: array + items: + description: |- + HTTP01Challenge defines the status of a HTTP01 challenge that a certificate needs + to fulfill. + type: object + properties: + serviceName: + description: ServiceName is the name of the service to serve HTTP01 challenge requests. + type: string + serviceNamespace: + description: ServiceNamespace is the namespace of the service to serve HTTP01 challenge requests. + type: string + servicePort: + description: ServicePort is the port of the service to serve HTTP01 challenge requests. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + url: + description: URL is the URL that the HTTP01 challenge is expected to serve on. + type: string + notAfter: + description: |- + The expiration time of the TLS certificate stored in the secret named + by this resource in spec.secretName. + type: string + format: date-time + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + additionalPrinterColumns: + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type==\"Ready\")].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type==\"Ready\")].reason" + names: + kind: Certificate + plural: certificates + singular: certificate + categories: + - knative-internal + - networking + shortNames: + - kcert + scope: Namespaced + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +# Note: The schema part of the spec is auto-generated by hack/update-schemas.sh. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: configurations.serving.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" + duck.knative.dev/podspecable: "true" +spec: + group: serving.knative.dev + names: + kind: Configuration + plural: configurations + singular: configuration + categories: + - all + - knative + - serving + shortNames: + - config + - cfg + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - name: LatestCreated + type: string + jsonPath: .status.latestCreatedRevisionName + - name: LatestReady + type: string + jsonPath: .status.latestReadyRevisionName + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + schema: + openAPIV3Schema: + description: |- + Configuration represents the "floating HEAD" of a linear history of Revisions. + Users create new Revisions by updating the Configuration's spec. + The "latest created" revision's name is available under status, as is the + "latest ready" revision's name. + See also: https://github.com/knative/serving/blob/main/docs/spec/overview.md#configuration + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ConfigurationSpec holds the desired state of the Configuration (from the client). + type: object + properties: + template: + description: Template holds the latest specification for the Revision to be stamped out. + type: object + properties: + metadata: + type: object + properties: + annotations: + type: object + additionalProperties: + type: string + finalizers: + type: array + items: + type: string + labels: + type: object + additionalProperties: + type: string + name: + type: string + namespace: + type: string + x-kubernetes-preserve-unknown-fields: true + spec: + description: RevisionSpec holds the desired state of the Revision (from the client). + type: object + required: + - containers + properties: + affinity: + description: This is accessible behind a feature flag - kubernetes.podspec-affinity + type: object + x-kubernetes-preserve-unknown-fields: true + automountServiceAccountToken: + description: AutomountServiceAccountToken indicates whether a service account token should be automatically mounted. + type: boolean + containerConcurrency: + description: |- + ContainerConcurrency specifies the maximum allowed in-flight (concurrent) + requests per container of the Revision. Defaults to `0` which means + concurrency to the application is not limited, and the system decides the + target concurrency for the autoscaler. + type: integer + format: int64 + containers: + description: |- + List of containers belonging to the pod. + Containers cannot currently be added or removed. + There must be at least one container in a Pod. + Cannot be updated. + type: array + items: + description: A single application container that you want to run within a pod. + type: object + properties: + args: + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + type: array + items: + type: string + x-kubernetes-list-type: atomic + command: + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + type: array + items: + type: string + x-kubernetes-list-type: atomic + env: + description: |- + List of environment variables to set in the container. + Cannot be updated. + type: array + items: + description: EnvVar represents an environment variable present in a Container. + type: object + required: + - name + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. Cannot be used if value is not empty. + type: object + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + type: object + required: + - key + properties: + key: + description: The key to select. + type: string + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the ConfigMap or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + fieldRef: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-fieldref + type: object + x-kubernetes-map-type: atomic + x-kubernetes-preserve-unknown-fields: true + resourceFieldRef: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-fieldref + type: object + x-kubernetes-map-type: atomic + x-kubernetes-preserve-unknown-fields: true + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + type: object + required: + - key + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. + type: array + items: + description: EnvFromSource represents the source of a set of ConfigMaps or Secrets + type: object + properties: + configMapRef: + description: The ConfigMap to select from + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + x-kubernetes-map-type: atomic + prefix: + description: Optional text to prepend to the name of each environment variable. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the Secret must be defined + type: boolean + x-kubernetes-map-type: atomic + x-kubernetes-list-type: atomic + image: + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + imagePullPolicy: + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + type: string + livenessProbe: + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + name: + description: |- + Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + type: array + items: + description: ContainerPort represents a network port in a single container. + type: object + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + type: integer + format: int32 + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + default: TCP + readinessProbe: + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + resources: + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + properties: + limits: + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + requests: + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + securityContext: + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + type: object + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + type: object + properties: + add: + description: This is accessible behind a feature flag - kubernetes.containerspec-addcapabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + x-kubernetes-list-type: atomic + privileged: + description: |- + Run container in privileged mode. This can only be set to explicitly to 'false' + type: boolean + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + type: object + required: + - type + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + startupProbe: + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + terminationMessagePath: + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. + type: string + terminationMessagePolicy: + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. + type: string + volumeMounts: + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. + type: array + items: + description: VolumeMount describes a mounting of a Volume within a container. + type: object + required: + - mountPath + - name + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-mount-propagation + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. + type: string + dnsConfig: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-dnsconfig + type: object + x-kubernetes-preserve-unknown-fields: true + dnsPolicy: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-dnspolicy + type: string + enableServiceLinks: + description: |- + EnableServiceLinks indicates whether information aboutservices should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Knative defaults this to false. + type: boolean + hostAliases: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostaliases + type: array + items: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostaliases + type: object + x-kubernetes-preserve-unknown-fields: true + hostIPC: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostipc + type: boolean + hostNetwork: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostnetwork + type: boolean + hostPID: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostpid + type: boolean + idleTimeoutSeconds: + description: |- + IdleTimeoutSeconds is the maximum duration in seconds a request will be allowed + to stay open while not receiving any bytes from the user's application. If + unspecified, a system default will be provided. + type: integer + format: int64 + imagePullSecrets: + description: |- + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. + If specified, these secrets will be passed to individual puller implementations for them to use. + More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod + type: array + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + x-kubernetes-map-type: atomic + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-init-containers + type: array + items: + description: This is accessible behind a feature flag - kubernetes.podspec-init-containers + type: object + x-kubernetes-preserve-unknown-fields: true + nodeSelector: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-nodeselector + type: object + additionalProperties: + type: string + x-kubernetes-map-type: atomic + priorityClassName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-priorityclassname + type: string + responseStartTimeoutSeconds: + description: |- + ResponseStartTimeoutSeconds is the maximum duration in seconds that the request + routing layer will wait for a request delivered to a container to begin + sending any network traffic. + type: integer + format: int64 + runtimeClassName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-runtimeclassname + type: string + schedulerName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-schedulername + type: string + securityContext: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-securitycontext + type: object + x-kubernetes-preserve-unknown-fields: true + serviceAccountName: + description: |- + ServiceAccountName is the name of the ServiceAccount to use to run this pod. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ + type: string + shareProcessNamespace: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-shareprocessnamespace + type: boolean + timeoutSeconds: + description: |- + TimeoutSeconds is the maximum duration in seconds that the request instance + is allowed to respond to a request. If unspecified, a system default will + be provided. + type: integer + format: int64 + tolerations: + description: This is accessible behind a feature flag - kubernetes.podspec-tolerations + type: array + items: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-tolerations + type: object + x-kubernetes-preserve-unknown-fields: true + topologySpreadConstraints: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-topologyspreadconstraints + type: array + items: + description: This is accessible behind a feature flag - kubernetes.podspec-topologyspreadconstraints + type: object + x-kubernetes-preserve-unknown-fields: true + volumes: + description: |- + List of volumes that can be mounted by containers belonging to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes + type: array + items: + description: Volume represents a named volume in a pod that may be accessed by any container in the pod. + type: object + required: + - name + properties: + configMap: + description: configMap represents a configMap that should populate this volume + type: object + properties: + defaultMode: + description: |- + defaultMode is optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + x-kubernetes-map-type: atomic + csi: + description: This is accessible behind a feature flag - kubernetes.podspec-volumes-csi + type: object + x-kubernetes-preserve-unknown-fields: true + emptyDir: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-emptydir + type: object + x-kubernetes-preserve-unknown-fields: true + hostPath: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-hostpath + type: object + x-kubernetes-preserve-unknown-fields: true + image: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-image + type: object + x-kubernetes-preserve-unknown-fields: true + name: + description: |- + name of the volume. + Must be a DNS_LABEL and unique within the pod. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + persistentVolumeClaim: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-persistent-volume-claim + type: object + x-kubernetes-preserve-unknown-fields: true + projected: + description: projected items for all in one resources secrets, configmaps, and downward API + type: object + properties: + defaultMode: + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + sources: + description: |- + sources is the list of volume projections. Each entry in this list + handles one source. + type: array + items: + description: |- + Projection that may be projected along with other supported volume types. + Exactly one of these fields must be set. + type: object + properties: + configMap: + description: configMap information about the configMap data to project + type: object + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the downwardAPI data to project + type: object + properties: + items: + description: Items is a list of DownwardAPIVolume file + type: array + items: + description: DownwardAPIVolumeFile represents information to create the file containing the pod field + type: object + required: + - path + properties: + fieldRef: + description: 'Required: Selects a field of the pod: only annotations, labels, name, namespace and uid are supported.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: 'Required: Path is the relative path name of the file to be created. Must not be absolute or contain the ''..'' path. Must be utf-8 encoded. The first item of the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + type: object + required: + - resource + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + x-kubernetes-map-type: atomic + x-kubernetes-list-type: atomic + secret: + description: secret information about the secret data to project + type: object + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional field specify whether the Secret or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information about the serviceAccountToken data to project + type: object + required: + - path + properties: + audience: + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. + type: integer + format: int64 + path: + description: |- + path is the path relative to the mount point of the file to project the + token into. + type: string + x-kubernetes-list-type: atomic + secret: + description: |- + secret represents a secret that should populate this volume. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: object + properties: + defaultMode: + description: |- + defaultMode is Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values + for mode bits. Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + items: + description: |- + items If unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + optional: + description: optional field specify whether the Secret or its keys must be defined + type: boolean + secretName: + description: |- + secretName is the name of the secret in the pod's namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: string + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + status: + description: ConfigurationStatus communicates the observed state of the Configuration (from the controller). + type: object + properties: + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + latestCreatedRevisionName: + description: |- + LatestCreatedRevisionName is the last revision that was created from this + Configuration. It might not be ready yet, for that use LatestReadyRevisionName. + type: string + latestReadyRevisionName: + description: |- + LatestReadyRevisionName holds the name of the latest Revision stamped out + from this Configuration that has had its "Ready" condition become "True". + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + +--- +# Copyright 2020 The Knative 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 +# +# https://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. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: clusterdomainclaims.networking.internal.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: networking + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: networking.internal.knative.dev + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + description: ClusterDomainClaim is a cluster-wide reservation for a particular domain name. + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Spec is the desired state of the ClusterDomainClaim. + More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + required: + - namespace + properties: + namespace: + description: |- + Namespace is the namespace which is allowed to create a DomainMapping + using this ClusterDomainClaim's name. + type: string + names: + kind: ClusterDomainClaim + plural: clusterdomainclaims + singular: clusterdomainclaim + categories: + - knative-internal + - networking + shortNames: + - cdc + scope: Cluster + +--- +# Copyright 2020 The Knative 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 +# +# https://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. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: domainmappings.serving.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: serving.knative.dev + versions: + - name: v1beta1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - name: URL + type: string + jsonPath: .status.url + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + "schema": + "openAPIV3Schema": + description: DomainMapping is a mapping from a custom hostname to an Addressable. + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Spec is the desired state of the DomainMapping. + More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + required: + - ref + properties: + ref: + description: |- + Ref specifies the target of the Domain Mapping. + + The object identified by the Ref must be an Addressable with a URL of the + form `{name}.{namespace}.{domain}` where `{domain}` is the cluster domain, + and `{name}` and `{namespace}` are the name and namespace of a Kubernetes + Service. + + This contract is satisfied by Knative types such as Knative Services and + Knative Routes, and by Kubernetes Services. + type: object + required: + - kind + - name + properties: + address: + description: Address points to a specific Address Name. + type: string + apiVersion: + description: API version of the referent. + type: string + group: + description: |- + Group of the API, without the version of the group. This can be used as an alternative to the APIVersion, and then resolved using ResolveGroup. + Note: This API is EXPERIMENTAL and might break anytime. For more details: https://github.com/knative/eventing/issues/5086 + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + This is optional field, it gets defaulted to the object holding it if left out. + type: string + tls: + description: TLS allows the DomainMapping to terminate TLS traffic with an existing secret. + type: object + required: + - secretName + properties: + secretName: + description: SecretName is the name of the existing secret used to terminate TLS traffic. + type: string + status: + description: |- + Status is the current state of the DomainMapping. + More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + properties: + address: + description: Address holds the information needed for a DomainMapping to be the target of an event. + type: object + properties: + CACerts: + description: |- + CACerts is the Certification Authority (CA) certificates in PEM format + according to https://www.rfc-editor.org/rfc/rfc7468. + type: string + audience: + description: Audience is the OIDC audience for this address. + type: string + name: + description: Name is the name of the address. + type: string + url: + type: string + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + url: + description: URL is the URL of this DomainMapping. + type: string + names: + kind: DomainMapping + plural: domainmappings + singular: domainmapping + categories: + - all + - knative + - serving + shortNames: + - dm + scope: Namespaced + +--- +# Copyright 2020 The Knative 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 +# +# https://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. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: ingresses.networking.internal.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: networking + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: networking.internal.knative.dev + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + description: |- + Ingress is a collection of rules that allow inbound connections to reach the endpoints defined + by a backend. An Ingress can be configured to give services externally-reachable URLs, load + balance traffic, offer name based virtual hosting, etc. + + This is heavily based on K8s Ingress https://godoc.org/k8s.io/api/networking/v1beta1#Ingress + which some highlighted modifications. + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Spec is the desired state of the Ingress. + More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + properties: + httpOption: + description: |- + HTTPOption is the option of HTTP. It has the following two values: + `HTTPOptionEnabled`, `HTTPOptionRedirected` + type: string + rules: + description: A list of host rules used to configure the Ingress. + type: array + items: + description: |- + IngressRule represents the rules mapping the paths under a specified host to + the related backend services. Incoming requests are first evaluated for a host + match, then routed to the backend associated with the matching IngressRuleValue. + type: object + properties: + hosts: + description: |- + Host is the fully qualified domain name of a network host, as defined + by RFC 3986. Note the following deviations from the "host" part of the + URI as defined in the RFC: + 1. IPs are not allowed. Currently a rule value can only apply to the + IP in the Spec of the parent . + 2. The `:` delimiter is not respected because ports are not allowed. + Currently the port of an Ingress is implicitly :80 for http and + :443 for https. + Both these may change in the future. + If the host is unspecified, the Ingress routes all traffic based on the + specified IngressRuleValue. + If multiple matching Hosts were provided, the first rule will take precedent. + type: array + items: + type: string + http: + description: |- + HTTP represents a rule to apply against incoming requests. If the + rule is satisfied, the request is routed to the specified backend. + type: object + required: + - paths + properties: + paths: + description: |- + A collection of paths that map requests to backends. + + If they are multiple matching paths, the first match takes precedence. + type: array + items: + description: |- + HTTPIngressPath associates a path regex with a backend. Incoming URLs matching + the path are forwarded to the backend. + type: object + required: + - splits + properties: + appendHeaders: + description: |- + AppendHeaders allow specifying additional HTTP headers to add + before forwarding a request to the destination service. + + NOTE: This differs from K8s Ingress which doesn't allow header appending. + type: object + additionalProperties: + type: string + headers: + description: |- + Headers defines header matching rules which is a map from a header name + to HeaderMatch which specify a matching condition. + When a request matched with all the header matching rules, + the request is routed by the corresponding ingress rule. + If it is empty, the headers are not used for matching + type: object + additionalProperties: + description: |- + HeaderMatch represents a matching value of Headers in HTTPIngressPath. + Currently, only the exact matching is supported. + type: object + required: + - exact + properties: + exact: + type: string + path: + description: |- + Path represents a literal prefix to which this rule should apply. + Currently it can contain characters disallowed from the conventional + "path" part of a URL as defined by RFC 3986. Paths must begin with + a '/'. If unspecified, the path defaults to a catch all sending + traffic to the backend. + type: string + rewriteHost: + description: |- + RewriteHost rewrites the incoming request's host header. + + This field is currently experimental and not supported by all Ingress + implementations. + type: string + splits: + description: |- + Splits defines the referenced service endpoints to which the traffic + will be forwarded to. + type: array + items: + description: IngressBackendSplit describes all endpoints for a given service and port. + type: object + required: + - serviceName + - serviceNamespace + - servicePort + properties: + appendHeaders: + description: |- + AppendHeaders allow specifying additional HTTP headers to add + before forwarding a request to the destination service. + + NOTE: This differs from K8s Ingress which doesn't allow header appending. + type: object + additionalProperties: + type: string + percent: + description: |- + Specifies the split percentage, a number between 0 and 100. If + only one split is specified, we default to 100. + + NOTE: This differs from K8s Ingress to allow percentage split. + type: integer + serviceName: + description: Specifies the name of the referenced service. + type: string + serviceNamespace: + description: |- + Specifies the namespace of the referenced service. + + NOTE: This differs from K8s Ingress to allow routing to different namespaces. + type: string + servicePort: + description: Specifies the port of the referenced service. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + visibility: + description: |- + Visibility signifies whether this rule should `ClusterLocal`. If it's not + specified then it defaults to `ExternalIP`. + type: string + tls: + description: |- + TLS configuration. Currently Ingress only supports a single TLS + port: 443. If multiple members of this list specify different hosts, they + will be multiplexed on the same port according to the hostname specified + through the SNI TLS extension, if the ingress controller fulfilling the + ingress supports SNI. + type: array + items: + description: IngressTLS describes the transport layer security associated with an Ingress. + type: object + properties: + hosts: + description: |- + Hosts is a list of hosts included in the TLS certificate. The values in + this list must match the name/s used in the tlsSecret. Defaults to the + wildcard host setting for the loadbalancer controller fulfilling this + Ingress, if left unspecified. + type: array + items: + type: string + secretName: + description: SecretName is the name of the secret used to terminate SSL traffic. + type: string + secretNamespace: + description: |- + SecretNamespace is the namespace of the secret used to terminate SSL traffic. + If not set the namespace should be assumed to be the same as the Ingress. + If set the secret should have the same namespace as the Ingress otherwise + the behaviour is undefined and not supported. + type: string + status: + description: |- + Status is the current state of the Ingress. + More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + properties: + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + privateLoadBalancer: + description: PrivateLoadBalancer contains the current status of the load-balancer. + type: object + properties: + ingress: + description: |- + Ingress is a list containing ingress points for the load-balancer. + Traffic intended for the service should be sent to these ingress points. + type: array + items: + description: |- + LoadBalancerIngressStatus represents the status of a load-balancer ingress point: + traffic intended for the service should be sent to an ingress point. + type: object + properties: + domain: + description: |- + Domain is set for load-balancer ingress points that are DNS based + (typically AWS load-balancers) + type: string + domainInternal: + description: |- + DomainInternal is set if there is a cluster-local DNS name to access the Ingress. + + NOTE: This differs from K8s Ingress, since we also desire to have a cluster-local + DNS name to allow routing in case of not having a mesh. + type: string + ip: + description: |- + IP is set for load-balancer ingress points that are IP based + (typically GCE or OpenStack load-balancers) + type: string + meshOnly: + description: MeshOnly is set if the Ingress is only load-balanced through a Service mesh. + type: boolean + publicLoadBalancer: + description: PublicLoadBalancer contains the current status of the load-balancer. + type: object + properties: + ingress: + description: |- + Ingress is a list containing ingress points for the load-balancer. + Traffic intended for the service should be sent to these ingress points. + type: array + items: + description: |- + LoadBalancerIngressStatus represents the status of a load-balancer ingress point: + traffic intended for the service should be sent to an ingress point. + type: object + properties: + domain: + description: |- + Domain is set for load-balancer ingress points that are DNS based + (typically AWS load-balancers) + type: string + domainInternal: + description: |- + DomainInternal is set if there is a cluster-local DNS name to access the Ingress. + + NOTE: This differs from K8s Ingress, since we also desire to have a cluster-local + DNS name to allow routing in case of not having a mesh. + type: string + ip: + description: |- + IP is set for load-balancer ingress points that are IP based + (typically GCE or OpenStack load-balancers) + type: string + meshOnly: + description: MeshOnly is set if the Ingress is only load-balanced through a Service mesh. + type: boolean + additionalPrinterColumns: + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + names: + kind: Ingress + plural: ingresses + singular: ingress + categories: + - knative-internal + - networking + shortNames: + - kingress + - king + scope: Namespaced + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +# Note: The schema part of the spec is auto-generated by hack/update-schemas.sh. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: metrics.autoscaling.internal.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: autoscaling.internal.knative.dev + names: + kind: Metric + plural: metrics + singular: metric + categories: + - knative-internal + - autoscaling + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + schema: + openAPIV3Schema: + description: Metric represents a resource to configure the metric collector with. + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec holds the desired state of the Metric (from the client). + type: object + required: + - panicWindow + - scrapeTarget + - stableWindow + properties: + panicWindow: + description: PanicWindow is the aggregation window for metrics where quick reactions are needed. + type: integer + format: int64 + scrapeTarget: + description: ScrapeTarget is the K8s service that publishes the metric endpoint. + type: string + stableWindow: + description: StableWindow is the aggregation window for metrics in a stable state. + type: integer + format: int64 + status: + description: Status communicates the observed state of the Metric (from the controller). + type: object + properties: + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + +--- +# Copyright 2018 The Knative 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 +# +# https://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. + +# Note: The schema part of the spec is auto-generated by hack/update-schemas.sh. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: podautoscalers.autoscaling.internal.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: autoscaling.internal.knative.dev + names: + kind: PodAutoscaler + plural: podautoscalers + singular: podautoscaler + categories: + - knative-internal + - autoscaling + shortNames: + - kpa + - pa + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - name: DesiredScale + type: integer + jsonPath: ".status.desiredScale" + - name: ActualScale + type: integer + jsonPath: ".status.actualScale" + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + schema: + openAPIV3Schema: + description: |- + PodAutoscaler is a Knative abstraction that encapsulates the interface by which Knative + components instantiate autoscalers. This definition is an abstraction that may be backed + by multiple definitions. For more information, see the Knative Pluggability presentation: + https://docs.google.com/presentation/d/19vW9HFZ6Puxt31biNZF3uLRejDmu82rxJIk1cWmxF7w/edit + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec holds the desired state of the PodAutoscaler (from the client). + type: object + required: + - protocolType + - scaleTargetRef + properties: + containerConcurrency: + description: |- + ContainerConcurrency specifies the maximum allowed + in-flight (concurrent) requests per container of the Revision. + Defaults to `0` which means unlimited concurrency. + type: integer + format: int64 + protocolType: + description: The application-layer protocol. Matches `ProtocolType` inferred from the revision spec. + type: string + reachability: + description: |- + Reachability specifies whether or not the `ScaleTargetRef` can be reached (ie. has a route). + Defaults to `ReachabilityUnknown` + type: string + scaleTargetRef: + description: |- + ScaleTargetRef defines the /scale-able resource that this PodAutoscaler + is responsible for quickly right-sizing. + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + x-kubernetes-map-type: atomic + status: + description: Status communicates the observed state of the PodAutoscaler (from the controller). + type: object + required: + - metricsServiceName + - serviceName + properties: + actualScale: + description: ActualScale shows the actual number of replicas for the revision. + type: integer + format: int32 + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + desiredScale: + description: DesiredScale shows the current desired number of replicas for the revision. + type: integer + format: int32 + metricsServiceName: + description: |- + MetricsServiceName is the K8s Service name that provides revision metrics. + The service is managed by the PA object. + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + serviceName: + description: |- + ServiceName is the K8s Service name that serves the revision, scaled by this PA. + The service is created and owned by the ServerlessService object owned by this PA. + type: string + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +# Note: The schema part of the spec is auto-generated by hack/update-schemas.sh. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: revisions.serving.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: serving.knative.dev + names: + kind: Revision + plural: revisions + singular: revision + categories: + - all + - knative + - serving + shortNames: + - rev + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - name: Config Name + type: string + jsonPath: ".metadata.labels['serving\\.knative\\.dev/configuration']" + - name: Generation + type: string # int in string form :( + jsonPath: ".metadata.labels['serving\\.knative\\.dev/configurationGeneration']" + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + - name: Actual Replicas + type: integer + jsonPath: ".status.actualReplicas" + - name: Desired Replicas + type: integer + jsonPath: ".status.desiredReplicas" + schema: + openAPIV3Schema: + description: |- + Revision is an immutable snapshot of code and configuration. A revision + references a container image. Revisions are created by updates to a + Configuration. + + See also: https://github.com/knative/serving/blob/main/docs/spec/overview.md#revision + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: RevisionSpec holds the desired state of the Revision (from the client). + type: object + required: + - containers + properties: + affinity: + description: This is accessible behind a feature flag - kubernetes.podspec-affinity + type: object + x-kubernetes-preserve-unknown-fields: true + automountServiceAccountToken: + description: AutomountServiceAccountToken indicates whether a service account token should be automatically mounted. + type: boolean + containerConcurrency: + description: |- + ContainerConcurrency specifies the maximum allowed in-flight (concurrent) + requests per container of the Revision. Defaults to `0` which means + concurrency to the application is not limited, and the system decides the + target concurrency for the autoscaler. + type: integer + format: int64 + containers: + description: |- + List of containers belonging to the pod. + Containers cannot currently be added or removed. + There must be at least one container in a Pod. + Cannot be updated. + type: array + items: + description: A single application container that you want to run within a pod. + type: object + properties: + args: + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + type: array + items: + type: string + x-kubernetes-list-type: atomic + command: + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + type: array + items: + type: string + x-kubernetes-list-type: atomic + env: + description: |- + List of environment variables to set in the container. + Cannot be updated. + type: array + items: + description: EnvVar represents an environment variable present in a Container. + type: object + required: + - name + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. Cannot be used if value is not empty. + type: object + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + type: object + required: + - key + properties: + key: + description: The key to select. + type: string + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the ConfigMap or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + fieldRef: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-fieldref + type: object + x-kubernetes-map-type: atomic + x-kubernetes-preserve-unknown-fields: true + resourceFieldRef: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-fieldref + type: object + x-kubernetes-map-type: atomic + x-kubernetes-preserve-unknown-fields: true + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + type: object + required: + - key + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. + type: array + items: + description: EnvFromSource represents the source of a set of ConfigMaps or Secrets + type: object + properties: + configMapRef: + description: The ConfigMap to select from + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + x-kubernetes-map-type: atomic + prefix: + description: Optional text to prepend to the name of each environment variable. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the Secret must be defined + type: boolean + x-kubernetes-map-type: atomic + x-kubernetes-list-type: atomic + image: + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + imagePullPolicy: + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + type: string + livenessProbe: + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + name: + description: |- + Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + type: array + items: + description: ContainerPort represents a network port in a single container. + type: object + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + type: integer + format: int32 + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + default: TCP + readinessProbe: + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + resources: + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + properties: + limits: + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + requests: + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + securityContext: + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + type: object + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + type: object + properties: + add: + description: This is accessible behind a feature flag - kubernetes.containerspec-addcapabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + x-kubernetes-list-type: atomic + privileged: + description: |- + Run container in privileged mode. This can only be set to explicitly to 'false' + type: boolean + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + type: object + required: + - type + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + startupProbe: + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + terminationMessagePath: + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. + type: string + terminationMessagePolicy: + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. + type: string + volumeMounts: + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. + type: array + items: + description: VolumeMount describes a mounting of a Volume within a container. + type: object + required: + - mountPath + - name + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-mount-propagation + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. + type: string + dnsConfig: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-dnsconfig + type: object + x-kubernetes-preserve-unknown-fields: true + dnsPolicy: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-dnspolicy + type: string + enableServiceLinks: + description: |- + EnableServiceLinks indicates whether information aboutservices should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Knative defaults this to false. + type: boolean + hostAliases: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostaliases + type: array + items: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostaliases + type: object + x-kubernetes-preserve-unknown-fields: true + hostIPC: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostipc + type: boolean + hostNetwork: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostnetwork + type: boolean + hostPID: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostpid + type: boolean + idleTimeoutSeconds: + description: |- + IdleTimeoutSeconds is the maximum duration in seconds a request will be allowed + to stay open while not receiving any bytes from the user's application. If + unspecified, a system default will be provided. + type: integer + format: int64 + imagePullSecrets: + description: |- + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. + If specified, these secrets will be passed to individual puller implementations for them to use. + More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod + type: array + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + x-kubernetes-map-type: atomic + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-init-containers + type: array + items: + description: This is accessible behind a feature flag - kubernetes.podspec-init-containers + type: object + x-kubernetes-preserve-unknown-fields: true + nodeSelector: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-nodeselector + type: object + additionalProperties: + type: string + x-kubernetes-map-type: atomic + priorityClassName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-priorityclassname + type: string + responseStartTimeoutSeconds: + description: |- + ResponseStartTimeoutSeconds is the maximum duration in seconds that the request + routing layer will wait for a request delivered to a container to begin + sending any network traffic. + type: integer + format: int64 + runtimeClassName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-runtimeclassname + type: string + schedulerName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-schedulername + type: string + securityContext: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-securitycontext + type: object + x-kubernetes-preserve-unknown-fields: true + serviceAccountName: + description: |- + ServiceAccountName is the name of the ServiceAccount to use to run this pod. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ + type: string + shareProcessNamespace: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-shareprocessnamespace + type: boolean + timeoutSeconds: + description: |- + TimeoutSeconds is the maximum duration in seconds that the request instance + is allowed to respond to a request. If unspecified, a system default will + be provided. + type: integer + format: int64 + tolerations: + description: This is accessible behind a feature flag - kubernetes.podspec-tolerations + type: array + items: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-tolerations + type: object + x-kubernetes-preserve-unknown-fields: true + topologySpreadConstraints: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-topologyspreadconstraints + type: array + items: + description: This is accessible behind a feature flag - kubernetes.podspec-topologyspreadconstraints + type: object + x-kubernetes-preserve-unknown-fields: true + volumes: + description: |- + List of volumes that can be mounted by containers belonging to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes + type: array + items: + description: Volume represents a named volume in a pod that may be accessed by any container in the pod. + type: object + required: + - name + properties: + configMap: + description: configMap represents a configMap that should populate this volume + type: object + properties: + defaultMode: + description: |- + defaultMode is optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + x-kubernetes-map-type: atomic + csi: + description: This is accessible behind a feature flag - kubernetes.podspec-volumes-csi + type: object + x-kubernetes-preserve-unknown-fields: true + emptyDir: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-emptydir + type: object + x-kubernetes-preserve-unknown-fields: true + hostPath: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-hostpath + type: object + x-kubernetes-preserve-unknown-fields: true + image: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-image + type: object + x-kubernetes-preserve-unknown-fields: true + name: + description: |- + name of the volume. + Must be a DNS_LABEL and unique within the pod. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + persistentVolumeClaim: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-persistent-volume-claim + type: object + x-kubernetes-preserve-unknown-fields: true + projected: + description: projected items for all in one resources secrets, configmaps, and downward API + type: object + properties: + defaultMode: + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + sources: + description: |- + sources is the list of volume projections. Each entry in this list + handles one source. + type: array + items: + description: |- + Projection that may be projected along with other supported volume types. + Exactly one of these fields must be set. + type: object + properties: + configMap: + description: configMap information about the configMap data to project + type: object + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the downwardAPI data to project + type: object + properties: + items: + description: Items is a list of DownwardAPIVolume file + type: array + items: + description: DownwardAPIVolumeFile represents information to create the file containing the pod field + type: object + required: + - path + properties: + fieldRef: + description: 'Required: Selects a field of the pod: only annotations, labels, name, namespace and uid are supported.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: 'Required: Path is the relative path name of the file to be created. Must not be absolute or contain the ''..'' path. Must be utf-8 encoded. The first item of the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + type: object + required: + - resource + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + x-kubernetes-map-type: atomic + x-kubernetes-list-type: atomic + secret: + description: secret information about the secret data to project + type: object + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional field specify whether the Secret or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information about the serviceAccountToken data to project + type: object + required: + - path + properties: + audience: + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. + type: integer + format: int64 + path: + description: |- + path is the path relative to the mount point of the file to project the + token into. + type: string + x-kubernetes-list-type: atomic + secret: + description: |- + secret represents a secret that should populate this volume. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: object + properties: + defaultMode: + description: |- + defaultMode is Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values + for mode bits. Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + items: + description: |- + items If unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + optional: + description: optional field specify whether the Secret or its keys must be defined + type: boolean + secretName: + description: |- + secretName is the name of the secret in the pod's namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: string + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + status: + description: RevisionStatus communicates the observed state of the Revision (from the controller). + type: object + properties: + actualReplicas: + description: ActualReplicas reflects the amount of ready pods running this revision. + type: integer + format: int32 + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + containerStatuses: + description: |- + ContainerStatuses is a slice of images present in .Spec.Container[*].Image + to their respective digests and their container name. + The digests are resolved during the creation of Revision. + ContainerStatuses holds the container name and image digests + for both serving and non serving containers. + ref: http://bit.ly/image-digests + type: array + items: + description: ContainerStatus holds the information of container name and image digest value + type: object + properties: + imageDigest: + type: string + name: + type: string + desiredReplicas: + description: DesiredReplicas reflects the desired amount of pods running this revision. + type: integer + format: int32 + initContainerStatuses: + description: |- + InitContainerStatuses is a slice of images present in .Spec.InitContainer[*].Image + to their respective digests and their container name. + The digests are resolved during the creation of Revision. + ContainerStatuses holds the container name and image digests + for both serving and non serving containers. + ref: http://bit.ly/image-digests + type: array + items: + description: ContainerStatus holds the information of container name and image digest value + type: object + properties: + imageDigest: + type: string + name: + type: string + logUrl: + description: |- + LogURL specifies the generated logging url for this particular revision + based on the revision url template specified in the controller's config. + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +# Note: The schema part of the spec is auto-generated by hack/update-schemas.sh. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: routes.serving.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" + duck.knative.dev/addressable: "true" +spec: + group: serving.knative.dev + names: + kind: Route + plural: routes + singular: route + categories: + - all + - knative + - serving + shortNames: + - rt + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - name: URL + type: string + jsonPath: .status.url + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + schema: + openAPIV3Schema: + description: |- + Route is responsible for configuring ingress over a collection of Revisions. + Some of the Revisions a Route distributes traffic over may be specified by + referencing the Configuration responsible for creating them; in these cases + the Route is additionally responsible for monitoring the Configuration for + "latest ready revision" changes, and smoothly rolling out latest revisions. + See also: https://github.com/knative/serving/blob/main/docs/spec/overview.md#route + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec holds the desired state of the Route (from the client). + type: object + properties: + traffic: + description: |- + Traffic specifies how to distribute traffic over a collection of + revisions and configurations. + type: array + items: + description: TrafficTarget holds a single entry of the routing table for a Route. + type: object + properties: + configurationName: + description: |- + ConfigurationName of a configuration to whose latest revision we will send + this portion of traffic. When the "status.latestReadyRevisionName" of the + referenced configuration changes, we will automatically migrate traffic + from the prior "latest ready" revision to the new one. This field is never + set in Route's status, only its spec. This is mutually exclusive with + RevisionName. + type: string + latestRevision: + description: |- + LatestRevision may be optionally provided to indicate that the latest + ready Revision of the Configuration should be used for this traffic + target. When provided LatestRevision must be true if RevisionName is + empty; it must be false when RevisionName is non-empty. + type: boolean + percent: + description: |- + Percent indicates that percentage based routing should be used and + the value indicates the percent of traffic that is be routed to this + Revision or Configuration. `0` (zero) mean no traffic, `100` means all + traffic. + When percentage based routing is being used the follow rules apply: + - the sum of all percent values must equal 100 + - when not specified, the implied value for `percent` is zero for + that particular Revision or Configuration + type: integer + format: int64 + revisionName: + description: |- + RevisionName of a specific revision to which to send this portion of + traffic. This is mutually exclusive with ConfigurationName. + type: string + tag: + description: |- + Tag is optionally used to expose a dedicated url for referencing + this target exclusively. + type: string + url: + description: |- + URL displays the URL for accessing named traffic targets. URL is displayed in + status, and is disallowed on spec. URL must contain a scheme (e.g. http://) and + a hostname, but may not contain anything else (e.g. basic auth, url path, etc.) + type: string + status: + description: Status communicates the observed state of the Route (from the controller). + type: object + properties: + address: + description: Address holds the information needed for a Route to be the target of an event. + type: object + properties: + CACerts: + description: |- + CACerts is the Certification Authority (CA) certificates in PEM format + according to https://www.rfc-editor.org/rfc/rfc7468. + type: string + audience: + description: Audience is the OIDC audience for this address. + type: string + name: + description: Name is the name of the address. + type: string + url: + type: string + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + traffic: + description: |- + Traffic holds the configured traffic distribution. + These entries will always contain RevisionName references. + When ConfigurationName appears in the spec, this will hold the + LatestReadyRevisionName that we last observed. + type: array + items: + description: TrafficTarget holds a single entry of the routing table for a Route. + type: object + properties: + configurationName: + description: |- + ConfigurationName of a configuration to whose latest revision we will send + this portion of traffic. When the "status.latestReadyRevisionName" of the + referenced configuration changes, we will automatically migrate traffic + from the prior "latest ready" revision to the new one. This field is never + set in Route's status, only its spec. This is mutually exclusive with + RevisionName. + type: string + latestRevision: + description: |- + LatestRevision may be optionally provided to indicate that the latest + ready Revision of the Configuration should be used for this traffic + target. When provided LatestRevision must be true if RevisionName is + empty; it must be false when RevisionName is non-empty. + type: boolean + percent: + description: |- + Percent indicates that percentage based routing should be used and + the value indicates the percent of traffic that is be routed to this + Revision or Configuration. `0` (zero) mean no traffic, `100` means all + traffic. + When percentage based routing is being used the follow rules apply: + - the sum of all percent values must equal 100 + - when not specified, the implied value for `percent` is zero for + that particular Revision or Configuration + type: integer + format: int64 + revisionName: + description: |- + RevisionName of a specific revision to which to send this portion of + traffic. This is mutually exclusive with ConfigurationName. + type: string + tag: + description: |- + Tag is optionally used to expose a dedicated url for referencing + this target exclusively. + type: string + url: + description: |- + URL displays the URL for accessing named traffic targets. URL is displayed in + status, and is disallowed on spec. URL must contain a scheme (e.g. http://) and + a hostname, but may not contain anything else (e.g. basic auth, url path, etc.) + type: string + url: + description: |- + URL holds the url that will distribute traffic over the provided traffic targets. + It generally has the form http[s]://{route-name}.{route-namespace}.{cluster-level-suffix} + type: string + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: serverlessservices.networking.internal.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: networking + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: networking.internal.knative.dev + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + description: |- + ServerlessService is a proxy for the K8s service objects containing the + endpoints for the revision, whether those are endpoints of the activator or + revision pods. + See: https://knative.page.link/naxz for details. + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Spec is the desired state of the ServerlessService. + More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + required: + - objectRef + - protocolType + properties: + mode: + description: Mode describes the mode of operation of the ServerlessService. + type: string + numActivators: + description: |- + NumActivators contains number of Activators that this revision should be + assigned. + O means — assign all. + type: integer + format: int32 + objectRef: + description: |- + ObjectRef defines the resource that this ServerlessService + is responsible for making "serverless". + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + x-kubernetes-map-type: atomic + protocolType: + description: |- + The application-layer protocol. Matches `RevisionProtocolType` set on the owning pa/revision. + serving imports networking, so just use string. + type: string + status: + description: |- + Status is the current state of the ServerlessService. + More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + properties: + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + privateServiceName: + description: |- + PrivateServiceName holds the name of a core K8s Service resource that + load balances over the user service pods backing this Revision. + type: string + serviceName: + description: |- + ServiceName holds the name of a core K8s Service resource that + load balances over the pods backing this Revision (activator or revision). + type: string + additionalPrinterColumns: + - name: Mode + type: string + jsonPath: ".spec.mode" + - name: Activators + type: integer + jsonPath: ".spec.numActivators" + - name: ServiceName + type: string + jsonPath: ".status.serviceName" + - name: PrivateServiceName + type: string + jsonPath: ".status.privateServiceName" + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + names: + kind: ServerlessService + plural: serverlessservices + singular: serverlessservice + categories: + - knative-internal + - networking + shortNames: + - sks + scope: Namespaced + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +# Note: The schema part of the spec is auto-generated by hack/update-schemas.sh. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: services.serving.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" + duck.knative.dev/addressable: "true" + duck.knative.dev/podspecable: "true" +spec: + group: serving.knative.dev + names: + kind: Service + plural: services + singular: service + categories: + - all + - knative + - serving + shortNames: + - kservice + - ksvc + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - name: URL + type: string + jsonPath: .status.url + - name: LatestCreated + type: string + jsonPath: .status.latestCreatedRevisionName + - name: LatestReady + type: string + jsonPath: .status.latestReadyRevisionName + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + schema: + openAPIV3Schema: + description: |- + Service acts as a top-level container that manages a Route and Configuration + which implement a network service. Service exists to provide a singular + abstraction which can be access controlled, reasoned about, and which + encapsulates software lifecycle decisions such as rollout policy and + team resource ownership. Service acts only as an orchestrator of the + underlying Routes and Configurations (much as a kubernetes Deployment + orchestrates ReplicaSets), and its usage is optional but recommended. + + The Service's controller will track the statuses of its owned Configuration + and Route, reflecting their statuses and conditions as its own. + + See also: https://github.com/knative/serving/blob/main/docs/spec/overview.md#service + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + ServiceSpec represents the configuration for the Service object. + A Service's specification is the union of the specifications for a Route + and Configuration. The Service restricts what can be expressed in these + fields, e.g. the Route must reference the provided Configuration; + however, these limitations also enable friendlier defaulting, + e.g. Route never needs a Configuration name, and may be defaulted to + the appropriate "run latest" spec. + type: object + properties: + template: + description: Template holds the latest specification for the Revision to be stamped out. + type: object + properties: + metadata: + type: object + properties: + annotations: + type: object + additionalProperties: + type: string + finalizers: + type: array + items: + type: string + labels: + type: object + additionalProperties: + type: string + name: + type: string + namespace: + type: string + x-kubernetes-preserve-unknown-fields: true + spec: + description: RevisionSpec holds the desired state of the Revision (from the client). + type: object + required: + - containers + properties: + affinity: + description: This is accessible behind a feature flag - kubernetes.podspec-affinity + type: object + x-kubernetes-preserve-unknown-fields: true + automountServiceAccountToken: + description: AutomountServiceAccountToken indicates whether a service account token should be automatically mounted. + type: boolean + containerConcurrency: + description: |- + ContainerConcurrency specifies the maximum allowed in-flight (concurrent) + requests per container of the Revision. Defaults to `0` which means + concurrency to the application is not limited, and the system decides the + target concurrency for the autoscaler. + type: integer + format: int64 + containers: + description: |- + List of containers belonging to the pod. + Containers cannot currently be added or removed. + There must be at least one container in a Pod. + Cannot be updated. + type: array + items: + description: A single application container that you want to run within a pod. + type: object + properties: + args: + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + type: array + items: + type: string + x-kubernetes-list-type: atomic + command: + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + type: array + items: + type: string + x-kubernetes-list-type: atomic + env: + description: |- + List of environment variables to set in the container. + Cannot be updated. + type: array + items: + description: EnvVar represents an environment variable present in a Container. + type: object + required: + - name + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. Cannot be used if value is not empty. + type: object + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + type: object + required: + - key + properties: + key: + description: The key to select. + type: string + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the ConfigMap or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + fieldRef: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-fieldref + type: object + x-kubernetes-map-type: atomic + x-kubernetes-preserve-unknown-fields: true + resourceFieldRef: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-fieldref + type: object + x-kubernetes-map-type: atomic + x-kubernetes-preserve-unknown-fields: true + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + type: object + required: + - key + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. + type: array + items: + description: EnvFromSource represents the source of a set of ConfigMaps or Secrets + type: object + properties: + configMapRef: + description: The ConfigMap to select from + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + x-kubernetes-map-type: atomic + prefix: + description: Optional text to prepend to the name of each environment variable. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the Secret must be defined + type: boolean + x-kubernetes-map-type: atomic + x-kubernetes-list-type: atomic + image: + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + imagePullPolicy: + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + type: string + livenessProbe: + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + name: + description: |- + Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + type: array + items: + description: ContainerPort represents a network port in a single container. + type: object + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + type: integer + format: int32 + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + default: TCP + readinessProbe: + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + resources: + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + properties: + limits: + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + requests: + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + securityContext: + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + type: object + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + type: object + properties: + add: + description: This is accessible behind a feature flag - kubernetes.containerspec-addcapabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + x-kubernetes-list-type: atomic + privileged: + description: |- + Run container in privileged mode. This can only be set to explicitly to 'false' + type: boolean + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + type: object + required: + - type + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + startupProbe: + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + terminationMessagePath: + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. + type: string + terminationMessagePolicy: + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. + type: string + volumeMounts: + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. + type: array + items: + description: VolumeMount describes a mounting of a Volume within a container. + type: object + required: + - mountPath + - name + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-mount-propagation + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. + type: string + dnsConfig: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-dnsconfig + type: object + x-kubernetes-preserve-unknown-fields: true + dnsPolicy: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-dnspolicy + type: string + enableServiceLinks: + description: |- + EnableServiceLinks indicates whether information aboutservices should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Knative defaults this to false. + type: boolean + hostAliases: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostaliases + type: array + items: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostaliases + type: object + x-kubernetes-preserve-unknown-fields: true + hostIPC: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostipc + type: boolean + hostNetwork: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostnetwork + type: boolean + hostPID: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostpid + type: boolean + idleTimeoutSeconds: + description: |- + IdleTimeoutSeconds is the maximum duration in seconds a request will be allowed + to stay open while not receiving any bytes from the user's application. If + unspecified, a system default will be provided. + type: integer + format: int64 + imagePullSecrets: + description: |- + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. + If specified, these secrets will be passed to individual puller implementations for them to use. + More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod + type: array + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + x-kubernetes-map-type: atomic + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-init-containers + type: array + items: + description: This is accessible behind a feature flag - kubernetes.podspec-init-containers + type: object + x-kubernetes-preserve-unknown-fields: true + nodeSelector: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-nodeselector + type: object + additionalProperties: + type: string + x-kubernetes-map-type: atomic + priorityClassName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-priorityclassname + type: string + responseStartTimeoutSeconds: + description: |- + ResponseStartTimeoutSeconds is the maximum duration in seconds that the request + routing layer will wait for a request delivered to a container to begin + sending any network traffic. + type: integer + format: int64 + runtimeClassName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-runtimeclassname + type: string + schedulerName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-schedulername + type: string + securityContext: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-securitycontext + type: object + x-kubernetes-preserve-unknown-fields: true + serviceAccountName: + description: |- + ServiceAccountName is the name of the ServiceAccount to use to run this pod. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ + type: string + shareProcessNamespace: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-shareprocessnamespace + type: boolean + timeoutSeconds: + description: |- + TimeoutSeconds is the maximum duration in seconds that the request instance + is allowed to respond to a request. If unspecified, a system default will + be provided. + type: integer + format: int64 + tolerations: + description: This is accessible behind a feature flag - kubernetes.podspec-tolerations + type: array + items: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-tolerations + type: object + x-kubernetes-preserve-unknown-fields: true + topologySpreadConstraints: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-topologyspreadconstraints + type: array + items: + description: This is accessible behind a feature flag - kubernetes.podspec-topologyspreadconstraints + type: object + x-kubernetes-preserve-unknown-fields: true + volumes: + description: |- + List of volumes that can be mounted by containers belonging to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes + type: array + items: + description: Volume represents a named volume in a pod that may be accessed by any container in the pod. + type: object + required: + - name + properties: + configMap: + description: configMap represents a configMap that should populate this volume + type: object + properties: + defaultMode: + description: |- + defaultMode is optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + x-kubernetes-map-type: atomic + csi: + description: This is accessible behind a feature flag - kubernetes.podspec-volumes-csi + type: object + x-kubernetes-preserve-unknown-fields: true + emptyDir: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-emptydir + type: object + x-kubernetes-preserve-unknown-fields: true + hostPath: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-hostpath + type: object + x-kubernetes-preserve-unknown-fields: true + image: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-image + type: object + x-kubernetes-preserve-unknown-fields: true + name: + description: |- + name of the volume. + Must be a DNS_LABEL and unique within the pod. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + persistentVolumeClaim: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-persistent-volume-claim + type: object + x-kubernetes-preserve-unknown-fields: true + projected: + description: projected items for all in one resources secrets, configmaps, and downward API + type: object + properties: + defaultMode: + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + sources: + description: |- + sources is the list of volume projections. Each entry in this list + handles one source. + type: array + items: + description: |- + Projection that may be projected along with other supported volume types. + Exactly one of these fields must be set. + type: object + properties: + configMap: + description: configMap information about the configMap data to project + type: object + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the downwardAPI data to project + type: object + properties: + items: + description: Items is a list of DownwardAPIVolume file + type: array + items: + description: DownwardAPIVolumeFile represents information to create the file containing the pod field + type: object + required: + - path + properties: + fieldRef: + description: 'Required: Selects a field of the pod: only annotations, labels, name, namespace and uid are supported.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: 'Required: Path is the relative path name of the file to be created. Must not be absolute or contain the ''..'' path. Must be utf-8 encoded. The first item of the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + type: object + required: + - resource + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + x-kubernetes-map-type: atomic + x-kubernetes-list-type: atomic + secret: + description: secret information about the secret data to project + type: object + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional field specify whether the Secret or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information about the serviceAccountToken data to project + type: object + required: + - path + properties: + audience: + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. + type: integer + format: int64 + path: + description: |- + path is the path relative to the mount point of the file to project the + token into. + type: string + x-kubernetes-list-type: atomic + secret: + description: |- + secret represents a secret that should populate this volume. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: object + properties: + defaultMode: + description: |- + defaultMode is Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values + for mode bits. Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + items: + description: |- + items If unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + optional: + description: optional field specify whether the Secret or its keys must be defined + type: boolean + secretName: + description: |- + secretName is the name of the secret in the pod's namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: string + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + traffic: + description: |- + Traffic specifies how to distribute traffic over a collection of + revisions and configurations. + type: array + items: + description: TrafficTarget holds a single entry of the routing table for a Route. + type: object + properties: + configurationName: + description: |- + ConfigurationName of a configuration to whose latest revision we will send + this portion of traffic. When the "status.latestReadyRevisionName" of the + referenced configuration changes, we will automatically migrate traffic + from the prior "latest ready" revision to the new one. This field is never + set in Route's status, only its spec. This is mutually exclusive with + RevisionName. + type: string + latestRevision: + description: |- + LatestRevision may be optionally provided to indicate that the latest + ready Revision of the Configuration should be used for this traffic + target. When provided LatestRevision must be true if RevisionName is + empty; it must be false when RevisionName is non-empty. + type: boolean + percent: + description: |- + Percent indicates that percentage based routing should be used and + the value indicates the percent of traffic that is be routed to this + Revision or Configuration. `0` (zero) mean no traffic, `100` means all + traffic. + When percentage based routing is being used the follow rules apply: + - the sum of all percent values must equal 100 + - when not specified, the implied value for `percent` is zero for + that particular Revision or Configuration + type: integer + format: int64 + revisionName: + description: |- + RevisionName of a specific revision to which to send this portion of + traffic. This is mutually exclusive with ConfigurationName. + type: string + tag: + description: |- + Tag is optionally used to expose a dedicated url for referencing + this target exclusively. + type: string + url: + description: |- + URL displays the URL for accessing named traffic targets. URL is displayed in + status, and is disallowed on spec. URL must contain a scheme (e.g. http://) and + a hostname, but may not contain anything else (e.g. basic auth, url path, etc.) + type: string + status: + description: ServiceStatus represents the Status stanza of the Service resource. + type: object + properties: + address: + description: Address holds the information needed for a Route to be the target of an event. + type: object + properties: + CACerts: + description: |- + CACerts is the Certification Authority (CA) certificates in PEM format + according to https://www.rfc-editor.org/rfc/rfc7468. + type: string + audience: + description: Audience is the OIDC audience for this address. + type: string + name: + description: Name is the name of the address. + type: string + url: + type: string + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + latestCreatedRevisionName: + description: |- + LatestCreatedRevisionName is the last revision that was created from this + Configuration. It might not be ready yet, for that use LatestReadyRevisionName. + type: string + latestReadyRevisionName: + description: |- + LatestReadyRevisionName holds the name of the latest Revision stamped out + from this Configuration that has had its "Ready" condition become "True". + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + traffic: + description: |- + Traffic holds the configured traffic distribution. + These entries will always contain RevisionName references. + When ConfigurationName appears in the spec, this will hold the + LatestReadyRevisionName that we last observed. + type: array + items: + description: TrafficTarget holds a single entry of the routing table for a Route. + type: object + properties: + configurationName: + description: |- + ConfigurationName of a configuration to whose latest revision we will send + this portion of traffic. When the "status.latestReadyRevisionName" of the + referenced configuration changes, we will automatically migrate traffic + from the prior "latest ready" revision to the new one. This field is never + set in Route's status, only its spec. This is mutually exclusive with + RevisionName. + type: string + latestRevision: + description: |- + LatestRevision may be optionally provided to indicate that the latest + ready Revision of the Configuration should be used for this traffic + target. When provided LatestRevision must be true if RevisionName is + empty; it must be false when RevisionName is non-empty. + type: boolean + percent: + description: |- + Percent indicates that percentage based routing should be used and + the value indicates the percent of traffic that is be routed to this + Revision or Configuration. `0` (zero) mean no traffic, `100` means all + traffic. + When percentage based routing is being used the follow rules apply: + - the sum of all percent values must equal 100 + - when not specified, the implied value for `percent` is zero for + that particular Revision or Configuration + type: integer + format: int64 + revisionName: + description: |- + RevisionName of a specific revision to which to send this portion of + traffic. This is mutually exclusive with ConfigurationName. + type: string + tag: + description: |- + Tag is optionally used to expose a dedicated url for referencing + this target exclusively. + type: string + url: + description: |- + URL displays the URL for accessing named traffic targets. URL is displayed in + status, and is disallowed on spec. URL must contain a scheme (e.g. http://) and + a hostname, but may not contain anything else (e.g. basic auth, url path, etc.) + type: string + url: + description: |- + URL holds the url that will distribute traffic over the provided traffic targets. + It generally has the form http[s]://{route-name}.{route-namespace}.{cluster-level-suffix} + type: string + +--- +# Copyright 2018 The Knative 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 +# +# https://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. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: images.caching.internal.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: caching.internal.knative.dev + names: + kind: Image + plural: images + singular: image + categories: + - knative-internal + - caching + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + description: |- + Image is a Knative abstraction that encapsulates the interface by which Knative + components express a desire to have a particular image cached. + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec holds the desired state of the Image (from the client). + type: object + required: + - image + properties: + image: + description: Image is the name of the container image url to cache across the cluster. + type: string + imagePullSecrets: + description: |- + ImagePullSecrets contains the names of the Kubernetes Secrets containing login + information used by the Pods which will run this container. + type: array + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + x-kubernetes-map-type: atomic + serviceAccountName: + description: |- + ServiceAccountName is the name of the Kubernetes ServiceAccount as which the Pods + will run this container. This is potentially used to authenticate the image pull + if the service account has attached pull secrets. For more information: + https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account + type: string + status: + description: Status communicates the observed state of the Image (from the controller). + type: object + properties: + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + additionalPrinterColumns: + - name: Image + type: string + jsonPath: .spec.image + +--- diff --git a/integration/fixtures/knative/01-rbac.yml b/integration/fixtures/knative/01-rbac.yml new file mode 100644 index 000000000..af29709e9 --- /dev/null +++ b/integration/fixtures/knative/01-rbac.yml @@ -0,0 +1,50 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: knative-networking-role +rules: + - apiGroups: + - "" + resources: + - services + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - secrets + verbs: + - get + - list + - watch + - apiGroups: + - networking.internal.knative.dev + resources: + - ingresses + verbs: + - get + - list + - watch + - apiGroups: + - networking.internal.knative.dev + resources: + - ingresses/status + verbs: + - update + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: traefik +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: knative-networking-role +subjects: + - kind: ServiceAccount + name: traefik + namespace: traefik diff --git a/integration/fixtures/knative/02-traefik.yml b/integration/fixtures/knative/02-traefik.yml new file mode 100644 index 000000000..15588a4a6 --- /dev/null +++ b/integration/fixtures/knative/02-traefik.yml @@ -0,0 +1,102 @@ +--- +kind: Namespace +apiVersion: v1 +metadata: + name: traefik + +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: traefik + namespace: traefik + +--- +kind: Deployment +apiVersion: apps/v1 +metadata: + name: traefik + namespace: traefik + labels: + app: traefik +spec: + replicas: 1 + selector: + matchLabels: + app: traefik + template: + metadata: + labels: + app: traefik + spec: + serviceAccountName: traefik + containers: + - name: traefik + image: traefik/traefik:latest + imagePullPolicy: Never + args: + - --api.insecure + - --log.level=debug + - --entrypoints.pweb.address=:80 + - --entrypoints.pwebsecure.address=:443 + - --entrypoints.privweb.address=:8080 + - --entrypoints.privwebsecure.address=:4443 + - --entrypoints.traefik.address=:9000 + - --experimental.knative + - --providers.knative.publicEntrypoints=pweb,pwebsecure + - --providers.knative.publicService.namespace=traefik + - --providers.knative.publicService.name=traefik + - --providers.knative.privateEntrypoints=privweb,privwebsecure + - --providers.knative.privateService.namespace=traefik + - --providers.knative.privateService.name=privtraefik + - --providers.knative.throttleduration=2s + + ports: + - name: pweb + containerPort: 80 + - name: pwebsecure + containerPort: 443 + - name: privweb + containerPort: 8080 + - name: privwebsecure + containerPort: 4443 + - name: traefik + containerPort: 9000 + +--- +apiVersion: v1 +kind: Service +metadata: + name: traefik + namespace: traefik +spec: + type: LoadBalancer + selector: + app: traefik + ports: + - port: 80 + name: web + targetPort: pweb + - port: 443 + name: websecure + targetPort: pwebsecure + - port: 9000 + name: traefik + targetPort: traefik + +--- +apiVersion: v1 +kind: Service +metadata: + name: privtraefik + namespace: traefik +spec: + selector: + app: traefik + ports: + - port: 80 + name: web + targetPort: privweb + - port: 443 + name: websecure + targetPort: privwebsecure diff --git a/integration/fixtures/knative/03-knative-serving-v1.19.0.yaml b/integration/fixtures/knative/03-knative-serving-v1.19.0.yaml new file mode 100644 index 000000000..98a89d394 --- /dev/null +++ b/integration/fixtures/knative/03-knative-serving-v1.19.0.yaml @@ -0,0 +1,9513 @@ +# Copyright 2018 The Knative 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 +# +# https://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. + +apiVersion: v1 +kind: Namespace +metadata: + name: knative-serving + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + +--- +# Copyright 2023 The Knative 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 +# +# https://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. + +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: knative-serving-activator + namespace: knative-serving + labels: + serving.knative.dev/controller: "true" + app.kubernetes.io/version: "1.19.0" + app.kubernetes.io/name: knative-serving +rules: + - apiGroups: [""] + resources: ["configmaps", "secrets"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list", "watch"] + resourceNames: ["routing-serving-certs", "knative-serving-certs"] +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: knative-serving-activator-cluster + labels: + serving.knative.dev/controller: "true" + app.kubernetes.io/version: "1.19.0" + app.kubernetes.io/name: knative-serving +rules: + - apiGroups: [""] + resources: ["services", "endpoints"] + verbs: ["get", "list", "watch"] + - apiGroups: ["serving.knative.dev"] + resources: ["revisions"] + verbs: ["get", "list", "watch"] + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +# Use this aggregated ClusterRole when you need readonly access to "Addressables" +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + # Named like this to avoid clashing with eventing's existing `addressable-resolver` role + # (which should be identical, but isn't guaranteed to be installed alongside serving). + name: knative-serving-aggregated-addressable-resolver + labels: + app.kubernetes.io/version: "1.19.0" + app.kubernetes.io/name: knative-serving +aggregationRule: + clusterRoleSelectors: + - matchLabels: + duck.knative.dev/addressable: "true" +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: knative-serving-addressable-resolver + labels: + app.kubernetes.io/version: "1.19.0" + app.kubernetes.io/name: knative-serving + # Labeled to facilitate aggregated cluster roles that act on Addressables. + duck.knative.dev/addressable: "true" +# Do not use this role directly. These rules will be added to the "addressable-resolver" role. +rules: + - apiGroups: + - serving.knative.dev + resources: + - routes + - routes/status + - services + - services/status + verbs: + - get + - list + - watch + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: knative-serving-namespaced-admin + labels: + rbac.authorization.k8s.io/aggregate-to-admin: "true" + app.kubernetes.io/version: "1.19.0" + app.kubernetes.io/name: knative-serving +rules: + - apiGroups: ["serving.knative.dev"] + resources: ["*"] + verbs: ["*"] + - apiGroups: ["networking.internal.knative.dev", "autoscaling.internal.knative.dev", "caching.internal.knative.dev"] + resources: ["*"] + verbs: ["get", "list", "watch"] +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: knative-serving-namespaced-edit + labels: + rbac.authorization.k8s.io/aggregate-to-edit: "true" + app.kubernetes.io/version: "1.19.0" + app.kubernetes.io/name: knative-serving +rules: + - apiGroups: ["serving.knative.dev"] + resources: ["*"] + verbs: ["create", "update", "patch", "delete"] + - apiGroups: ["networking.internal.knative.dev", "autoscaling.internal.knative.dev", "caching.internal.knative.dev"] + resources: ["*"] + verbs: ["get", "list", "watch"] +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: knative-serving-namespaced-view + labels: + rbac.authorization.k8s.io/aggregate-to-view: "true" + app.kubernetes.io/version: "1.19.0" + app.kubernetes.io/name: knative-serving +rules: + - apiGroups: ["serving.knative.dev", "networking.internal.knative.dev", "autoscaling.internal.knative.dev", "caching.internal.knative.dev"] + resources: ["*"] + verbs: ["get", "list", "watch"] + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: knative-serving-core + labels: + serving.knative.dev/controller: "true" + app.kubernetes.io/version: "1.19.0" + app.kubernetes.io/name: knative-serving +rules: + - apiGroups: [""] + resources: ["pods", "namespaces", "secrets", "configmaps", "endpoints", "services", "events", "serviceaccounts"] + verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] + - apiGroups: [""] + resources: ["endpoints/restricted"] # Permission for RestrictedEndpointsAdmission + verbs: ["create"] + - apiGroups: [""] + resources: ["namespaces/finalizers"] # finalizers are needed for the owner reference of the webhook + verbs: ["update"] + - apiGroups: ["apps"] + resources: ["deployments", "deployments/finalizers"] # finalizers are needed for the owner reference of the webhook + verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] + - apiGroups: ["admissionregistration.k8s.io"] + resources: ["mutatingwebhookconfigurations", "validatingwebhookconfigurations"] + verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] + - apiGroups: ["apiextensions.k8s.io"] + resources: ["customresourcedefinitions", "customresourcedefinitions/status"] + verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] + - apiGroups: ["autoscaling"] + resources: ["horizontalpodautoscalers"] + verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] + - apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] + - apiGroups: ["serving.knative.dev", "autoscaling.internal.knative.dev", "networking.internal.knative.dev"] + resources: ["*", "*/status", "*/finalizers"] + verbs: ["get", "list", "create", "update", "delete", "deletecollection", "patch", "watch"] + - apiGroups: ["caching.internal.knative.dev"] + resources: ["images"] + verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] + - apiGroups: ["cert-manager.io"] + resources: ["certificates", "clusterissuers", "certificaterequests", "issuers"] + verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] + - apiGroups: ["acme.cert-manager.io"] + resources: ["challenges"] + verbs: ["get", "list", "create", "update", "delete", "patch", "watch"] + - apiGroups: ["rbac.authorization.k8s.io"] + resources: ["clusterroles"] + verbs: ["delete"] + resourceNames: ["knative-serving-certmanager"] + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: knative-serving-podspecable-binding + labels: + app.kubernetes.io/version: "1.19.0" + app.kubernetes.io/name: knative-serving + # Labeled to facilitate aggregated cluster roles that act on PodSpecables. + duck.knative.dev/podspecable: "true" +# Do not use this role directly. These rules will be added to the "podspecable-binder" role. +rules: + - apiGroups: + - serving.knative.dev + resources: + - configurations + - services + verbs: + - list + - watch + - patch + +--- +# Copyright 2018 The Knative 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 +# +# https://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. + +apiVersion: v1 +kind: ServiceAccount +metadata: + name: controller + namespace: knative-serving + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: knative-serving-admin + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +aggregationRule: + clusterRoleSelectors: + - matchLabels: + serving.knative.dev/controller: "true" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: knative-serving-controller-admin + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +subjects: + - kind: ServiceAccount + name: controller + namespace: knative-serving +roleRef: + kind: ClusterRole + name: knative-serving-admin + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: knative-serving-controller-addressable-resolver + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +subjects: + - kind: ServiceAccount + name: controller + namespace: knative-serving +roleRef: + kind: ClusterRole + name: knative-serving-aggregated-addressable-resolver + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: activator + namespace: knative-serving + labels: + app.kubernetes.io/component: activator + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: knative-serving-activator + namespace: knative-serving + labels: + app.kubernetes.io/component: activator + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +subjects: + - kind: ServiceAccount + name: activator + namespace: knative-serving +roleRef: + kind: Role + name: knative-serving-activator + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: knative-serving-activator-cluster + labels: + app.kubernetes.io/component: activator + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +subjects: + - kind: ServiceAccount + name: activator + namespace: knative-serving +roleRef: + kind: ClusterRole + name: knative-serving-activator-cluster + apiGroup: rbac.authorization.k8s.io + +--- +# Copyright 2018 The Knative 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 +# +# https://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. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: images.caching.internal.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: caching.internal.knative.dev + names: + kind: Image + plural: images + singular: image + categories: + - knative-internal + - caching + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + description: |- + Image is a Knative abstraction that encapsulates the interface by which Knative + components express a desire to have a particular image cached. + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec holds the desired state of the Image (from the client). + type: object + required: + - image + properties: + image: + description: Image is the name of the container image url to cache across the cluster. + type: string + imagePullSecrets: + description: |- + ImagePullSecrets contains the names of the Kubernetes Secrets containing login + information used by the Pods which will run this container. + type: array + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + x-kubernetes-map-type: atomic + serviceAccountName: + description: |- + ServiceAccountName is the name of the Kubernetes ServiceAccount as which the Pods + will run this container. This is potentially used to authenticate the image pull + if the service account has attached pull secrets. For more information: + https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account + type: string + status: + description: Status communicates the observed state of the Image (from the controller). + type: object + properties: + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + additionalPrinterColumns: + - name: Image + type: string + jsonPath: .spec.image + +--- +apiVersion: networking.internal.knative.dev/v1alpha1 +kind: Certificate +metadata: + annotations: + networking.knative.dev/certificate.class: cert-manager.certificate.networking.knative.dev + labels: + networking.knative.dev/certificate-type: system-internal + name: routing-serving-certs + namespace: knative-serving +spec: + dnsNames: + - kn-routing + - data-plane.knative.dev # for reverse-compatibility with net-* implementations that do not work with multi-SANs + secretName: routing-serving-certs + +--- +# Copyright 2020 The Knative 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 +# +# https://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. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: certificates.networking.internal.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: networking + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: networking.internal.knative.dev + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + description: |- + Certificate is responsible for provisioning a SSL certificate for the + given hosts. It is a Knative abstraction for various SSL certificate + provisioning solutions (such as cert-manager or self-signed SSL certificate). + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Spec is the desired state of the Certificate. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + required: + - dnsNames + - secretName + properties: + dnsNames: + description: |- + DNSNames is a list of DNS names the Certificate could support. + The wildcard format of DNSNames (e.g. *.default.example.com) is supported. + type: array + items: + type: string + domain: + description: Domain is the top level domain of the values for DNSNames. + type: string + secretName: + description: SecretName is the name of the secret resource to store the SSL certificate in. + type: string + status: + description: |- + Status is the current state of the Certificate. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + properties: + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + http01Challenges: + description: |- + HTTP01Challenges is a list of HTTP01 challenges that need to be fulfilled + in order to get the TLS certificate.. + type: array + items: + description: |- + HTTP01Challenge defines the status of a HTTP01 challenge that a certificate needs + to fulfill. + type: object + properties: + serviceName: + description: ServiceName is the name of the service to serve HTTP01 challenge requests. + type: string + serviceNamespace: + description: ServiceNamespace is the namespace of the service to serve HTTP01 challenge requests. + type: string + servicePort: + description: ServicePort is the port of the service to serve HTTP01 challenge requests. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + url: + description: URL is the URL that the HTTP01 challenge is expected to serve on. + type: string + notAfter: + description: |- + The expiration time of the TLS certificate stored in the secret named + by this resource in spec.secretName. + type: string + format: date-time + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + additionalPrinterColumns: + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type==\"Ready\")].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type==\"Ready\")].reason" + names: + kind: Certificate + plural: certificates + singular: certificate + categories: + - knative-internal + - networking + shortNames: + - kcert + scope: Namespaced + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +# Note: The schema part of the spec is auto-generated by hack/update-schemas.sh. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: configurations.serving.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" + duck.knative.dev/podspecable: "true" +spec: + group: serving.knative.dev + names: + kind: Configuration + plural: configurations + singular: configuration + categories: + - all + - knative + - serving + shortNames: + - config + - cfg + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - name: LatestCreated + type: string + jsonPath: .status.latestCreatedRevisionName + - name: LatestReady + type: string + jsonPath: .status.latestReadyRevisionName + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + schema: + openAPIV3Schema: + description: |- + Configuration represents the "floating HEAD" of a linear history of Revisions. + Users create new Revisions by updating the Configuration's spec. + The "latest created" revision's name is available under status, as is the + "latest ready" revision's name. + See also: https://github.com/knative/serving/blob/main/docs/spec/overview.md#configuration + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: ConfigurationSpec holds the desired state of the Configuration (from the client). + type: object + properties: + template: + description: Template holds the latest specification for the Revision to be stamped out. + type: object + properties: + metadata: + type: object + properties: + annotations: + type: object + additionalProperties: + type: string + finalizers: + type: array + items: + type: string + labels: + type: object + additionalProperties: + type: string + name: + type: string + namespace: + type: string + x-kubernetes-preserve-unknown-fields: true + spec: + description: RevisionSpec holds the desired state of the Revision (from the client). + type: object + required: + - containers + properties: + affinity: + description: This is accessible behind a feature flag - kubernetes.podspec-affinity + type: object + x-kubernetes-preserve-unknown-fields: true + automountServiceAccountToken: + description: AutomountServiceAccountToken indicates whether a service account token should be automatically mounted. + type: boolean + containerConcurrency: + description: |- + ContainerConcurrency specifies the maximum allowed in-flight (concurrent) + requests per container of the Revision. Defaults to `0` which means + concurrency to the application is not limited, and the system decides the + target concurrency for the autoscaler. + type: integer + format: int64 + containers: + description: |- + List of containers belonging to the pod. + Containers cannot currently be added or removed. + There must be at least one container in a Pod. + Cannot be updated. + type: array + items: + description: A single application container that you want to run within a pod. + type: object + properties: + args: + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + type: array + items: + type: string + x-kubernetes-list-type: atomic + command: + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + type: array + items: + type: string + x-kubernetes-list-type: atomic + env: + description: |- + List of environment variables to set in the container. + Cannot be updated. + type: array + items: + description: EnvVar represents an environment variable present in a Container. + type: object + required: + - name + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. Cannot be used if value is not empty. + type: object + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + type: object + required: + - key + properties: + key: + description: The key to select. + type: string + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the ConfigMap or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + fieldRef: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-fieldref + type: object + x-kubernetes-map-type: atomic + x-kubernetes-preserve-unknown-fields: true + resourceFieldRef: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-fieldref + type: object + x-kubernetes-map-type: atomic + x-kubernetes-preserve-unknown-fields: true + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + type: object + required: + - key + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. + type: array + items: + description: EnvFromSource represents the source of a set of ConfigMaps or Secrets + type: object + properties: + configMapRef: + description: The ConfigMap to select from + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + x-kubernetes-map-type: atomic + prefix: + description: Optional text to prepend to the name of each environment variable. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the Secret must be defined + type: boolean + x-kubernetes-map-type: atomic + x-kubernetes-list-type: atomic + image: + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + imagePullPolicy: + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + type: string + livenessProbe: + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + name: + description: |- + Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + type: array + items: + description: ContainerPort represents a network port in a single container. + type: object + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + type: integer + format: int32 + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + default: TCP + readinessProbe: + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + resources: + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + properties: + limits: + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + requests: + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + securityContext: + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + type: object + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + type: object + properties: + add: + description: This is accessible behind a feature flag - kubernetes.containerspec-addcapabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + x-kubernetes-list-type: atomic + privileged: + description: |- + Run container in privileged mode. This can only be set to explicitly to 'false' + type: boolean + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + type: object + required: + - type + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + startupProbe: + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + terminationMessagePath: + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. + type: string + terminationMessagePolicy: + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. + type: string + volumeMounts: + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. + type: array + items: + description: VolumeMount describes a mounting of a Volume within a container. + type: object + required: + - mountPath + - name + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-mount-propagation + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. + type: string + dnsConfig: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-dnsconfig + type: object + x-kubernetes-preserve-unknown-fields: true + dnsPolicy: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-dnspolicy + type: string + enableServiceLinks: + description: |- + EnableServiceLinks indicates whether information aboutservices should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Knative defaults this to false. + type: boolean + hostAliases: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostaliases + type: array + items: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostaliases + type: object + x-kubernetes-preserve-unknown-fields: true + hostIPC: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostipc + type: boolean + hostNetwork: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostnetwork + type: boolean + hostPID: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostpid + type: boolean + idleTimeoutSeconds: + description: |- + IdleTimeoutSeconds is the maximum duration in seconds a request will be allowed + to stay open while not receiving any bytes from the user's application. If + unspecified, a system default will be provided. + type: integer + format: int64 + imagePullSecrets: + description: |- + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. + If specified, these secrets will be passed to individual puller implementations for them to use. + More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod + type: array + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + x-kubernetes-map-type: atomic + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-init-containers + type: array + items: + description: This is accessible behind a feature flag - kubernetes.podspec-init-containers + type: object + x-kubernetes-preserve-unknown-fields: true + nodeSelector: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-nodeselector + type: object + additionalProperties: + type: string + x-kubernetes-map-type: atomic + priorityClassName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-priorityclassname + type: string + responseStartTimeoutSeconds: + description: |- + ResponseStartTimeoutSeconds is the maximum duration in seconds that the request + routing layer will wait for a request delivered to a container to begin + sending any network traffic. + type: integer + format: int64 + runtimeClassName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-runtimeclassname + type: string + schedulerName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-schedulername + type: string + securityContext: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-securitycontext + type: object + x-kubernetes-preserve-unknown-fields: true + serviceAccountName: + description: |- + ServiceAccountName is the name of the ServiceAccount to use to run this pod. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ + type: string + shareProcessNamespace: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-shareprocessnamespace + type: boolean + timeoutSeconds: + description: |- + TimeoutSeconds is the maximum duration in seconds that the request instance + is allowed to respond to a request. If unspecified, a system default will + be provided. + type: integer + format: int64 + tolerations: + description: This is accessible behind a feature flag - kubernetes.podspec-tolerations + type: array + items: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-tolerations + type: object + x-kubernetes-preserve-unknown-fields: true + topologySpreadConstraints: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-topologyspreadconstraints + type: array + items: + description: This is accessible behind a feature flag - kubernetes.podspec-topologyspreadconstraints + type: object + x-kubernetes-preserve-unknown-fields: true + volumes: + description: |- + List of volumes that can be mounted by containers belonging to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes + type: array + items: + description: Volume represents a named volume in a pod that may be accessed by any container in the pod. + type: object + required: + - name + properties: + configMap: + description: configMap represents a configMap that should populate this volume + type: object + properties: + defaultMode: + description: |- + defaultMode is optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + x-kubernetes-map-type: atomic + csi: + description: This is accessible behind a feature flag - kubernetes.podspec-volumes-csi + type: object + x-kubernetes-preserve-unknown-fields: true + emptyDir: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-emptydir + type: object + x-kubernetes-preserve-unknown-fields: true + hostPath: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-hostpath + type: object + x-kubernetes-preserve-unknown-fields: true + image: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-image + type: object + x-kubernetes-preserve-unknown-fields: true + name: + description: |- + name of the volume. + Must be a DNS_LABEL and unique within the pod. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + persistentVolumeClaim: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-persistent-volume-claim + type: object + x-kubernetes-preserve-unknown-fields: true + projected: + description: projected items for all in one resources secrets, configmaps, and downward API + type: object + properties: + defaultMode: + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + sources: + description: |- + sources is the list of volume projections. Each entry in this list + handles one source. + type: array + items: + description: |- + Projection that may be projected along with other supported volume types. + Exactly one of these fields must be set. + type: object + properties: + configMap: + description: configMap information about the configMap data to project + type: object + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the downwardAPI data to project + type: object + properties: + items: + description: Items is a list of DownwardAPIVolume file + type: array + items: + description: DownwardAPIVolumeFile represents information to create the file containing the pod field + type: object + required: + - path + properties: + fieldRef: + description: 'Required: Selects a field of the pod: only annotations, labels, name, namespace and uid are supported.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: 'Required: Path is the relative path name of the file to be created. Must not be absolute or contain the ''..'' path. Must be utf-8 encoded. The first item of the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + type: object + required: + - resource + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + x-kubernetes-map-type: atomic + x-kubernetes-list-type: atomic + secret: + description: secret information about the secret data to project + type: object + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional field specify whether the Secret or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information about the serviceAccountToken data to project + type: object + required: + - path + properties: + audience: + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. + type: integer + format: int64 + path: + description: |- + path is the path relative to the mount point of the file to project the + token into. + type: string + x-kubernetes-list-type: atomic + secret: + description: |- + secret represents a secret that should populate this volume. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: object + properties: + defaultMode: + description: |- + defaultMode is Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values + for mode bits. Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + items: + description: |- + items If unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + optional: + description: optional field specify whether the Secret or its keys must be defined + type: boolean + secretName: + description: |- + secretName is the name of the secret in the pod's namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: string + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + status: + description: ConfigurationStatus communicates the observed state of the Configuration (from the controller). + type: object + properties: + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + latestCreatedRevisionName: + description: |- + LatestCreatedRevisionName is the last revision that was created from this + Configuration. It might not be ready yet, for that use LatestReadyRevisionName. + type: string + latestReadyRevisionName: + description: |- + LatestReadyRevisionName holds the name of the latest Revision stamped out + from this Configuration that has had its "Ready" condition become "True". + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + +--- +# Copyright 2020 The Knative 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 +# +# https://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. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: clusterdomainclaims.networking.internal.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: networking + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: networking.internal.knative.dev + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + description: ClusterDomainClaim is a cluster-wide reservation for a particular domain name. + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Spec is the desired state of the ClusterDomainClaim. + More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + required: + - namespace + properties: + namespace: + description: |- + Namespace is the namespace which is allowed to create a DomainMapping + using this ClusterDomainClaim's name. + type: string + names: + kind: ClusterDomainClaim + plural: clusterdomainclaims + singular: clusterdomainclaim + categories: + - knative-internal + - networking + shortNames: + - cdc + scope: Cluster + +--- +# Copyright 2020 The Knative 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 +# +# https://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. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: domainmappings.serving.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: serving.knative.dev + versions: + - name: v1beta1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - name: URL + type: string + jsonPath: .status.url + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + "schema": + "openAPIV3Schema": + description: DomainMapping is a mapping from a custom hostname to an Addressable. + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Spec is the desired state of the DomainMapping. + More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + required: + - ref + properties: + ref: + description: |- + Ref specifies the target of the Domain Mapping. + + The object identified by the Ref must be an Addressable with a URL of the + form `{name}.{namespace}.{domain}` where `{domain}` is the cluster domain, + and `{name}` and `{namespace}` are the name and namespace of a Kubernetes + Service. + + This contract is satisfied by Knative types such as Knative Services and + Knative Routes, and by Kubernetes Services. + type: object + required: + - kind + - name + properties: + address: + description: Address points to a specific Address Name. + type: string + apiVersion: + description: API version of the referent. + type: string + group: + description: |- + Group of the API, without the version of the group. This can be used as an alternative to the APIVersion, and then resolved using ResolveGroup. + Note: This API is EXPERIMENTAL and might break anytime. For more details: https://github.com/knative/eventing/issues/5086 + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + This is optional field, it gets defaulted to the object holding it if left out. + type: string + tls: + description: TLS allows the DomainMapping to terminate TLS traffic with an existing secret. + type: object + required: + - secretName + properties: + secretName: + description: SecretName is the name of the existing secret used to terminate TLS traffic. + type: string + status: + description: |- + Status is the current state of the DomainMapping. + More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + properties: + address: + description: Address holds the information needed for a DomainMapping to be the target of an event. + type: object + properties: + CACerts: + description: |- + CACerts is the Certification Authority (CA) certificates in PEM format + according to https://www.rfc-editor.org/rfc/rfc7468. + type: string + audience: + description: Audience is the OIDC audience for this address. + type: string + name: + description: Name is the name of the address. + type: string + url: + type: string + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + url: + description: URL is the URL of this DomainMapping. + type: string + names: + kind: DomainMapping + plural: domainmappings + singular: domainmapping + categories: + - all + - knative + - serving + shortNames: + - dm + scope: Namespaced + +--- +# Copyright 2020 The Knative 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 +# +# https://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. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: ingresses.networking.internal.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: networking + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: networking.internal.knative.dev + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + description: |- + Ingress is a collection of rules that allow inbound connections to reach the endpoints defined + by a backend. An Ingress can be configured to give services externally-reachable URLs, load + balance traffic, offer name based virtual hosting, etc. + + This is heavily based on K8s Ingress https://godoc.org/k8s.io/api/networking/v1beta1#Ingress + which some highlighted modifications. + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Spec is the desired state of the Ingress. + More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + properties: + httpOption: + description: |- + HTTPOption is the option of HTTP. It has the following two values: + `HTTPOptionEnabled`, `HTTPOptionRedirected` + type: string + rules: + description: A list of host rules used to configure the Ingress. + type: array + items: + description: |- + IngressRule represents the rules mapping the paths under a specified host to + the related backend services. Incoming requests are first evaluated for a host + match, then routed to the backend associated with the matching IngressRuleValue. + type: object + properties: + hosts: + description: |- + Host is the fully qualified domain name of a network host, as defined + by RFC 3986. Note the following deviations from the "host" part of the + URI as defined in the RFC: + 1. IPs are not allowed. Currently a rule value can only apply to the + IP in the Spec of the parent . + 2. The `:` delimiter is not respected because ports are not allowed. + Currently the port of an Ingress is implicitly :80 for http and + :443 for https. + Both these may change in the future. + If the host is unspecified, the Ingress routes all traffic based on the + specified IngressRuleValue. + If multiple matching Hosts were provided, the first rule will take precedent. + type: array + items: + type: string + http: + description: |- + HTTP represents a rule to apply against incoming requests. If the + rule is satisfied, the request is routed to the specified backend. + type: object + required: + - paths + properties: + paths: + description: |- + A collection of paths that map requests to backends. + + If they are multiple matching paths, the first match takes precedence. + type: array + items: + description: |- + HTTPIngressPath associates a path regex with a backend. Incoming URLs matching + the path are forwarded to the backend. + type: object + required: + - splits + properties: + appendHeaders: + description: |- + AppendHeaders allow specifying additional HTTP headers to add + before forwarding a request to the destination service. + + NOTE: This differs from K8s Ingress which doesn't allow header appending. + type: object + additionalProperties: + type: string + headers: + description: |- + Headers defines header matching rules which is a map from a header name + to HeaderMatch which specify a matching condition. + When a request matched with all the header matching rules, + the request is routed by the corresponding ingress rule. + If it is empty, the headers are not used for matching + type: object + additionalProperties: + description: |- + HeaderMatch represents a matching value of Headers in HTTPIngressPath. + Currently, only the exact matching is supported. + type: object + required: + - exact + properties: + exact: + type: string + path: + description: |- + Path represents a literal prefix to which this rule should apply. + Currently it can contain characters disallowed from the conventional + "path" part of a URL as defined by RFC 3986. Paths must begin with + a '/'. If unspecified, the path defaults to a catch all sending + traffic to the backend. + type: string + rewriteHost: + description: |- + RewriteHost rewrites the incoming request's host header. + + This field is currently experimental and not supported by all Ingress + implementations. + type: string + splits: + description: |- + Splits defines the referenced service endpoints to which the traffic + will be forwarded to. + type: array + items: + description: IngressBackendSplit describes all endpoints for a given service and port. + type: object + required: + - serviceName + - serviceNamespace + - servicePort + properties: + appendHeaders: + description: |- + AppendHeaders allow specifying additional HTTP headers to add + before forwarding a request to the destination service. + + NOTE: This differs from K8s Ingress which doesn't allow header appending. + type: object + additionalProperties: + type: string + percent: + description: |- + Specifies the split percentage, a number between 0 and 100. If + only one split is specified, we default to 100. + + NOTE: This differs from K8s Ingress to allow percentage split. + type: integer + serviceName: + description: Specifies the name of the referenced service. + type: string + serviceNamespace: + description: |- + Specifies the namespace of the referenced service. + + NOTE: This differs from K8s Ingress to allow routing to different namespaces. + type: string + servicePort: + description: Specifies the port of the referenced service. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + visibility: + description: |- + Visibility signifies whether this rule should `ClusterLocal`. If it's not + specified then it defaults to `ExternalIP`. + type: string + tls: + description: |- + TLS configuration. Currently Ingress only supports a single TLS + port: 443. If multiple members of this list specify different hosts, they + will be multiplexed on the same port according to the hostname specified + through the SNI TLS extension, if the ingress controller fulfilling the + ingress supports SNI. + type: array + items: + description: IngressTLS describes the transport layer security associated with an Ingress. + type: object + properties: + hosts: + description: |- + Hosts is a list of hosts included in the TLS certificate. The values in + this list must match the name/s used in the tlsSecret. Defaults to the + wildcard host setting for the loadbalancer controller fulfilling this + Ingress, if left unspecified. + type: array + items: + type: string + secretName: + description: SecretName is the name of the secret used to terminate SSL traffic. + type: string + secretNamespace: + description: |- + SecretNamespace is the namespace of the secret used to terminate SSL traffic. + If not set the namespace should be assumed to be the same as the Ingress. + If set the secret should have the same namespace as the Ingress otherwise + the behaviour is undefined and not supported. + type: string + status: + description: |- + Status is the current state of the Ingress. + More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + properties: + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + privateLoadBalancer: + description: PrivateLoadBalancer contains the current status of the load-balancer. + type: object + properties: + ingress: + description: |- + Ingress is a list containing ingress points for the load-balancer. + Traffic intended for the service should be sent to these ingress points. + type: array + items: + description: |- + LoadBalancerIngressStatus represents the status of a load-balancer ingress point: + traffic intended for the service should be sent to an ingress point. + type: object + properties: + domain: + description: |- + Domain is set for load-balancer ingress points that are DNS based + (typically AWS load-balancers) + type: string + domainInternal: + description: |- + DomainInternal is set if there is a cluster-local DNS name to access the Ingress. + + NOTE: This differs from K8s Ingress, since we also desire to have a cluster-local + DNS name to allow routing in case of not having a mesh. + type: string + ip: + description: |- + IP is set for load-balancer ingress points that are IP based + (typically GCE or OpenStack load-balancers) + type: string + meshOnly: + description: MeshOnly is set if the Ingress is only load-balanced through a Service mesh. + type: boolean + publicLoadBalancer: + description: PublicLoadBalancer contains the current status of the load-balancer. + type: object + properties: + ingress: + description: |- + Ingress is a list containing ingress points for the load-balancer. + Traffic intended for the service should be sent to these ingress points. + type: array + items: + description: |- + LoadBalancerIngressStatus represents the status of a load-balancer ingress point: + traffic intended for the service should be sent to an ingress point. + type: object + properties: + domain: + description: |- + Domain is set for load-balancer ingress points that are DNS based + (typically AWS load-balancers) + type: string + domainInternal: + description: |- + DomainInternal is set if there is a cluster-local DNS name to access the Ingress. + + NOTE: This differs from K8s Ingress, since we also desire to have a cluster-local + DNS name to allow routing in case of not having a mesh. + type: string + ip: + description: |- + IP is set for load-balancer ingress points that are IP based + (typically GCE or OpenStack load-balancers) + type: string + meshOnly: + description: MeshOnly is set if the Ingress is only load-balanced through a Service mesh. + type: boolean + additionalPrinterColumns: + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + names: + kind: Ingress + plural: ingresses + singular: ingress + categories: + - knative-internal + - networking + shortNames: + - kingress + - king + scope: Namespaced + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +# Note: The schema part of the spec is auto-generated by hack/update-schemas.sh. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: metrics.autoscaling.internal.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: autoscaling.internal.knative.dev + names: + kind: Metric + plural: metrics + singular: metric + categories: + - knative-internal + - autoscaling + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + schema: + openAPIV3Schema: + description: Metric represents a resource to configure the metric collector with. + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec holds the desired state of the Metric (from the client). + type: object + required: + - panicWindow + - scrapeTarget + - stableWindow + properties: + panicWindow: + description: PanicWindow is the aggregation window for metrics where quick reactions are needed. + type: integer + format: int64 + scrapeTarget: + description: ScrapeTarget is the K8s service that publishes the metric endpoint. + type: string + stableWindow: + description: StableWindow is the aggregation window for metrics in a stable state. + type: integer + format: int64 + status: + description: Status communicates the observed state of the Metric (from the controller). + type: object + properties: + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + +--- +# Copyright 2018 The Knative 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 +# +# https://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. + +# Note: The schema part of the spec is auto-generated by hack/update-schemas.sh. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: podautoscalers.autoscaling.internal.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: autoscaling.internal.knative.dev + names: + kind: PodAutoscaler + plural: podautoscalers + singular: podautoscaler + categories: + - knative-internal + - autoscaling + shortNames: + - kpa + - pa + scope: Namespaced + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - name: DesiredScale + type: integer + jsonPath: ".status.desiredScale" + - name: ActualScale + type: integer + jsonPath: ".status.actualScale" + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + schema: + openAPIV3Schema: + description: |- + PodAutoscaler is a Knative abstraction that encapsulates the interface by which Knative + components instantiate autoscalers. This definition is an abstraction that may be backed + by multiple definitions. For more information, see the Knative Pluggability presentation: + https://docs.google.com/presentation/d/19vW9HFZ6Puxt31biNZF3uLRejDmu82rxJIk1cWmxF7w/edit + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec holds the desired state of the PodAutoscaler (from the client). + type: object + required: + - protocolType + - scaleTargetRef + properties: + containerConcurrency: + description: |- + ContainerConcurrency specifies the maximum allowed + in-flight (concurrent) requests per container of the Revision. + Defaults to `0` which means unlimited concurrency. + type: integer + format: int64 + protocolType: + description: The application-layer protocol. Matches `ProtocolType` inferred from the revision spec. + type: string + reachability: + description: |- + Reachability specifies whether or not the `ScaleTargetRef` can be reached (ie. has a route). + Defaults to `ReachabilityUnknown` + type: string + scaleTargetRef: + description: |- + ScaleTargetRef defines the /scale-able resource that this PodAutoscaler + is responsible for quickly right-sizing. + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + x-kubernetes-map-type: atomic + status: + description: Status communicates the observed state of the PodAutoscaler (from the controller). + type: object + required: + - metricsServiceName + - serviceName + properties: + actualScale: + description: ActualScale shows the actual number of replicas for the revision. + type: integer + format: int32 + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + desiredScale: + description: DesiredScale shows the current desired number of replicas for the revision. + type: integer + format: int32 + metricsServiceName: + description: |- + MetricsServiceName is the K8s Service name that provides revision metrics. + The service is managed by the PA object. + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + serviceName: + description: |- + ServiceName is the K8s Service name that serves the revision, scaled by this PA. + The service is created and owned by the ServerlessService object owned by this PA. + type: string + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +# Note: The schema part of the spec is auto-generated by hack/update-schemas.sh. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: revisions.serving.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: serving.knative.dev + names: + kind: Revision + plural: revisions + singular: revision + categories: + - all + - knative + - serving + shortNames: + - rev + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - name: Config Name + type: string + jsonPath: ".metadata.labels['serving\\.knative\\.dev/configuration']" + - name: Generation + type: string # int in string form :( + jsonPath: ".metadata.labels['serving\\.knative\\.dev/configurationGeneration']" + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + - name: Actual Replicas + type: integer + jsonPath: ".status.actualReplicas" + - name: Desired Replicas + type: integer + jsonPath: ".status.desiredReplicas" + schema: + openAPIV3Schema: + description: |- + Revision is an immutable snapshot of code and configuration. A revision + references a container image. Revisions are created by updates to a + Configuration. + + See also: https://github.com/knative/serving/blob/main/docs/spec/overview.md#revision + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: RevisionSpec holds the desired state of the Revision (from the client). + type: object + required: + - containers + properties: + affinity: + description: This is accessible behind a feature flag - kubernetes.podspec-affinity + type: object + x-kubernetes-preserve-unknown-fields: true + automountServiceAccountToken: + description: AutomountServiceAccountToken indicates whether a service account token should be automatically mounted. + type: boolean + containerConcurrency: + description: |- + ContainerConcurrency specifies the maximum allowed in-flight (concurrent) + requests per container of the Revision. Defaults to `0` which means + concurrency to the application is not limited, and the system decides the + target concurrency for the autoscaler. + type: integer + format: int64 + containers: + description: |- + List of containers belonging to the pod. + Containers cannot currently be added or removed. + There must be at least one container in a Pod. + Cannot be updated. + type: array + items: + description: A single application container that you want to run within a pod. + type: object + properties: + args: + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + type: array + items: + type: string + x-kubernetes-list-type: atomic + command: + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + type: array + items: + type: string + x-kubernetes-list-type: atomic + env: + description: |- + List of environment variables to set in the container. + Cannot be updated. + type: array + items: + description: EnvVar represents an environment variable present in a Container. + type: object + required: + - name + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. Cannot be used if value is not empty. + type: object + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + type: object + required: + - key + properties: + key: + description: The key to select. + type: string + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the ConfigMap or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + fieldRef: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-fieldref + type: object + x-kubernetes-map-type: atomic + x-kubernetes-preserve-unknown-fields: true + resourceFieldRef: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-fieldref + type: object + x-kubernetes-map-type: atomic + x-kubernetes-preserve-unknown-fields: true + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + type: object + required: + - key + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. + type: array + items: + description: EnvFromSource represents the source of a set of ConfigMaps or Secrets + type: object + properties: + configMapRef: + description: The ConfigMap to select from + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + x-kubernetes-map-type: atomic + prefix: + description: Optional text to prepend to the name of each environment variable. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the Secret must be defined + type: boolean + x-kubernetes-map-type: atomic + x-kubernetes-list-type: atomic + image: + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + imagePullPolicy: + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + type: string + livenessProbe: + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + name: + description: |- + Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + type: array + items: + description: ContainerPort represents a network port in a single container. + type: object + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + type: integer + format: int32 + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + default: TCP + readinessProbe: + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + resources: + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + properties: + limits: + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + requests: + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + securityContext: + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + type: object + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + type: object + properties: + add: + description: This is accessible behind a feature flag - kubernetes.containerspec-addcapabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + x-kubernetes-list-type: atomic + privileged: + description: |- + Run container in privileged mode. This can only be set to explicitly to 'false' + type: boolean + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + type: object + required: + - type + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + startupProbe: + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + terminationMessagePath: + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. + type: string + terminationMessagePolicy: + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. + type: string + volumeMounts: + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. + type: array + items: + description: VolumeMount describes a mounting of a Volume within a container. + type: object + required: + - mountPath + - name + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-mount-propagation + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. + type: string + dnsConfig: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-dnsconfig + type: object + x-kubernetes-preserve-unknown-fields: true + dnsPolicy: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-dnspolicy + type: string + enableServiceLinks: + description: |- + EnableServiceLinks indicates whether information aboutservices should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Knative defaults this to false. + type: boolean + hostAliases: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostaliases + type: array + items: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostaliases + type: object + x-kubernetes-preserve-unknown-fields: true + hostIPC: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostipc + type: boolean + hostNetwork: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostnetwork + type: boolean + hostPID: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostpid + type: boolean + idleTimeoutSeconds: + description: |- + IdleTimeoutSeconds is the maximum duration in seconds a request will be allowed + to stay open while not receiving any bytes from the user's application. If + unspecified, a system default will be provided. + type: integer + format: int64 + imagePullSecrets: + description: |- + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. + If specified, these secrets will be passed to individual puller implementations for them to use. + More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod + type: array + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + x-kubernetes-map-type: atomic + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-init-containers + type: array + items: + description: This is accessible behind a feature flag - kubernetes.podspec-init-containers + type: object + x-kubernetes-preserve-unknown-fields: true + nodeSelector: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-nodeselector + type: object + additionalProperties: + type: string + x-kubernetes-map-type: atomic + priorityClassName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-priorityclassname + type: string + responseStartTimeoutSeconds: + description: |- + ResponseStartTimeoutSeconds is the maximum duration in seconds that the request + routing layer will wait for a request delivered to a container to begin + sending any network traffic. + type: integer + format: int64 + runtimeClassName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-runtimeclassname + type: string + schedulerName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-schedulername + type: string + securityContext: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-securitycontext + type: object + x-kubernetes-preserve-unknown-fields: true + serviceAccountName: + description: |- + ServiceAccountName is the name of the ServiceAccount to use to run this pod. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ + type: string + shareProcessNamespace: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-shareprocessnamespace + type: boolean + timeoutSeconds: + description: |- + TimeoutSeconds is the maximum duration in seconds that the request instance + is allowed to respond to a request. If unspecified, a system default will + be provided. + type: integer + format: int64 + tolerations: + description: This is accessible behind a feature flag - kubernetes.podspec-tolerations + type: array + items: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-tolerations + type: object + x-kubernetes-preserve-unknown-fields: true + topologySpreadConstraints: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-topologyspreadconstraints + type: array + items: + description: This is accessible behind a feature flag - kubernetes.podspec-topologyspreadconstraints + type: object + x-kubernetes-preserve-unknown-fields: true + volumes: + description: |- + List of volumes that can be mounted by containers belonging to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes + type: array + items: + description: Volume represents a named volume in a pod that may be accessed by any container in the pod. + type: object + required: + - name + properties: + configMap: + description: configMap represents a configMap that should populate this volume + type: object + properties: + defaultMode: + description: |- + defaultMode is optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + x-kubernetes-map-type: atomic + csi: + description: This is accessible behind a feature flag - kubernetes.podspec-volumes-csi + type: object + x-kubernetes-preserve-unknown-fields: true + emptyDir: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-emptydir + type: object + x-kubernetes-preserve-unknown-fields: true + hostPath: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-hostpath + type: object + x-kubernetes-preserve-unknown-fields: true + image: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-image + type: object + x-kubernetes-preserve-unknown-fields: true + name: + description: |- + name of the volume. + Must be a DNS_LABEL and unique within the pod. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + persistentVolumeClaim: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-persistent-volume-claim + type: object + x-kubernetes-preserve-unknown-fields: true + projected: + description: projected items for all in one resources secrets, configmaps, and downward API + type: object + properties: + defaultMode: + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + sources: + description: |- + sources is the list of volume projections. Each entry in this list + handles one source. + type: array + items: + description: |- + Projection that may be projected along with other supported volume types. + Exactly one of these fields must be set. + type: object + properties: + configMap: + description: configMap information about the configMap data to project + type: object + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the downwardAPI data to project + type: object + properties: + items: + description: Items is a list of DownwardAPIVolume file + type: array + items: + description: DownwardAPIVolumeFile represents information to create the file containing the pod field + type: object + required: + - path + properties: + fieldRef: + description: 'Required: Selects a field of the pod: only annotations, labels, name, namespace and uid are supported.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: 'Required: Path is the relative path name of the file to be created. Must not be absolute or contain the ''..'' path. Must be utf-8 encoded. The first item of the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + type: object + required: + - resource + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + x-kubernetes-map-type: atomic + x-kubernetes-list-type: atomic + secret: + description: secret information about the secret data to project + type: object + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional field specify whether the Secret or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information about the serviceAccountToken data to project + type: object + required: + - path + properties: + audience: + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. + type: integer + format: int64 + path: + description: |- + path is the path relative to the mount point of the file to project the + token into. + type: string + x-kubernetes-list-type: atomic + secret: + description: |- + secret represents a secret that should populate this volume. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: object + properties: + defaultMode: + description: |- + defaultMode is Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values + for mode bits. Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + items: + description: |- + items If unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + optional: + description: optional field specify whether the Secret or its keys must be defined + type: boolean + secretName: + description: |- + secretName is the name of the secret in the pod's namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: string + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + status: + description: RevisionStatus communicates the observed state of the Revision (from the controller). + type: object + properties: + actualReplicas: + description: ActualReplicas reflects the amount of ready pods running this revision. + type: integer + format: int32 + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + containerStatuses: + description: |- + ContainerStatuses is a slice of images present in .Spec.Container[*].Image + to their respective digests and their container name. + The digests are resolved during the creation of Revision. + ContainerStatuses holds the container name and image digests + for both serving and non serving containers. + ref: http://bit.ly/image-digests + type: array + items: + description: ContainerStatus holds the information of container name and image digest value + type: object + properties: + imageDigest: + type: string + name: + type: string + desiredReplicas: + description: DesiredReplicas reflects the desired amount of pods running this revision. + type: integer + format: int32 + initContainerStatuses: + description: |- + InitContainerStatuses is a slice of images present in .Spec.InitContainer[*].Image + to their respective digests and their container name. + The digests are resolved during the creation of Revision. + ContainerStatuses holds the container name and image digests + for both serving and non serving containers. + ref: http://bit.ly/image-digests + type: array + items: + description: ContainerStatus holds the information of container name and image digest value + type: object + properties: + imageDigest: + type: string + name: + type: string + logUrl: + description: |- + LogURL specifies the generated logging url for this particular revision + based on the revision url template specified in the controller's config. + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +# Note: The schema part of the spec is auto-generated by hack/update-schemas.sh. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: routes.serving.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" + duck.knative.dev/addressable: "true" +spec: + group: serving.knative.dev + names: + kind: Route + plural: routes + singular: route + categories: + - all + - knative + - serving + shortNames: + - rt + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - name: URL + type: string + jsonPath: .status.url + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + schema: + openAPIV3Schema: + description: |- + Route is responsible for configuring ingress over a collection of Revisions. + Some of the Revisions a Route distributes traffic over may be specified by + referencing the Configuration responsible for creating them; in these cases + the Route is additionally responsible for monitoring the Configuration for + "latest ready revision" changes, and smoothly rolling out latest revisions. + See also: https://github.com/knative/serving/blob/main/docs/spec/overview.md#route + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec holds the desired state of the Route (from the client). + type: object + properties: + traffic: + description: |- + Traffic specifies how to distribute traffic over a collection of + revisions and configurations. + type: array + items: + description: TrafficTarget holds a single entry of the routing table for a Route. + type: object + properties: + configurationName: + description: |- + ConfigurationName of a configuration to whose latest revision we will send + this portion of traffic. When the "status.latestReadyRevisionName" of the + referenced configuration changes, we will automatically migrate traffic + from the prior "latest ready" revision to the new one. This field is never + set in Route's status, only its spec. This is mutually exclusive with + RevisionName. + type: string + latestRevision: + description: |- + LatestRevision may be optionally provided to indicate that the latest + ready Revision of the Configuration should be used for this traffic + target. When provided LatestRevision must be true if RevisionName is + empty; it must be false when RevisionName is non-empty. + type: boolean + percent: + description: |- + Percent indicates that percentage based routing should be used and + the value indicates the percent of traffic that is be routed to this + Revision or Configuration. `0` (zero) mean no traffic, `100` means all + traffic. + When percentage based routing is being used the follow rules apply: + - the sum of all percent values must equal 100 + - when not specified, the implied value for `percent` is zero for + that particular Revision or Configuration + type: integer + format: int64 + revisionName: + description: |- + RevisionName of a specific revision to which to send this portion of + traffic. This is mutually exclusive with ConfigurationName. + type: string + tag: + description: |- + Tag is optionally used to expose a dedicated url for referencing + this target exclusively. + type: string + url: + description: |- + URL displays the URL for accessing named traffic targets. URL is displayed in + status, and is disallowed on spec. URL must contain a scheme (e.g. http://) and + a hostname, but may not contain anything else (e.g. basic auth, url path, etc.) + type: string + status: + description: Status communicates the observed state of the Route (from the controller). + type: object + properties: + address: + description: Address holds the information needed for a Route to be the target of an event. + type: object + properties: + CACerts: + description: |- + CACerts is the Certification Authority (CA) certificates in PEM format + according to https://www.rfc-editor.org/rfc/rfc7468. + type: string + audience: + description: Audience is the OIDC audience for this address. + type: string + name: + description: Name is the name of the address. + type: string + url: + type: string + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + traffic: + description: |- + Traffic holds the configured traffic distribution. + These entries will always contain RevisionName references. + When ConfigurationName appears in the spec, this will hold the + LatestReadyRevisionName that we last observed. + type: array + items: + description: TrafficTarget holds a single entry of the routing table for a Route. + type: object + properties: + configurationName: + description: |- + ConfigurationName of a configuration to whose latest revision we will send + this portion of traffic. When the "status.latestReadyRevisionName" of the + referenced configuration changes, we will automatically migrate traffic + from the prior "latest ready" revision to the new one. This field is never + set in Route's status, only its spec. This is mutually exclusive with + RevisionName. + type: string + latestRevision: + description: |- + LatestRevision may be optionally provided to indicate that the latest + ready Revision of the Configuration should be used for this traffic + target. When provided LatestRevision must be true if RevisionName is + empty; it must be false when RevisionName is non-empty. + type: boolean + percent: + description: |- + Percent indicates that percentage based routing should be used and + the value indicates the percent of traffic that is be routed to this + Revision or Configuration. `0` (zero) mean no traffic, `100` means all + traffic. + When percentage based routing is being used the follow rules apply: + - the sum of all percent values must equal 100 + - when not specified, the implied value for `percent` is zero for + that particular Revision or Configuration + type: integer + format: int64 + revisionName: + description: |- + RevisionName of a specific revision to which to send this portion of + traffic. This is mutually exclusive with ConfigurationName. + type: string + tag: + description: |- + Tag is optionally used to expose a dedicated url for referencing + this target exclusively. + type: string + url: + description: |- + URL displays the URL for accessing named traffic targets. URL is displayed in + status, and is disallowed on spec. URL must contain a scheme (e.g. http://) and + a hostname, but may not contain anything else (e.g. basic auth, url path, etc.) + type: string + url: + description: |- + URL holds the url that will distribute traffic over the provided traffic targets. + It generally has the form http[s]://{route-name}.{route-namespace}.{cluster-level-suffix} + type: string + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: serverlessservices.networking.internal.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: networking + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" +spec: + group: networking.internal.knative.dev + versions: + - name: v1alpha1 + served: true + storage: true + subresources: + status: {} + schema: + openAPIV3Schema: + description: |- + ServerlessService is a proxy for the K8s service objects containing the + endpoints for the revision, whether those are endpoints of the activator or + revision pods. + See: https://knative.page.link/naxz for details. + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Spec is the desired state of the ServerlessService. + More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + required: + - objectRef + - protocolType + properties: + mode: + description: Mode describes the mode of operation of the ServerlessService. + type: string + numActivators: + description: |- + NumActivators contains number of Activators that this revision should be + assigned. + O means — assign all. + type: integer + format: int32 + objectRef: + description: |- + ObjectRef defines the resource that this ServerlessService + is responsible for making "serverless". + type: object + properties: + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + x-kubernetes-map-type: atomic + protocolType: + description: |- + The application-layer protocol. Matches `RevisionProtocolType` set on the owning pa/revision. + serving imports networking, so just use string. + type: string + status: + description: |- + Status is the current state of the ServerlessService. + More info: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + type: object + properties: + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + privateServiceName: + description: |- + PrivateServiceName holds the name of a core K8s Service resource that + load balances over the user service pods backing this Revision. + type: string + serviceName: + description: |- + ServiceName holds the name of a core K8s Service resource that + load balances over the pods backing this Revision (activator or revision). + type: string + additionalPrinterColumns: + - name: Mode + type: string + jsonPath: ".spec.mode" + - name: Activators + type: integer + jsonPath: ".spec.numActivators" + - name: ServiceName + type: string + jsonPath: ".status.serviceName" + - name: PrivateServiceName + type: string + jsonPath: ".status.privateServiceName" + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + names: + kind: ServerlessService + plural: serverlessservices + singular: serverlessservice + categories: + - knative-internal + - networking + shortNames: + - sks + scope: Namespaced + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +# Note: The schema part of the spec is auto-generated by hack/update-schemas.sh. + +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: services.serving.knative.dev + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + knative.dev/crd-install: "true" + duck.knative.dev/addressable: "true" + duck.knative.dev/podspecable: "true" +spec: + group: serving.knative.dev + names: + kind: Service + plural: services + singular: service + categories: + - all + - knative + - serving + shortNames: + - kservice + - ksvc + scope: Namespaced + versions: + - name: v1 + served: true + storage: true + subresources: + status: {} + additionalPrinterColumns: + - name: URL + type: string + jsonPath: .status.url + - name: LatestCreated + type: string + jsonPath: .status.latestCreatedRevisionName + - name: LatestReady + type: string + jsonPath: .status.latestReadyRevisionName + - name: Ready + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].status" + - name: Reason + type: string + jsonPath: ".status.conditions[?(@.type=='Ready')].reason" + schema: + openAPIV3Schema: + description: |- + Service acts as a top-level container that manages a Route and Configuration + which implement a network service. Service exists to provide a singular + abstraction which can be access controlled, reasoned about, and which + encapsulates software lifecycle decisions such as rollout policy and + team resource ownership. Service acts only as an orchestrator of the + underlying Routes and Configurations (much as a kubernetes Deployment + orchestrates ReplicaSets), and its usage is optional but recommended. + + The Service's controller will track the statuses of its owned Configuration + and Route, reflecting their statuses and conditions as its own. + + See also: https://github.com/knative/serving/blob/main/docs/spec/overview.md#service + type: object + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + ServiceSpec represents the configuration for the Service object. + A Service's specification is the union of the specifications for a Route + and Configuration. The Service restricts what can be expressed in these + fields, e.g. the Route must reference the provided Configuration; + however, these limitations also enable friendlier defaulting, + e.g. Route never needs a Configuration name, and may be defaulted to + the appropriate "run latest" spec. + type: object + properties: + template: + description: Template holds the latest specification for the Revision to be stamped out. + type: object + properties: + metadata: + type: object + properties: + annotations: + type: object + additionalProperties: + type: string + finalizers: + type: array + items: + type: string + labels: + type: object + additionalProperties: + type: string + name: + type: string + namespace: + type: string + x-kubernetes-preserve-unknown-fields: true + spec: + description: RevisionSpec holds the desired state of the Revision (from the client). + type: object + required: + - containers + properties: + affinity: + description: This is accessible behind a feature flag - kubernetes.podspec-affinity + type: object + x-kubernetes-preserve-unknown-fields: true + automountServiceAccountToken: + description: AutomountServiceAccountToken indicates whether a service account token should be automatically mounted. + type: boolean + containerConcurrency: + description: |- + ContainerConcurrency specifies the maximum allowed in-flight (concurrent) + requests per container of the Revision. Defaults to `0` which means + concurrency to the application is not limited, and the system decides the + target concurrency for the autoscaler. + type: integer + format: int64 + containers: + description: |- + List of containers belonging to the pod. + Containers cannot currently be added or removed. + There must be at least one container in a Pod. + Cannot be updated. + type: array + items: + description: A single application container that you want to run within a pod. + type: object + properties: + args: + description: |- + Arguments to the entrypoint. + The container image's CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + type: array + items: + type: string + x-kubernetes-list-type: atomic + command: + description: |- + Entrypoint array. Not executed within a shell. + The container image's ENTRYPOINT is used if this is not provided. + Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + cannot be resolved, the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will + produce the string literal "$(VAR_NAME)". Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot be updated. + More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + type: array + items: + type: string + x-kubernetes-list-type: atomic + env: + description: |- + List of environment variables to set in the container. + Cannot be updated. + type: array + items: + description: EnvVar represents an environment variable present in a Container. + type: object + required: + - name + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. Cannot be used if value is not empty. + type: object + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + type: object + required: + - key + properties: + key: + description: The key to select. + type: string + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the ConfigMap or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + fieldRef: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-fieldref + type: object + x-kubernetes-map-type: atomic + x-kubernetes-preserve-unknown-fields: true + resourceFieldRef: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-fieldref + type: object + x-kubernetes-map-type: atomic + x-kubernetes-preserve-unknown-fields: true + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + type: object + required: + - key + properties: + key: + description: The key of the secret to select from. Must be a valid secret key. + type: string + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + envFrom: + description: |- + List of sources to populate environment variables in the container. + The keys defined within a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container is starting. When a key exists in multiple + sources, the value associated with the last source will take precedence. + Values defined by an Env with a duplicate key will take precedence. + Cannot be updated. + type: array + items: + description: EnvFromSource represents the source of a set of ConfigMaps or Secrets + type: object + properties: + configMapRef: + description: The ConfigMap to select from + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + x-kubernetes-map-type: atomic + prefix: + description: Optional text to prepend to the name of each environment variable. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: Specify whether the Secret must be defined + type: boolean + x-kubernetes-map-type: atomic + x-kubernetes-list-type: atomic + image: + description: |- + Container image name. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + imagePullPolicy: + description: |- + Image pull policy. + One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/containers/images#updating-images + type: string + livenessProbe: + description: |- + Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + name: + description: |- + Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: |- + List of ports to expose from the container. Not specifying a port here + DOES NOT prevent that port from being exposed. Any port which is + listening on the default "0.0.0.0" address inside a container will be + accessible from the network. + Modifying this array with strategic merge patch may corrupt the data. + For more information See https://github.com/kubernetes/kubernetes/issues/108255. + Cannot be updated. + type: array + items: + description: ContainerPort represents a network port in a single container. + type: object + properties: + containerPort: + description: |- + Number of port to expose on the pod's IP address. + This must be a valid port number, 0 < x < 65536. + type: integer + format: int32 + name: + description: |- + If specified, this must be an IANA_SVC_NAME and unique within the pod. Each + named port in a pod must have a unique name. Name for the port that can be + referred to by services. + type: string + protocol: + description: |- + Protocol for port. Must be UDP, TCP, or SCTP. + Defaults to "TCP". + type: string + default: TCP + readinessProbe: + description: |- + Periodic probe of container service readiness. + Container will be removed from service endpoints if the probe fails. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + resources: + description: |- + Compute Resources required by this container. + Cannot be updated. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + properties: + limits: + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + requests: + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + additionalProperties: + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + securityContext: + description: |- + SecurityContext defines the security options the container should be run with. + If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + type: object + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + type: object + properties: + add: + description: This is accessible behind a feature flag - kubernetes.containerspec-addcapabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + type: array + items: + description: Capability represent POSIX capabilities type + type: string + x-kubernetes-list-type: atomic + privileged: + description: |- + Run container in privileged mode. This can only be set to explicitly to 'false' + type: boolean + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + type: integer + format: int64 + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + type: object + required: + - type + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + startupProbe: + description: |- + StartupProbe indicates that the Pod has successfully initialized. + If specified, no other probes are executed until this completes successfully. + If this probe fails, the Pod will be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters at the beginning of a Pod's lifecycle, + when it might take a long time to load data or warm a cache, than during steady-state operation. + This cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: object + properties: + exec: + description: Exec specifies a command to execute in the container. + type: object + properties: + command: + description: |- + Command is the command line to execute inside the container, the working directory for the + command is root ('/') in the container's filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions ('|', etc) won't work. To use + a shell, you need to explicitly call out to that shell. + Exit status of 0 is treated as live/healthy and non-zero is unhealthy. + type: array + items: + type: string + x-kubernetes-list-type: atomic + failureThreshold: + description: |- + Minimum consecutive failures for the probe to be considered failed after having succeeded. + Defaults to 3. Minimum value is 1. + type: integer + format: int32 + grpc: + description: GRPC specifies a GRPC HealthCheckRequest. + type: object + properties: + port: + description: Port number of the gRPC service. Number must be in the range 1 to 65535. + type: integer + format: int32 + service: + description: |- + Service is the name of the service to place in the gRPC HealthCheckRequest + (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + + If this is not specified, the default behavior is defined by gRPC. + type: string + default: "" + httpGet: + description: HTTPGet specifies an HTTP GET request to perform. + type: object + properties: + host: + description: |- + Host name to connect to, defaults to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows repeated headers. + type: array + items: + description: HTTPHeader describes a custom header to be used in HTTP probes + type: object + required: + - name + - value + properties: + name: + description: |- + The header field name. + This will be canonicalized upon output, so case-variant names will be understood as the same header. + type: string + value: + description: The header field value + type: string + x-kubernetes-list-type: atomic + path: + description: Path to access on the HTTP server. + type: string + port: + description: |- + Name or number of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + scheme: + description: |- + Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + initialDelaySeconds: + description: |- + Number of seconds after the container has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + periodSeconds: + description: |- + How often (in seconds) to perform the probe. + type: integer + format: int32 + successThreshold: + description: |- + Minimum consecutive successes for the probe to be considered successful after having failed. + Defaults to 1. Must be 1 for liveness and startup. Minimum value is 1. + type: integer + format: int32 + tcpSocket: + description: TCPSocket specifies a connection to a TCP port. + type: object + properties: + host: + description: 'Optional: Host name to connect to, defaults to the pod IP.' + type: string + port: + description: |- + Number or name of the port to access on the container. + Number must be in the range 1 to 65535. + Name must be an IANA_SVC_NAME. + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + timeoutSeconds: + description: |- + Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes + type: integer + format: int32 + terminationMessagePath: + description: |- + Optional: Path at which the file to which the container's termination message + will be written is mounted into the container's filesystem. + Message written is intended to be brief final status, such as an assertion failure message. + Will be truncated by the node if greater than 4096 bytes. The total message length across + all containers will be limited to 12kb. + Defaults to /dev/termination-log. + Cannot be updated. + type: string + terminationMessagePolicy: + description: |- + Indicate how the termination message should be populated. File will use the contents of + terminationMessagePath to populate the container status message on both success and failure. + FallbackToLogsOnError will use the last chunk of container log output if the termination + message file is empty and the container exited with an error. + The log output is limited to 2048 bytes or 80 lines, whichever is smaller. + Defaults to File. + Cannot be updated. + type: string + volumeMounts: + description: |- + Pod volumes to mount into the container's filesystem. + Cannot be updated. + type: array + items: + description: VolumeMount describes a mounting of a Volume within a container. + type: object + required: + - mountPath + - name + properties: + mountPath: + description: |- + Path within the container at which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-mount-propagation + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: |- + Mounted read-only if true, read-write otherwise (false or unspecified). + Defaults to false. + type: boolean + subPath: + description: |- + Path within the volume from which the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + x-kubernetes-list-map-keys: + - mountPath + x-kubernetes-list-type: map + workingDir: + description: |- + Container's working directory. + If not specified, the container runtime's default will be used, which + might be configured in the container image. + Cannot be updated. + type: string + dnsConfig: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-dnsconfig + type: object + x-kubernetes-preserve-unknown-fields: true + dnsPolicy: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-dnspolicy + type: string + enableServiceLinks: + description: |- + EnableServiceLinks indicates whether information aboutservices should be injected into pod's environment variables, matching the syntax of Docker links. Optional: Knative defaults this to false. + type: boolean + hostAliases: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostaliases + type: array + items: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostaliases + type: object + x-kubernetes-preserve-unknown-fields: true + hostIPC: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostipc + type: boolean + hostNetwork: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostnetwork + type: boolean + hostPID: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-hostpid + type: boolean + idleTimeoutSeconds: + description: |- + IdleTimeoutSeconds is the maximum duration in seconds a request will be allowed + to stay open while not receiving any bytes from the user's application. If + unspecified, a system default will be provided. + type: integer + format: int64 + imagePullSecrets: + description: |- + ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. + If specified, these secrets will be passed to individual puller implementations for them to use. + More info: https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod + type: array + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + type: object + properties: + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + x-kubernetes-map-type: atomic + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + initContainers: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-init-containers + type: array + items: + description: This is accessible behind a feature flag - kubernetes.podspec-init-containers + type: object + x-kubernetes-preserve-unknown-fields: true + nodeSelector: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-nodeselector + type: object + additionalProperties: + type: string + x-kubernetes-map-type: atomic + priorityClassName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-priorityclassname + type: string + responseStartTimeoutSeconds: + description: |- + ResponseStartTimeoutSeconds is the maximum duration in seconds that the request + routing layer will wait for a request delivered to a container to begin + sending any network traffic. + type: integer + format: int64 + runtimeClassName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-runtimeclassname + type: string + schedulerName: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-schedulername + type: string + securityContext: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-securitycontext + type: object + x-kubernetes-preserve-unknown-fields: true + serviceAccountName: + description: |- + ServiceAccountName is the name of the ServiceAccount to use to run this pod. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ + type: string + shareProcessNamespace: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-shareprocessnamespace + type: boolean + timeoutSeconds: + description: |- + TimeoutSeconds is the maximum duration in seconds that the request instance + is allowed to respond to a request. If unspecified, a system default will + be provided. + type: integer + format: int64 + tolerations: + description: This is accessible behind a feature flag - kubernetes.podspec-tolerations + type: array + items: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-tolerations + type: object + x-kubernetes-preserve-unknown-fields: true + topologySpreadConstraints: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-topologyspreadconstraints + type: array + items: + description: This is accessible behind a feature flag - kubernetes.podspec-topologyspreadconstraints + type: object + x-kubernetes-preserve-unknown-fields: true + volumes: + description: |- + List of volumes that can be mounted by containers belonging to the pod. + More info: https://kubernetes.io/docs/concepts/storage/volumes + type: array + items: + description: Volume represents a named volume in a pod that may be accessed by any container in the pod. + type: object + required: + - name + properties: + configMap: + description: configMap represents a configMap that should populate this volume + type: object + properties: + defaultMode: + description: |- + defaultMode is optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + x-kubernetes-map-type: atomic + csi: + description: This is accessible behind a feature flag - kubernetes.podspec-volumes-csi + type: object + x-kubernetes-preserve-unknown-fields: true + emptyDir: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-emptydir + type: object + x-kubernetes-preserve-unknown-fields: true + hostPath: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-hostpath + type: object + x-kubernetes-preserve-unknown-fields: true + image: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-volumes-image + type: object + x-kubernetes-preserve-unknown-fields: true + name: + description: |- + name of the volume. + Must be a DNS_LABEL and unique within the pod. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + persistentVolumeClaim: + description: |- + This is accessible behind a feature flag - kubernetes.podspec-persistent-volume-claim + type: object + x-kubernetes-preserve-unknown-fields: true + projected: + description: projected items for all in one resources secrets, configmaps, and downward API + type: object + properties: + defaultMode: + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + sources: + description: |- + sources is the list of volume projections. Each entry in this list + handles one source. + type: array + items: + description: |- + Projection that may be projected along with other supported volume types. + Exactly one of these fields must be set. + type: object + properties: + configMap: + description: configMap information about the configMap data to project + type: object + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional specify whether the ConfigMap or its keys must be defined + type: boolean + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the downwardAPI data to project + type: object + properties: + items: + description: Items is a list of DownwardAPIVolume file + type: array + items: + description: DownwardAPIVolumeFile represents information to create the file containing the pod field + type: object + required: + - path + properties: + fieldRef: + description: 'Required: Selects a field of the pod: only annotations, labels, name, namespace and uid are supported.' + type: object + required: + - fieldPath + properties: + apiVersion: + description: Version of the schema the FieldPath is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified API version. + type: string + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: 'Required: Path is the relative path name of the file to be created. Must not be absolute or contain the ''..'' path. Must be utf-8 encoded. The first item of the relative path must not start with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + type: object + required: + - resource + properties: + containerName: + description: 'Container name: required for volumes, optional for env vars' + type: string + divisor: + description: Specifies the output format of the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + x-kubernetes-map-type: atomic + x-kubernetes-list-type: atomic + secret: + description: secret information about the secret data to project + type: object + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + name: + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + default: "" + optional: + description: optional field specify whether the Secret or its key must be defined + type: boolean + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information about the serviceAccountToken data to project + type: object + required: + - path + properties: + audience: + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. + type: integer + format: int64 + path: + description: |- + path is the path relative to the mount point of the file to project the + token into. + type: string + x-kubernetes-list-type: atomic + secret: + description: |- + secret represents a secret that should populate this volume. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: object + properties: + defaultMode: + description: |- + defaultMode is Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values + for mode bits. Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + items: + description: |- + items If unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + type: array + items: + description: Maps a string key to a path within a volume. + type: object + required: + - key + - path + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + type: integer + format: int32 + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + x-kubernetes-list-type: atomic + optional: + description: optional field specify whether the Secret or its keys must be defined + type: boolean + secretName: + description: |- + secretName is the name of the secret in the pod's namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: string + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + traffic: + description: |- + Traffic specifies how to distribute traffic over a collection of + revisions and configurations. + type: array + items: + description: TrafficTarget holds a single entry of the routing table for a Route. + type: object + properties: + configurationName: + description: |- + ConfigurationName of a configuration to whose latest revision we will send + this portion of traffic. When the "status.latestReadyRevisionName" of the + referenced configuration changes, we will automatically migrate traffic + from the prior "latest ready" revision to the new one. This field is never + set in Route's status, only its spec. This is mutually exclusive with + RevisionName. + type: string + latestRevision: + description: |- + LatestRevision may be optionally provided to indicate that the latest + ready Revision of the Configuration should be used for this traffic + target. When provided LatestRevision must be true if RevisionName is + empty; it must be false when RevisionName is non-empty. + type: boolean + percent: + description: |- + Percent indicates that percentage based routing should be used and + the value indicates the percent of traffic that is be routed to this + Revision or Configuration. `0` (zero) mean no traffic, `100` means all + traffic. + When percentage based routing is being used the follow rules apply: + - the sum of all percent values must equal 100 + - when not specified, the implied value for `percent` is zero for + that particular Revision or Configuration + type: integer + format: int64 + revisionName: + description: |- + RevisionName of a specific revision to which to send this portion of + traffic. This is mutually exclusive with ConfigurationName. + type: string + tag: + description: |- + Tag is optionally used to expose a dedicated url for referencing + this target exclusively. + type: string + url: + description: |- + URL displays the URL for accessing named traffic targets. URL is displayed in + status, and is disallowed on spec. URL must contain a scheme (e.g. http://) and + a hostname, but may not contain anything else (e.g. basic auth, url path, etc.) + type: string + status: + description: ServiceStatus represents the Status stanza of the Service resource. + type: object + properties: + address: + description: Address holds the information needed for a Route to be the target of an event. + type: object + properties: + CACerts: + description: |- + CACerts is the Certification Authority (CA) certificates in PEM format + according to https://www.rfc-editor.org/rfc/rfc7468. + type: string + audience: + description: Audience is the OIDC audience for this address. + type: string + name: + description: Name is the name of the address. + type: string + url: + type: string + annotations: + description: |- + Annotations is additional Status fields for the Resource to save some + additional State as well as convey more information to the user. This is + roughly akin to Annotations on any k8s resource, just the reconciler conveying + richer information outwards. + type: object + additionalProperties: + type: string + conditions: + description: Conditions the latest available observations of a resource's current state. + type: array + items: + description: |- + Condition defines a readiness condition for a Knative resource. + See: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#typical-status-properties + type: object + required: + - status + - type + properties: + lastTransitionTime: + description: |- + LastTransitionTime is the last time the condition transitioned from one status to another. + We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic + differences (all other things held constant). + type: string + message: + description: A human readable message indicating details about the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + severity: + description: |- + Severity with which to treat failures of this type of condition. + When this is not specified, it defaults to Error. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of condition. + type: string + latestCreatedRevisionName: + description: |- + LatestCreatedRevisionName is the last revision that was created from this + Configuration. It might not be ready yet, for that use LatestReadyRevisionName. + type: string + latestReadyRevisionName: + description: |- + LatestReadyRevisionName holds the name of the latest Revision stamped out + from this Configuration that has had its "Ready" condition become "True". + type: string + observedGeneration: + description: |- + ObservedGeneration is the 'Generation' of the Service that + was last processed by the controller. + type: integer + format: int64 + traffic: + description: |- + Traffic holds the configured traffic distribution. + These entries will always contain RevisionName references. + When ConfigurationName appears in the spec, this will hold the + LatestReadyRevisionName that we last observed. + type: array + items: + description: TrafficTarget holds a single entry of the routing table for a Route. + type: object + properties: + configurationName: + description: |- + ConfigurationName of a configuration to whose latest revision we will send + this portion of traffic. When the "status.latestReadyRevisionName" of the + referenced configuration changes, we will automatically migrate traffic + from the prior "latest ready" revision to the new one. This field is never + set in Route's status, only its spec. This is mutually exclusive with + RevisionName. + type: string + latestRevision: + description: |- + LatestRevision may be optionally provided to indicate that the latest + ready Revision of the Configuration should be used for this traffic + target. When provided LatestRevision must be true if RevisionName is + empty; it must be false when RevisionName is non-empty. + type: boolean + percent: + description: |- + Percent indicates that percentage based routing should be used and + the value indicates the percent of traffic that is be routed to this + Revision or Configuration. `0` (zero) mean no traffic, `100` means all + traffic. + When percentage based routing is being used the follow rules apply: + - the sum of all percent values must equal 100 + - when not specified, the implied value for `percent` is zero for + that particular Revision or Configuration + type: integer + format: int64 + revisionName: + description: |- + RevisionName of a specific revision to which to send this portion of + traffic. This is mutually exclusive with ConfigurationName. + type: string + tag: + description: |- + Tag is optionally used to expose a dedicated url for referencing + this target exclusively. + type: string + url: + description: |- + URL displays the URL for accessing named traffic targets. URL is displayed in + status, and is disallowed on spec. URL must contain a scheme (e.g. http://) and + a hostname, but may not contain anything else (e.g. basic auth, url path, etc.) + type: string + url: + description: |- + URL holds the url that will distribute traffic over the provided traffic targets. + It generally has the form http[s]://{route-name}.{route-namespace}.{cluster-level-suffix} + type: string + +--- +# Copyright 2018 The Knative 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 +# +# https://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. + +apiVersion: caching.internal.knative.dev/v1alpha1 +kind: Image +metadata: + name: queue-proxy + namespace: knative-serving + labels: + app.kubernetes.io/component: queue-proxy + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +spec: + # This is the Go import path for the binary that is containerized + # and substituted here. + image: gcr.io/knative-releases/knative.dev/serving/cmd/queue@sha256:1310917822086a5d8daa6328f6014001d5ea7ccfb0afc1a4e74b1b6a2eadc5ba + +--- +# Copyright 2018 The Knative 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 +# +# https://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. + +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-autoscaler + namespace: knative-serving + labels: + app.kubernetes.io/component: autoscaler + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + annotations: + knative.dev/example-checksum: "47c2487f" +data: + _example: | + ################################ + # # + # EXAMPLE CONFIGURATION # + # # + ################################ + + # This block is not actually functional configuration, + # but serves to illustrate the available configuration + # options and document them in a way that is accessible + # to users that `kubectl edit` this config map. + # + # These sample configuration options may be copied out of + # this example block and unindented to be in the data block + # to actually change the configuration. + + # The Revision ContainerConcurrency field specifies the maximum number + # of requests the Container can handle at once. Container concurrency + # target percentage is how much of that maximum to use in a stable + # state. E.g. if a Revision specifies ContainerConcurrency of 10, then + # the Autoscaler will try to maintain 7 concurrent connections per pod + # on average. + # Note: this limit will be applied to container concurrency set at every + # level (ConfigMap, Revision Spec or Annotation). + # For legacy and backwards compatibility reasons, this value also accepts + # fractional values in (0, 1] interval (i.e. 0.7 ⇒ 70%). + # Thus minimal percentage value must be greater than 1.0, or it will be + # treated as a fraction. + # NOTE: that this value does not affect actual number of concurrent requests + # the user container may receive, but only the average number of requests + # that the revision pods will receive. + container-concurrency-target-percentage: "70" + + # The container concurrency target default is what the Autoscaler will + # try to maintain when concurrency is used as the scaling metric for the + # Revision and the Revision specifies unlimited concurrency. + # When revision explicitly specifies container concurrency, that value + # will be used as a scaling target for autoscaler. + # When specifying unlimited concurrency, the autoscaler will + # horizontally scale the application based on this target concurrency. + # This is what we call "soft limit" in the documentation, i.e. it only + # affects number of pods and does not affect the number of requests + # individual pod processes. + # The value must be a positive number such that the value multiplied + # by container-concurrency-target-percentage is greater than 0.01. + # NOTE: that this value will be adjusted by application of + # container-concurrency-target-percentage, i.e. by default + # the system will target on average 70 concurrent requests + # per revision pod. + # NOTE: Only one metric can be used for autoscaling a Revision. + container-concurrency-target-default: "100" + + # The requests per second (RPS) target default is what the Autoscaler will + # try to maintain when RPS is used as the scaling metric for a Revision and + # the Revision specifies unlimited RPS. Even when specifying unlimited RPS, + # the autoscaler will horizontally scale the application based on this + # target RPS. + # Must be greater than 1.0. + # NOTE: Only one metric can be used for autoscaling a Revision. + requests-per-second-target-default: "200" + + # The target burst capacity specifies the size of burst in concurrent + # requests that the system operator expects the system will receive. + # Autoscaler will try to protect the system from queueing by introducing + # Activator in the request path if the current spare capacity of the + # service is less than this setting. + # If this setting is 0, then Activator will be in the request path only + # when the revision is scaled to 0. + # If this setting is > 0 and container-concurrency-target-percentage is + # 100% or 1.0, then activator will always be in the request path. + # -1 denotes unlimited target-burst-capacity and activator will always + # be in the request path. + # Other negative values are invalid. + target-burst-capacity: "211" + + # When operating in a stable mode, the autoscaler operates on the + # average concurrency over the stable window. + # Stable window must be in whole seconds. + stable-window: "60s" + + # When observed average concurrency during the panic window reaches + # panic-threshold-percentage the target concurrency, the autoscaler + # enters panic mode. When operating in panic mode, the autoscaler + # scales on the average concurrency over the panic window which is + # panic-window-percentage of the stable-window. + # Must be in the [1, 100] range. + # When computing the panic window it will be rounded to the closest + # whole second, at least 1s. + panic-window-percentage: "10.0" + + # The percentage of the container concurrency target at which to + # enter panic mode when reached within the panic window. + panic-threshold-percentage: "200.0" + + # Max scale up rate limits the rate at which the autoscaler will + # increase pod count. It is the maximum ratio of desired pods versus + # observed pods. + # Cannot be less or equal to 1. + # I.e with value of 2.0 the number of pods can at most go N to 2N + # over single Autoscaler period (2s), but at least N to + # N+1, if Autoscaler needs to scale up. + max-scale-up-rate: "1000.0" + + # Max scale down rate limits the rate at which the autoscaler will + # decrease pod count. It is the maximum ratio of observed pods versus + # desired pods. + # Cannot be less or equal to 1. + # I.e. with value of 2.0 the number of pods can at most go N to N/2 + # over single Autoscaler evaluation period (2s), but at + # least N to N-1, if Autoscaler needs to scale down. + max-scale-down-rate: "2.0" + + # Scale to zero feature flag. + enable-scale-to-zero: "true" + + # Scale to zero grace period is the time an inactive revision is left + # running before it is scaled to zero (must be positive, but recommended + # at least a few seconds if running with mesh networking). + # This is the upper limit and is provided not to enforce timeout after + # the revision stopped receiving requests for stable window, but to + # ensure network reprogramming to put activator in the path has completed. + # If the system determines that a shorter period is satisfactory, + # then the system will only wait that amount of time before scaling to 0. + # NOTE: this period might actually be 0, if activator has been + # in the request path sufficiently long. + # If there is necessity for the last pod to linger longer use + # scale-to-zero-pod-retention-period flag. + scale-to-zero-grace-period: "30s" + + # Scale to zero pod retention period defines the minimum amount + # of time the last pod will remain after Autoscaler has decided to + # scale to zero. + # This flag is for the situations where the pod startup is very expensive + # and the traffic is bursty (requiring smaller windows for fast action), + # but patchy. + # The larger of this flag and `scale-to-zero-grace-period` will effectively + # determine how the last pod will hang around. + scale-to-zero-pod-retention-period: "0s" + + # pod-autoscaler-class specifies the default pod autoscaler class + # that should be used if none is specified. If omitted, + # the Knative Pod Autoscaler (KPA) is used by default. + pod-autoscaler-class: "kpa.autoscaling.knative.dev" + + # The capacity of a single activator task. + # The `unit` is one concurrent request proxied by the activator. + # activator-capacity must be at least 1. + # This value is used for computation of the Activator subset size. + # See the algorithm here: http://bit.ly/38XiCZ3. + # TODO(vagababov): tune after actual benchmarking. + activator-capacity: "100.0" + + # initial-scale is the cluster-wide default value for the initial target + # scale of a revision after creation, unless overridden by the + # "autoscaling.knative.dev/initialScale" annotation. + # This value must be greater than 0 unless allow-zero-initial-scale is true. + initial-scale: "1" + + # allow-zero-initial-scale controls whether either the cluster-wide initial-scale flag, + # or the "autoscaling.knative.dev/initialScale" annotation, can be set to 0. + allow-zero-initial-scale: "false" + + # min-scale is the cluster-wide default value for the min scale of a revision, + # unless overridden by the "autoscaling.knative.dev/minScale" annotation. + min-scale: "0" + + # max-scale is the cluster-wide default value for the max scale of a revision, + # unless overridden by the "autoscaling.knative.dev/maxScale" annotation. + # If set to 0, the revision has no maximum scale. + max-scale: "0" + + # scale-down-delay is the amount of time that must pass at reduced + # concurrency before a scale down decision is applied. This can be useful, + # for example, to maintain replica count and avoid a cold start penalty if + # more requests come in within the scale down delay period. + # The default, 0s, imposes no delay at all. + scale-down-delay: "0s" + + # max-scale-limit sets the maximum permitted value for the max scale of a revision. + # When this is set to a positive value, a revision with a maxScale above that value + # (including a maxScale of "0" = unlimited) is disallowed. + # A value of zero (the default) allows any limit, including unlimited. + max-scale-limit: "0" + +--- +# Copyright 2020 The Knative 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 +# +# https://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. + +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-certmanager + namespace: knative-serving + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: controller + app.kubernetes.io/version: "1.19.0" + networking.knative.dev/certificate-provider: cert-manager + annotations: + knative.dev/example-checksum: "b7a9a602" +data: + _example: | + ################################ + # # + # EXAMPLE CONFIGURATION # + # # + ################################ + + # This block is not actually functional configuration, + # but serves to illustrate the available configuration + # options and document them in a way that is accessible + # to users that `kubectl edit` this config map. + # + # These sample configuration options may be copied out of + # this block and unindented to actually change the configuration. + + # issuerRef is a reference to the issuer for external-domain certificates used for ingress. + # IssuerRef should be either `ClusterIssuer` or `Issuer`. + # Please refer `IssuerRef` in https://cert-manager.io/docs/concepts/issuer/ + # for more details about IssuerRef configuration. + # If the issuerRef is not specified, the self-signed `knative-selfsigned-issuer` ClusterIssuer is used. + issuerRef: | + kind: ClusterIssuer + name: letsencrypt-issuer + + # clusterLocalIssuerRef is a reference to the issuer for cluster-local-domain certificates used for ingress. + # clusterLocalIssuerRef should be either `ClusterIssuer` or `Issuer`. + # Please refer `IssuerRef` in https://cert-manager.io/docs/concepts/issuer/ + # for more details about ClusterInternalIssuerRef configuration. + # If the clusterLocalIssuerRef is not specified, the self-signed `knative-selfsigned-issuer` ClusterIssuer is used. + clusterLocalIssuerRef: | + kind: ClusterIssuer + name: your-company-issuer + + # systemInternalIssuerRef is a reference to the issuer for certificates for system-internal-tls certificates used by Knative internal components. + # systemInternalIssuerRef should be either `ClusterIssuer` or `Issuer`. + # Please refer `IssuerRef` in https://cert-manager.io/docs/concepts/issuer/ + # for more details about ClusterInternalIssuerRef configuration. + # If the systemInternalIssuerRef is not specified, the self-signed `knative-selfsigned-issuer` ClusterIssuer is used. + systemInternalIssuerRef: | + kind: ClusterIssuer + name: knative-selfsigned-issuer + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-defaults + namespace: knative-serving + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: controller + app.kubernetes.io/version: "1.19.0" + annotations: + knative.dev/example-checksum: "5b64ff5c" +data: + _example: | + ################################ + # # + # EXAMPLE CONFIGURATION # + # # + ################################ + + # This block is not actually functional configuration, + # but serves to illustrate the available configuration + # options and document them in a way that is accessible + # to users that `kubectl edit` this config map. + # + # These sample configuration options may be copied out of + # this example block and unindented to be in the data block + # to actually change the configuration. + + # revision-timeout-seconds contains the default number of + # seconds to use for the revision's per-request timeout, if + # none is specified. + revision-timeout-seconds: "300" # 5 minutes + + # max-revision-timeout-seconds contains the maximum number of + # seconds that can be used for revision-timeout-seconds. + # This value must be greater than or equal to revision-timeout-seconds. + # If omitted, the system default is used (600 seconds). + # + # If this value is increased, the activator's terminationGracePeriodSeconds + # should also be increased to prevent in-flight requests being disrupted. + max-revision-timeout-seconds: "600" # 10 minutes + + # revision-response-start-timeout-seconds contains the default number of + # seconds a request will be allowed to stay open while waiting to + # receive any bytes from the user's application, if none is specified. + # + # This defaults to 'revision-timeout-seconds' + revision-response-start-timeout-seconds: "300" + + # revision-idle-timeout-seconds contains the default number of + # seconds a request will be allowed to stay open while not receiving any + # bytes from the user's application, if none is specified. + revision-idle-timeout-seconds: "0" # infinite + + # revision-cpu-request contains the cpu allocation to assign + # to revisions by default. If omitted, no value is specified + # and the system default is used. + # Below is an example of setting revision-cpu-request. + # By default, it is not set by Knative. + revision-cpu-request: "400m" # 0.4 of a CPU (aka 400 milli-CPU) + + # revision-memory-request contains the memory allocation to assign + # to revisions by default. If omitted, no value is specified + # and the system default is used. + # Below is an example of setting revision-memory-request. + # By default, it is not set by Knative. + revision-memory-request: "100M" # 100 megabytes of memory + + # revision-ephemeral-storage-request contains the ephemeral storage + # allocation to assign to revisions by default. If omitted, no value is + # specified and the system default is used. + revision-ephemeral-storage-request: "500M" # 500 megabytes of storage + + # revision-cpu-limit contains the cpu allocation to limit + # revisions to by default. If omitted, no value is specified + # and the system default is used. + # Below is an example of setting revision-cpu-limit. + # By default, it is not set by Knative. + revision-cpu-limit: "1000m" # 1 CPU (aka 1000 milli-CPU) + + # revision-memory-limit contains the memory allocation to limit + # revisions to by default. If omitted, no value is specified + # and the system default is used. + # Below is an example of setting revision-memory-limit. + # By default, it is not set by Knative. + revision-memory-limit: "200M" # 200 megabytes of memory + + # revision-ephemeral-storage-limit contains the ephemeral storage + # allocation to limit revisions to by default. If omitted, no value is + # specified and the system default is used. + revision-ephemeral-storage-limit: "750M" # 750 megabytes of storage + + # container-name-template contains a template for the default + # container name, if none is specified. This field supports + # Go templating and is supplied with the ObjectMeta of the + # enclosing Service or Configuration, so values such as + # {{.Name}} are also valid. + container-name-template: "user-container" + + # init-container-name-template contains a template for the default + # init container name, if none is specified. This field supports + # Go templating and is supplied with the ObjectMeta of the + # enclosing Service or Configuration, so values such as + # {{.Name}} are also valid. + init-container-name-template: "init-container" + + # container-concurrency specifies the maximum number + # of requests the Container can handle at once, and requests + # above this threshold are queued. Setting a value of zero + # disables this throttling and lets through as many requests as + # the pod receives. + container-concurrency: "0" + + # The container concurrency max limit is an operator setting ensuring that + # the individual revisions cannot have arbitrary large concurrency + # values, or autoscaling targets. `container-concurrency` default setting + # must be at or below this value. + # + # Must be greater than 1. + # + # Note: even with this set, a user can choose a containerConcurrency + # of 0 (i.e. unbounded) unless allow-container-concurrency-zero is + # set to "false". + container-concurrency-max-limit: "1000" + + # allow-container-concurrency-zero controls whether users can + # specify 0 (i.e. unbounded) for containerConcurrency. + allow-container-concurrency-zero: "true" + + # enable-service-links specifies the default value used for the + # enableServiceLinks field of the PodSpec, when it is omitted by the user. + # See: https://kubernetes.io/docs/concepts/services-networking/connect-applications-service/#accessing-the-service + # + # This is a tri-state flag with possible values of (true|false|default). + # + # In environments with large number of services it is suggested + # to set this value to `false`. + # See https://github.com/knative/serving/issues/8498. + enable-service-links: "false" + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-deployment + namespace: knative-serving + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: controller + app.kubernetes.io/version: "1.19.0" + annotations: + knative.dev/example-checksum: "720ddb97" +data: + # This is the Go import path for the binary that is containerized + # and substituted here. + queue-sidecar-image: gcr.io/knative-releases/knative.dev/serving/cmd/queue@sha256:1310917822086a5d8daa6328f6014001d5ea7ccfb0afc1a4e74b1b6a2eadc5ba + _example: |- + ################################ + # # + # EXAMPLE CONFIGURATION # + # # + ################################ + + # This block is not actually functional configuration, + # but serves to illustrate the available configuration + # options and document them in a way that is accessible + # to users that `kubectl edit` this config map. + # + # These sample configuration options may be copied out of + # this example block and unindented to be in the data block + # to actually change the configuration. + + # List of repositories for which tag to digest resolving should be skipped + registries-skipping-tag-resolving: "kind.local,ko.local,dev.local" + + # Maximum time allowed for an image's digests to be resolved. + digest-resolution-timeout: "10s" + + # Duration we wait for the deployment to be ready before considering it failed. + progress-deadline: "600s" + + # Sets the queue proxy's CPU request. + # If omitted, a default value (currently "25m"), is used. + queue-sidecar-cpu-request: "25m" + + # Sets the queue proxy's CPU limit. + # If omitted, a default value (currently "1000m"), is used when + # `queueproxy.resource-defaults` is set to `Enabled`. + queue-sidecar-cpu-limit: "1000m" + + # Sets the queue proxy's memory request. + # If omitted, a default value (currently "400Mi"), is used when + # `queueproxy.resource-defaults` is set to `Enabled`. + queue-sidecar-memory-request: "400Mi" + + # Sets the queue proxy's memory limit. + # If omitted, a default value (currently "800Mi"), is used when + # `queueproxy.resource-defaults` is set to `Enabled`. + queue-sidecar-memory-limit: "800Mi" + + # Sets the queue proxy's ephemeral storage request. + # If omitted, no value is specified and the system default is used. + queue-sidecar-ephemeral-storage-request: "512Mi" + + # Sets the queue proxy's ephemeral storage limit. + # If omitted, no value is specified and the system default is used. + queue-sidecar-ephemeral-storage-limit: "1024Mi" + + # Sets tokens associated with specific audiences for queue proxy - used by QPOptions + # + # For example, to add the `service-x` audience: + # queue-sidecar-token-audiences: "service-x" + # Also supports a list of audiences, for example: + # queue-sidecar-token-audiences: "service-x,service-y" + # If omitted, or empty, no tokens are created + queue-sidecar-token-audiences: "" + + # Sets rootCA for the queue proxy - used by QPOptions + # If omitted, or empty, no rootCA is added to the golang rootCAs + queue-sidecar-rootca: "" + + # If set, it automatically configures pod anti-affinity requirements for all Knative services. + # It employs the `preferredDuringSchedulingIgnoredDuringExecution` weighted pod affinity term, + # aligning with the Knative revision label. It yields the configuration below in all workloads' deployments: + # ` + # affinity: + # podAntiAffinity: + # preferredDuringSchedulingIgnoredDuringExecution: + # - podAffinityTerm: + # topologyKey: kubernetes.io/hostname + # labelSelector: + # matchLabels: + # serving.knative.dev/revision: {{revision-name}} + # weight: 100 + # ` + # This may be "none" or "prefer-spread-revision-over-nodes" (default) + # default-affinity-type: "prefer-spread-revision-over-nodes" + + # runtime-class-name contains the selector for which runtimeClassName + # is selected to put in a revision. + # By default, it is not set by Knative. + # + # Example: + # runtime-class-name: | + # "": + # selector: + # use-default-runc: "yes" + # kata: {} + # gvisor: + # selector: + # use-gvisor: "please" + runtime-class-name: "" + +--- +# Copyright 2018 The Knative 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 +# +# https://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. + +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-domain + namespace: knative-serving + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: controller + app.kubernetes.io/version: "1.19.0" + annotations: + knative.dev/example-checksum: "26c09de5" +data: + _example: | + ################################ + # # + # EXAMPLE CONFIGURATION # + # # + ################################ + + # This block is not actually functional configuration, + # but serves to illustrate the available configuration + # options and document them in a way that is accessible + # to users that `kubectl edit` this config map. + # + # These sample configuration options may be copied out of + # this example block and unindented to be in the data block + # to actually change the configuration. + + # Default value for domain. + # Routes having the cluster domain suffix (by default 'svc.cluster.local') + # will not be exposed through Ingress. You can define your own label + # selector to assign that domain suffix to your Route here, or you can set + # the label + # "networking.knative.dev/visibility=cluster-local" + # to achieve the same effect. This shows how to make routes having + # the label app=secret only exposed to the local cluster. + svc.cluster.local: | + selector: + app: secret + + # These are example settings of domain. + # example.com will be used for all routes, but it is the least-specific rule so it + # will only be used if no other domain matches. + example.com: | + + # example.org will be used for routes having app=nonprofit. + example.org: | + selector: + app: nonprofit + +--- +# Copyright 2020 The Knative 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 +# +# https://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. + +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-features + namespace: knative-serving + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: controller + app.kubernetes.io/version: "1.19.0" + annotations: + knative.dev/example-checksum: "0f9b4ade" +data: + _example: |- + ################################ + # # + # EXAMPLE CONFIGURATION # + # # + ################################ + + # This block is not actually functional configuration, + # but serves to illustrate the available configuration + # options and document them in a way that is accessible + # to users that `kubectl edit` this config map. + # + # These sample configuration options may be copied out of + # this example block and unindented to be in the data block + # to actually change the configuration. + + # Default SecurityContext settings to secure-by-default values + # if unset. + # + # This value will default to "enabled" in a future release, + # probably Knative 1.10 + secure-pod-defaults: "disabled" + + # Indicates whether multi container support is enabled + # + # WARNING: Cannot safely be disabled once enabled. + # See: https://knative.dev/docs/serving/configuration/feature-flags/#multiple-containers + multi-container: "enabled" + + # Indicates whether multi container probing is enabled + # + # WARNING: Cannot safely be disabled once enabled. + # See: https://knative.dev/docs/serving/configuration/feature-flags/#multiple-container-probing + multi-container-probing: "disabled" + + # Indicates whether Kubernetes affinity support is enabled + # + # WARNING: Cannot safely be disabled once enabled. + # See: https://knative.dev/docs/serving/feature-flags/#kubernetes-node-affinity + kubernetes.podspec-affinity: "disabled" + + # Indicates whether Kubernetes topologySpreadConstraints support is enabled + # + # WARNING: Cannot safely be disabled once enabled. + # See: https://knative.dev/docs/serving/feature-flags/#kubernetes-topology-spread-constraints + kubernetes.podspec-topologyspreadconstraints: "disabled" + + # Indicates whether Kubernetes hostAliases support is enabled + # + # WARNING: Cannot safely be disabled once enabled. + # See: https://knative.dev/docs/serving/feature-flags/#kubernetes-host-aliases + kubernetes.podspec-hostaliases: "disabled" + + # Indicates whether Kubernetes nodeSelector support is enabled + # + # WARNING: Cannot safely be disabled once enabled. + # See: https://knative.dev/docs/serving/feature-flags/#kubernetes-node-selector + kubernetes.podspec-nodeselector: "disabled" + + # Indicates whether Kubernetes tolerations support is enabled + # + # WARNING: Cannot safely be disabled once enabled + # See: https://knative.dev/docs/serving/feature-flags/#kubernetes-toleration + kubernetes.podspec-tolerations: "disabled" + + # Indicates whether Kubernetes FieldRef support is enabled + # + # WARNING: Cannot safely be disabled once enabled. + # See: https://knative.dev/docs/serving/feature-flags/#kubernetes-fieldref + kubernetes.podspec-fieldref: "disabled" + + # Indicates whether Kubernetes RuntimeClassName support is enabled + # + # WARNING: Cannot safely be disabled once enabled. + # See: https://knative.dev/docs/serving/feature-flags/#kubernetes-runtime-class + kubernetes.podspec-runtimeclassname: "disabled" + + # Indicates whether Kubernetes DNSPolicy support is enabled + # + # WARNING: Cannot safely be disabled once enabled. + # See: https://knative.dev/docs/serving/feature-flags/#kubernetes-dnspolicy + kubernetes.podspec-dnspolicy: "disabled" + + # Indicates whether Kubernetes DNSConfig support is enabled + # + # WARNING: Cannot safely be disabled once enabled. + # See: https://knative.dev/docs/serving/feature-flags/#kubernetes-dnsconfig + kubernetes.podspec-dnsconfig: "disabled" + + # This feature allows end-users to set a subset of fields on the Pod's SecurityContext + # + # When set to "enabled" or "allowed" it allows the following + # PodSecurityContext properties: + # - FSGroup + # - RunAsGroup + # - RunAsNonRoot + # - SupplementalGroups + # - RunAsUser + # - SeccompProfile + # + # This feature flag should be used with caution as the PodSecurityContext + # properties may have a side-effect on non-user sidecar containers that come + # from Knative or your service mesh + # + # WARNING: Cannot safely be disabled once enabled. + # See: https://knative.dev/docs/serving/feature-flags/#kubernetes-security-context + kubernetes.podspec-securitycontext: "disabled" + + # Indicated whether sharing the process namespace via ShareProcessNamespace pod spec is allowed. + # This can be especially useful for sharing data from images directly between sidecars + # + # See: https://knative.dev/docs/serving/configuration/feature-flags/#kubernetes-share-process-namespace + kubernetes.podspec-shareprocessnamespace: "disabled" + + # Indicates whether hostIPC support is enabled + # + # WARNING: Cannot safely be disabled once enabled. + # See https://knative.dev/docs/serving/configuration/feature-flags/#kubernetes-host-ipc + kubernetes.podspec-hostipc: "disabled" + + # Indicates whether hostPID support is enabled + # + # WARNING: Cannot safely be disabled once enabled. + # See https://knative.dev/docs/serving/configuration/feature-flags/#kubernetes-host-pid + kubernetes.podspec-hostpid: "disabled" + + # Indicates whether hostNetwork support is enabled + # + # WARNING: Cannot safely be disabled once enabled. + # See See https://knative.dev/docs/serving/configuration/feature-flags/#kubernetes-host-network + kubernetes.podspec-hostnetwork: "disabled" + + # Indicates whether Kubernetes PriorityClassName support is enabled + # + # WARNING: Cannot safely be disabled once enabled. + # See: https://knative.dev/docs/serving/feature-flags/#kubernetes-priority-class-name + kubernetes.podspec-priorityclassname: "disabled" + + # Indicates whether Kubernetes SchedulerName support is enabled + # + # WARNING: Cannot safely be disabled once enabled. + # See: https://knative.dev/docs/serving/feature-flags/#kubernetes-scheduler-name + kubernetes.podspec-schedulername: "disabled" + + # This feature flag allows end-users to add a subset of capabilities on the Pod's SecurityContext. + # + # When set to "enabled" or "allowed" it allows capabilities to be added to the container. + # For a list of possible capabilities, see https://man7.org/linux/man-pages/man7/capabilities.7.html + kubernetes.containerspec-addcapabilities: "disabled" + + # This feature validates PodSpecs from the validating webhook + # against the K8s API Server. + # + # When "enabled", the server will always run the extra validation. + # When "allowed", the server will not run the dry-run validation by default. + # However, clients may enable the behavior on an individual Service by + # attaching the following metadata annotation: "features.knative.dev/podspec-dryrun":"enabled". + # See: https://knative.dev/docs/serving/feature-flags/#kubernetes-dry-run + kubernetes.podspec-dryrun: "allowed" + + # Controls whether tag header based routing feature are enabled or not. + # 1. Enabled: enabling tag header based routing + # 2. Disabled: disabling tag header based routing + # See: https://knative.dev/docs/serving/feature-flags/#tag-header-based-routing + tag-header-based-routing: "disabled" + + # Controls whether http2 auto-detection should be enabled or not. + # 1. Enabled: http2 connection will be attempted via upgrade. + # 2. Disabled: http2 connection will only be attempted when port name is set to "h2c". + autodetect-http2: "disabled" + + # Controls whether volume support for EmptyDir is enabled or not. + # 1. Enabled: enabling EmptyDir volume support + # 2. Disabled: disabling EmptyDir volume support + kubernetes.podspec-volumes-emptydir: "enabled" + + # Controls whether volume support for image is enabled or not. + # 1. Enabled: enabling image volume support + # 2. Disabled: disabling image volume support + kubernetes.podspec-volumes-image: "disabled" + + # Controls whether volume support for HostPath is enabled or not. + # WARNING: Cannot safely be disabled once enabled. + # WARNING: If you can avoid using a hostPath volume, you should. + # Please read https://kubernetes.io/docs/concepts/storage/volumes/#hostpath before enabling this feature. + # 1. Enabled: enabling HostPath volume support + # 2. Disabled: disabling HostPath volume support + kubernetes.podspec-volumes-hostpath: "disabled" + + # Controls whether volume support for CSI is enabled or not. + # 1. Enabled: enabling CSI volume support + # 2. Disabled: disabling CSI volume support + kubernetes.podspec-volumes-csi: "disabled" + + # Controls whether init containers support is enabled or not. + # 1. Enabled: enabling init containers support + # 2. Disabled: disabling init containers support + kubernetes.podspec-init-containers: "disabled" + + # Controls whether persistent volume claim support is enabled or not. + # 1. Enabled: enabling persistent volume claim support + # 2. Disabled: disabling persistent volume claim support + kubernetes.podspec-persistent-volume-claim: "disabled" + + # Controls whether write access for persistent volumes is enabled or not. + # 1. Enabled: enabling write access for persistent volumes + # 2. Disabled: disabling write access for persistent volumes + kubernetes.podspec-persistent-volume-write: "disabled" + + # Controls whether volume mount propagation support is enabled or not. + # 1. Enabled: enabling volume mount propagation support + # 2. Disabled: disabling volume mount propagation support + kubernetes.podspec-volumes-mount-propagation: "disabled" + + # Controls if the queue proxy podInfo feature is enabled, allowed or disabled + # + # This feature should be enabled/allowed when using queue proxy Options (Extensions) + # Enabling will mount a podInfo volume to the queue proxy container. + # The volume will contains an 'annotations' file (from the pod's annotation field). + # The annotations in this file include the Service annotations set by the client creating the service. + # If mounted, the annotations can be accessed by queue proxy extensions at /etc/podinfo/annnotations + # + # 1. "enabled": always mount a podInfo volume + # 2. "disabled": never mount a podInfo volume + # 3. "allowed": by default, do not mount a podInfo volume + # However, a client may mount the podInfo volume on an individual Service by attaching + # the following metadata annotation to the Service: "features.knative.dev/queueproxy-podinfo":"enabled". + # + # NOTE THAT THIS IS AN EXPERIMENTAL / ALPHA FEATURE + queueproxy.mount-podinfo: "disabled" + + # Default queue proxy resource requests and limits to good values for most cases if set. + queueproxy.resource-defaults: "disabled" + +--- +# Copyright 2018 The Knative 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 +# +# https://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. + +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-gc + namespace: knative-serving + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: controller + app.kubernetes.io/version: "1.19.0" + annotations: + knative.dev/example-checksum: "aa3813a8" +data: + _example: | + ################################ + # # + # EXAMPLE CONFIGURATION # + # # + ################################ + + # This block is not actually functional configuration, + # but serves to illustrate the available configuration + # options and document them in a way that is accessible + # to users that `kubectl edit` this config map. + # + # These sample configuration options may be copied out of + # this example block and unindented to be in the data block + # to actually change the configuration. + + # --------------------------------------- + # Garbage Collector Settings + # --------------------------------------- + # + # Active + # * Revisions which are referenced by a Route are considered active. + # * Individual revisions may be marked with the annotation + # "serving.knative.dev/no-gc":"true" to be permanently considered active. + # * Active revisions are not considered for GC. + # Retention + # * Revisions are retained if they are any of the following: + # 1. Active + # 2. Were created within "retain-since-create-time" + # 3. Were last referenced by a route within + # "retain-since-last-active-time" + # 4. There are fewer than "min-non-active-revisions" + # If none of these conditions are met, or if the count of revisions exceed + # "max-non-active-revisions", they will be deleted by GC. + # The special value "disabled" may be used to turn off these limits. + # + # Example config to immediately collect any inactive revision: + # min-non-active-revisions: "0" + # max-non-active-revisions: "0" + # retain-since-create-time: "disabled" + # retain-since-last-active-time: "disabled" + # + # Example config to always keep around the last ten non-active revisions: + # retain-since-create-time: "disabled" + # retain-since-last-active-time: "disabled" + # max-non-active-revisions: "10" + # + # Example config to disable all garbage collection: + # retain-since-create-time: "disabled" + # retain-since-last-active-time: "disabled" + # max-non-active-revisions: "disabled" + # + # Example config to keep recently deployed or active revisions, + # always maintain the last two in case of rollback, and prevent + # burst activity from exploding the count of old revisions: + # retain-since-create-time: "48h" + # retain-since-last-active-time: "15h" + # min-non-active-revisions: "2" + # max-non-active-revisions: "1000" + + # Duration since creation before considering a revision for GC or "disabled". + retain-since-create-time: "48h" + + # Duration since active before considering a revision for GC or "disabled". + retain-since-last-active-time: "15h" + + # Minimum number of non-active revisions to retain. + min-non-active-revisions: "20" + + # Maximum number of non-active revisions to retain + # or "disabled" to disable any maximum limit. + max-non-active-revisions: "1000" + +--- +# Copyright 2020 The Knative 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 +# +# https://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. + +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-leader-election + namespace: knative-serving + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: controller + app.kubernetes.io/version: "1.19.0" + annotations: + knative.dev/example-checksum: "f4b71f57" +data: + _example: | + ################################ + # # + # EXAMPLE CONFIGURATION # + # # + ################################ + + # This block is not actually functional configuration, + # but serves to illustrate the available configuration + # options and document them in a way that is accessible + # to users that `kubectl edit` this config map. + # + # These sample configuration options may be copied out of + # this example block and unindented to be in the data block + # to actually change the configuration. + + # lease-duration is how long non-leaders will wait to try to acquire the + # lock; 15 seconds is the value used by core kubernetes controllers. + lease-duration: "60s" + + # renew-deadline is how long a leader will try to renew the lease before + # giving up; 10 seconds is the value used by core kubernetes controllers. + renew-deadline: "40s" + + # retry-period is how long the leader election client waits between tries of + # actions; 2 seconds is the value used by core kubernetes controllers. + retry-period: "10s" + + # buckets is the number of buckets used to partition key space of each + # Reconciler. If this number is M and the replica number of the controller + # is N, the N replicas will compete for the M buckets. The owner of a + # bucket will take care of the reconciling for the keys partitioned into + # that bucket. + buckets: "1" + +--- +# Copyright 2018 The Knative 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 +# +# https://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. + +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-logging + namespace: knative-serving + labels: + app.kubernetes.io/version: "1.19.0" + app.kubernetes.io/component: logging + app.kubernetes.io/name: knative-serving + annotations: + knative.dev/example-checksum: "9f25d429" +data: + _example: | + ################################ + # # + # EXAMPLE CONFIGURATION # + # # + ################################ + + # This block is not actually functional configuration, + # but serves to illustrate the available configuration + # options and document them in a way that is accessible + # to users that `kubectl edit` this config map. + # + # These sample configuration options may be copied out of + # this example block and unindented to be in the data block + # to actually change the configuration. + + # Common configuration for all Knative codebase + zap-logger-config: | + { + "level": "info", + "development": false, + "outputPaths": ["stdout"], + "errorOutputPaths": ["stderr"], + "encoding": "json", + "encoderConfig": { + "timeKey": "timestamp", + "levelKey": "severity", + "nameKey": "logger", + "callerKey": "caller", + "messageKey": "message", + "stacktraceKey": "stacktrace", + "lineEnding": "", + "levelEncoder": "", + "timeEncoder": "iso8601", + "durationEncoder": "", + "callerEncoder": "" + } + } + + # Log level overrides + # For all components except the queue proxy, + # changes are picked up immediately. + # For queue proxy, changes require recreation of the pods. + loglevel.controller: "info" + loglevel.autoscaler: "info" + loglevel.queueproxy: "info" + loglevel.webhook: "info" + loglevel.activator: "info" + loglevel.hpaautoscaler: "info" + loglevel.net-istio-controller: "info" + loglevel.net-contour-controller: "info" + loglevel.net-kourier-controller: "info" + loglevel.net-gateway-api-controller: "info" + +--- +# Copyright 2018 The Knative 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 +# +# https://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. + +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-network + namespace: knative-serving + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: networking + app.kubernetes.io/version: "1.19.0" + annotations: + knative.dev/example-checksum: "0573e07d" +data: + _example: | + ################################ + # # + # EXAMPLE CONFIGURATION # + # # + ################################ + + # This block is not actually functional configuration, + # but serves to illustrate the available configuration + # options and document them in a way that is accessible + # to users that `kubectl edit` this config map. + # + # These sample configuration options may be copied out of + # this example block and unindented to be in the data block + # to actually change the configuration. + + # ingress-class specifies the default ingress class + # to use when not dictated by Route annotation. + # + # If not specified, will use the Istio ingress. + # + # Note that changing the Ingress class of an existing Route + # will result in undefined behavior. Therefore it is best to only + # update this value during the setup of Knative, to avoid getting + # undefined behavior. + ingress-class: "istio.ingress.networking.knative.dev" + + # certificate-class specifies the default Certificate class + # to use when not dictated by Route annotation. + # + # If not specified, will use the Cert-Manager Certificate. + # + # Note that changing the Certificate class of an existing Route + # will result in undefined behavior. Therefore it is best to only + # update this value during the setup of Knative, to avoid getting + # undefined behavior. + certificate-class: "cert-manager.certificate.networking.knative.dev" + + # namespace-wildcard-cert-selector specifies a LabelSelector which + # determines which namespaces should have a wildcard certificate + # provisioned. + # + # Use an empty value to disable the feature (this is the default): + # namespace-wildcard-cert-selector: "" + # + # Use an empty object to enable for all namespaces + # namespace-wildcard-cert-selector: {} + # + # Useful labels include the "kubernetes.io/metadata.name" label to + # avoid provisioning a certificate for the "kube-system" namespaces. + # Use the following selector to match pre-1.0 behavior of using + # "networking.knative.dev/disableWildcardCert" to exclude namespaces: + # + # matchExpressions: + # - key: "networking.knative.dev/disableWildcardCert" + # operator: "NotIn" + # values: ["true"] + namespace-wildcard-cert-selector: "" + + # domain-template specifies the golang text template string to use + # when constructing the Knative service's DNS name. The default + # value is "{{.Name}}.{{.Namespace}}.{{.Domain}}". + # + # Valid variables defined in the template include Name, Namespace, Domain, + # Labels, and Annotations. Name will be the result of the tag-template + # below, if a tag is specified for the route. + # + # Changing this value might be necessary when the extra levels in + # the domain name generated is problematic for wildcard certificates + # that only support a single level of domain name added to the + # certificate's domain. In those cases you might consider using a value + # of "{{.Name}}-{{.Namespace}}.{{.Domain}}", or removing the Namespace + # entirely from the template. When choosing a new value be thoughtful + # of the potential for conflicts - for example, when users choose to use + # characters such as `-` in their service, or namespace, names. + # {{.Annotations}} or {{.Labels}} can be used for any customization in the + # go template if needed. + # We strongly recommend keeping namespace part of the template to avoid + # domain name clashes: + # eg. '{{.Name}}-{{.Namespace}}.{{ index .Annotations "sub"}}.{{.Domain}}' + # and you have an annotation {"sub":"foo"}, then the generated template + # would be {Name}-{Namespace}.foo.{Domain} + domain-template: "{{.Name}}.{{.Namespace}}.{{.Domain}}" + + # tag-template specifies the golang text template string to use + # when constructing the DNS name for "tags" within the traffic blocks + # of Routes and Configuration. This is used in conjunction with the + # domain-template above to determine the full URL for the tag. + tag-template: "{{.Tag}}-{{.Name}}" + + # auto-tls is deprecated and replaced by external-domain-tls + auto-tls: "Disabled" + + # Controls whether TLS certificates are automatically provisioned and + # installed in the Knative ingress to terminate TLS connections + # for cluster external domains (like: app.example.com) + # - Enabled: enables the TLS certificate provisioning feature for cluster external domains. + # - Disabled: disables the TLS certificate provisioning feature for cluster external domains. + external-domain-tls: "Disabled" + + # Controls weather TLS certificates are automatically provisioned and + # installed in the Knative ingress to terminate TLS connections + # for cluster local domains (like: app.namespace.svc.) + # - Enabled: enables the TLS certificate provisioning feature for cluster cluster-local domains. + # - Disabled: disables the TLS certificate provisioning feature for cluster cluster local domains. + # NOTE: This flag is in an alpha state and is mostly here to enable internal testing + # for now. Use with caution. + cluster-local-domain-tls: "Disabled" + + # internal-encryption is deprecated and replaced by system-internal-tls + internal-encryption: "false" + + # system-internal-tls controls weather TLS encryption is used for connections between + # the internal components of Knative: + # - ingress to activator + # - ingress to queue-proxy + # - activator to queue-proxy + # + # Possible values for this flag are: + # - Enabled: enables the TLS certificate provisioning feature for cluster cluster-local domains. + # - Disabled: disables the TLS certificate provisioning feature for cluster cluster local domains. + # NOTE: This flag is in an alpha state and is mostly here to enable internal testing + # for now. Use with caution. + system-internal-tls: "Disabled" + + # Controls the behavior of the HTTP endpoint for the Knative ingress. + # It requires auto-tls to be enabled. + # - Enabled: The Knative ingress will be able to serve HTTP connection. + # - Redirected: The Knative ingress will send a 301 redirect for all + # http connections, asking the clients to use HTTPS. + # + # "Disabled" option is deprecated. + http-protocol: "Enabled" + + # rollout-duration contains the minimal duration in seconds over which the + # Configuration traffic targets are rolled out to the newest revision. + rollout-duration: "0" + + # autocreate-cluster-domain-claims controls whether ClusterDomainClaims should + # be automatically created (and deleted) as needed when DomainMappings are + # reconciled. + # + # If this is "false" (the default), the cluster administrator is + # responsible for creating ClusterDomainClaims and delegating them to + # namespaces via their spec.Namespace field. This setting should be used in + # multitenant environments which need to control which namespace can use a + # particular domain name in a domain mapping. + # + # If this is "true", users are able to associate arbitrary names with their + # services via the DomainMapping feature. + autocreate-cluster-domain-claims: "false" + + # If true, networking plugins can add additional information to deployed + # applications to make their pods directly accessible via their IPs even if mesh is + # enabled and thus direct-addressability is usually not possible. + # Consumers like Knative Serving can use this setting to adjust their behavior + # accordingly, i.e. to drop fallback solutions for non-pod-addressable systems. + # + # NOTE: This flag is in an alpha state and is mostly here to enable internal testing + # for now. Use with caution. + enable-mesh-pod-addressability: "false" + + # mesh-compatibility-mode indicates whether consumers of network plugins + # should directly contact Pod IPs (most efficient), or should use the + # Cluster IP (less efficient, needed when mesh is enabled unless + # `enable-mesh-pod-addressability`, above, is set). + # Permitted values are: + # - "auto" (default): automatically determine which mesh mode to use by trying Pod IP and falling back to Cluster IP as needed. + # - "enabled": always use Cluster IP and do not attempt to use Pod IPs. + # - "disabled": always use Pod IPs and do not fall back to Cluster IP on failure. + mesh-compatibility-mode: "auto" + + # Defines the scheme used for external URLs if auto-tls is not enabled. + # This can be used for making Knative report all URLs as "HTTPS" for example, if you're + # fronting Knative with an external loadbalancer that deals with TLS termination and + # Knative doesn't know about that otherwise. + default-external-scheme: "http" + +--- +# Copyright 2018 The Knative 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 +# +# https://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. + +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-observability + namespace: knative-serving + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: observability + app.kubernetes.io/version: "1.19.0" + annotations: + knative.dev/example-checksum: "6bc8b73d" +data: + _example: | + ################################ + # # + # EXAMPLE CONFIGURATION # + # # + ################################ + + # This block is not actually functional configuration, + # but serves to illustrate the available configuration + # options and document them in a way that is accessible + # to users that `kubectl edit` this config map. + # + # These sample configuration options may be copied out of + # this example block and unindented to be in the data block + # to actually change the configuration. + + # logging.enable-var-log-collection defaults to false. + # The fluentd daemon set will be set up to collect /var/log if + # this flag is true. + logging.enable-var-log-collection: "false" + + # logging.revision-url-template provides a template to use for producing the + # logging URL that is injected into the status of each Revision. + logging.revision-url-template: "http://logging.example.com/?revisionUID=${REVISION_UID}" + + # If non-empty, this enables queue proxy writing user request logs to stdout, excluding probe + # requests. + # NB: after 0.18 release logging.enable-request-log must be explicitly set to true + # in order for request logging to be enabled. + # + # The value determines the shape of the request logs and it must be a valid go text/template. + # It is important to keep this as a single line. Multiple lines are parsed as separate entities + # by most collection agents and will split the request logs into multiple records. + # + # The following fields and functions are available to the template: + # + # Request: An http.Request (see https://golang.org/pkg/net/http/#Request) + # representing an HTTP request received by the server. + # + # Response: + # struct { + # Code int // HTTP status code (see https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml) + # Size int // An int representing the size of the response. + # Latency float64 // A float64 representing the latency of the response in seconds. + # } + # + # Revision: + # struct { + # Name string // Knative revision name + # Namespace string // Knative revision namespace + # Service string // Knative service name + # Configuration string // Knative configuration name + # PodName string // Name of the pod hosting the revision + # PodIP string // IP of the pod hosting the revision + # } + # + logging.request-log-template: '{"httpRequest": {"requestMethod": "{{.Request.Method}}", "requestUrl": "{{js .Request.RequestURI}}", "requestSize": "{{.Request.ContentLength}}", "status": {{.Response.Code}}, "responseSize": "{{.Response.Size}}", "userAgent": "{{js .Request.UserAgent}}", "remoteIp": "{{js .Request.RemoteAddr}}", "serverIp": "{{.Revision.PodIP}}", "referer": "{{js .Request.Referer}}", "latency": "{{.Response.Latency}}s", "protocol": "{{.Request.Proto}}"}, "traceId": "{{index .Request.Header "X-B3-Traceid"}}"}' + + # If true, the request logging will be enabled. + logging.enable-request-log: "false" + + # If true, this enables queue proxy writing request logs for probe requests to stdout. + # It uses the same template for user requests, i.e. logging.request-log-template. + logging.enable-probe-request-log: "false" + + # metrics-protocol field specifies the protocol used when exporting metrics + # It supports either 'none' (the default), 'prometheus', 'http/protobuf' (OTLP HTTP), 'grpc' (OTLP gRPC) + metrics-protocol: http/protobuf + + # metrics-endpoint field specifies the destination metrics should be exporter to. + # + # The endpoint MUST be set when the protocol is http/protobuf or grpc. + # The endpoint MUST NOT be set when the protocol is none. + # + # When the protocol is prometheus the endpoint can accept a 'host:port' string to customize the + # listening host interface and port. + metrics-endpoint: http://example.com/v1/traces + + # metrics-export-interval specifies the global metrics reporting period for control and data plane components. + # If a zero or negative value is passed the default reporting OTel period is used (60 secs). + metrics-export-interval: 60s + + # request-metrics-protocol field specifies the protocol used when exporting queue-proxy metrics + # It supports either 'none' (the default), 'prometheus', 'http/protobuf' (OTLP HTTP), 'grpc' (OTLP gRPC) + request-metrics-protocol: http/protobuf + + # request-metrics-endpoint field specifies the destination metrics from the queue proxy should be exporter to. + # + # The endpoint MUST be set when the protocol is http/protobuf or grpc. + # The endpoint MUST NOT be set when the protocol is none. + # + # When the protocol is prometheus the endpoint can accept a 'host:port' string to customize the + # listening host interface and port. + request-metrics-endpoint: http://promstack-kube-prometheus-prometheus.observability:9090/api/v1/otlp/v1/metrics + + # request-metrics-export-interval specifies the global metrics reporting period for the queue-proxy. + # + # If a zero or negative value is passed the default reporting OTel period is used (60 secs). + request-metrics-export-interval: 60s + + # runtime-profiling indicates whether it is allowed to retrieve runtime profiling data from + # the pods via an HTTP server in the format expected by the pprof visualization tool. When + # enabled, the Knative Serving pods expose the profiling data on an alternate HTTP port 8008. + # The HTTP context root for profiling is then /debug/pprof/. + runtime-profiling: enabled + + + # tracing-protocol field specifies the protocol used when exporting metrics + # It supports either 'none' (the default), 'prometheus', 'http/protobuf' (OTLP HTTP), 'grpc' (OTLP gRPC) + # or `stdout` for debugging purposes + tracing-protocol: http/protobuf + + # tracing-endpoint field specifies the destination traces should be exporter to. + # + # The endpoint MUST be set when the protocol is http/protobuf or grpc. + # The endpoint MUST NOT be set when the protocol is none. + tracing-endpoint: http://jaeger-collector.observability:4318/v1/traces + + # tracing-sampling-rate allows the user to specify what percentage of all traces should be exported + # The value should be between 0 (never sample) to 1 (always sample) + tracing-sampling-rate: "1" + +--- +# Copyright 2019 The Knative 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 +# +# https://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. + +apiVersion: v1 +kind: ConfigMap +metadata: + name: config-tracing + namespace: knative-serving + labels: + app.kubernetes.io/name: knative-serving + app.kubernetes.io/component: tracing + app.kubernetes.io/version: "1.19.0" + annotations: + knative.dev/example-checksum: "04c7e9a3" +data: + _example: | + ########################################################### + # # + # This config is deprecated - use config-observability # + # # + ########################################################### + +--- +# Copyright 2020 The Knative 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 +# +# https://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. + +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: activator + namespace: knative-serving + labels: + app.kubernetes.io/component: activator + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +spec: + minReplicas: 1 + maxReplicas: 20 + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: activator + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + # Percentage of the requested CPU + averageUtilization: 100 +--- +# Activator PDB. Currently we permit unavailability of 20% of tasks at the same time. +# Given the subsetting and that the activators are partially stateful systems, we want +# a slow rollout of the new versions and slow migration during node upgrades. +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: activator-pdb + namespace: knative-serving + labels: + app.kubernetes.io/component: activator + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +spec: + minAvailable: 80% + selector: + matchLabels: + app: activator + +--- +# Copyright 2018 The Knative 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 +# +# https://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. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: activator + namespace: knative-serving + labels: + app.kubernetes.io/component: activator + app.kubernetes.io/version: "1.19.0" + app.kubernetes.io/name: knative-serving +spec: + selector: + matchLabels: + app: activator + role: activator + template: + metadata: + labels: + app: activator + role: activator + app.kubernetes.io/component: activator + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + spec: + # To avoid node becoming SPOF, spread our replicas to different nodes. + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: + app: activator + topologyKey: kubernetes.io/hostname + weight: 100 + serviceAccountName: activator + containers: + - name: activator + # This is the Go import path for the binary that is containerized + # and substituted here. + image: gcr.io/knative-releases/knative.dev/serving/cmd/activator@sha256:3e81e0b0e2ead666c131a17b437b1759e59ec2b066db49c493e4663e42fa4452 + # The numbers are based on performance test results from + # https://github.com/knative/serving/issues/1625#issuecomment-511930023 + resources: + requests: + cpu: 300m + memory: 60Mi + limits: + cpu: 1000m + memory: 600Mi + env: + # Run Activator with GC collection when newly generated memory is 500%. + - name: GOGC + value: "500" + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: SYSTEM_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: CONFIG_LOGGING_NAME + value: config-logging + - name: CONFIG_OBSERVABILITY_NAME + value: config-observability + # TODO(https://github.com/knative/pkg/pull/953): Remove stackdriver specific config + - name: METRICS_DOMAIN + value: knative.dev/internal/serving + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + ports: + - name: metrics + containerPort: 9090 + - name: profiling + containerPort: 8008 + - name: http1 + containerPort: 8012 + - name: h2c + containerPort: 8013 + readinessProbe: + httpGet: + port: 8012 + periodSeconds: 5 + failureThreshold: 5 + livenessProbe: + httpGet: + port: 8012 + periodSeconds: 10 + failureThreshold: 12 + initialDelaySeconds: 15 + # The activator (often) sits on the dataplane, and may proxy long (e.g. + # streaming, websockets) requests. We give a long grace period for the + # activator to "lame duck" and drain outstanding requests before we + # forcibly terminate the pod (and outstanding connections). This value + # should be at least as large as the upper bound on the Revision's + # timeoutSeconds property to avoid servicing events disrupting + # connections. + terminationGracePeriodSeconds: 600 +--- +apiVersion: v1 +kind: Service +metadata: + name: activator-service + namespace: knative-serving + labels: + app: activator + app.kubernetes.io/component: activator + app.kubernetes.io/version: "1.19.0" + app.kubernetes.io/name: knative-serving +spec: + selector: + app: activator + ports: + # Define metrics and profiling for them to be accessible within service meshes. + - name: http-metrics + port: 9090 + targetPort: 9090 + - name: http-profiling + port: 8008 + targetPort: 8008 + - name: http + port: 80 + targetPort: 8012 + - name: http2 + port: 81 + targetPort: 8013 + - name: https + port: 443 + targetPort: 8112 + type: ClusterIP + +--- +# Copyright 2018 The Knative 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 +# +# https://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. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: autoscaler + namespace: knative-serving + labels: + app.kubernetes.io/component: autoscaler + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +spec: + replicas: 1 + selector: + matchLabels: + app: autoscaler + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 + template: + metadata: + labels: + app: autoscaler + app.kubernetes.io/component: autoscaler + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + spec: + # To avoid node becoming SPOF, spread our replicas to different nodes. + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: + app: autoscaler + topologyKey: kubernetes.io/hostname + weight: 100 + serviceAccountName: controller + containers: + - name: autoscaler + # This is the Go import path for the binary that is containerized + # and substituted here. + image: gcr.io/knative-releases/knative.dev/serving/cmd/autoscaler@sha256:998a790f7f74caec6e7fc9084d7b85f25b6c011e753b26076c7db766587b3e08 + resources: + requests: + cpu: 100m + memory: 100Mi + limits: + cpu: 1000m + memory: 1000Mi + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_IP + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: SYSTEM_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: CONFIG_LOGGING_NAME + value: config-logging + - name: CONFIG_OBSERVABILITY_NAME + value: config-observability + # TODO(https://github.com/knative/pkg/pull/953): Remove stackdriver specific config + - name: METRICS_DOMAIN + value: knative.dev/serving + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + ports: + - name: metrics + containerPort: 9090 + - name: profiling + containerPort: 8008 + - name: websocket + containerPort: 8080 + readinessProbe: + httpGet: + port: 8080 + livenessProbe: + httpGet: + port: 8080 + failureThreshold: 6 +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: autoscaler + app.kubernetes.io/component: autoscaler + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + name: autoscaler + namespace: knative-serving +spec: + ports: + # Define metrics and profiling for them to be accessible within service meshes. + - name: http-metrics + port: 9090 + targetPort: 9090 + - name: http-profiling + port: 8008 + targetPort: 8008 + - name: http + port: 8080 + targetPort: 8080 + selector: + app: autoscaler + +--- +# Copyright 2018 The Knative 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 +# +# https://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. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller + namespace: knative-serving + labels: + app.kubernetes.io/component: controller + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +spec: + selector: + matchLabels: + app: controller + template: + metadata: + labels: + app: controller + app.kubernetes.io/component: controller + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + spec: + # To avoid node becoming SPOF, spread our replicas to different nodes. + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: + app: controller + topologyKey: kubernetes.io/hostname + weight: 100 + serviceAccountName: controller + containers: + - name: controller + # This is the Go import path for the binary that is containerized + # and substituted here. + image: gcr.io/knative-releases/knative.dev/serving/cmd/controller@sha256:d9f40097903d1d9f4108723d2e41dfc21039ff380671ab80723fc861d81b8071 + resources: + requests: + cpu: 100m + memory: 100Mi + limits: + cpu: 1000m + memory: 1000Mi + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: SYSTEM_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: CONFIG_LOGGING_NAME + value: config-logging + - name: CONFIG_OBSERVABILITY_NAME + value: config-observability + # TODO(https://github.com/knative/pkg/pull/953): Remove stackdriver specific config + - name: METRICS_DOMAIN + value: knative.dev/internal/serving + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + livenessProbe: + httpGet: + path: /health + port: probes + scheme: HTTP + periodSeconds: 5 + failureThreshold: 6 + readinessProbe: + httpGet: + path: /readiness + port: probes + scheme: HTTP + periodSeconds: 5 + failureThreshold: 3 + ports: + - name: metrics + containerPort: 9090 + - name: profiling + containerPort: 8008 + - name: probes + containerPort: 8080 +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: controller + app.kubernetes.io/component: controller + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" + name: controller + namespace: knative-serving +spec: + ports: + # Define metrics and profiling for them to be accessible within service meshes. + - name: http-metrics + port: 9090 + targetPort: 9090 + - name: http-profiling + port: 8008 + targetPort: 8008 + selector: + app: controller + +--- +# Copyright 2020 The Knative 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 +# +# https://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. + +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: webhook + namespace: knative-serving + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +spec: + minReplicas: 1 + maxReplicas: 5 + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: webhook + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + # Percentage of the requested CPU + averageUtilization: 100 +--- +# Webhook PDB. +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: webhook-pdb + namespace: knative-serving + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +spec: + minAvailable: 80% + selector: + matchLabels: + app: webhook + +--- +# Copyright 2018 The Knative 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 +# +# https://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. + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: webhook + namespace: knative-serving + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/version: "1.19.0" + app.kubernetes.io/name: knative-serving +spec: + selector: + matchLabels: + app: webhook + role: webhook + template: + metadata: + labels: + app: webhook + role: webhook + app.kubernetes.io/component: webhook + app.kubernetes.io/version: "1.19.0" + app.kubernetes.io/name: knative-serving + spec: + # To avoid node becoming SPOF, spread our replicas to different nodes. + affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: + app: webhook + topologyKey: kubernetes.io/hostname + weight: 100 + serviceAccountName: controller + containers: + - name: webhook + # This is the Go import path for the binary that is containerized + # and substituted here. + image: gcr.io/knative-releases/knative.dev/serving/cmd/webhook@sha256:deb7f4ff25b854c6a1f58c2435fe0799731eba974d50dd012b534b6daf8eebf3 + resources: + requests: + cpu: 100m + memory: 100Mi + limits: + cpu: 500m + memory: 500Mi + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: SYSTEM_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: CONFIG_LOGGING_NAME + value: config-logging + - name: CONFIG_OBSERVABILITY_NAME + value: config-observability + - name: WEBHOOK_NAME + value: webhook + - name: WEBHOOK_PORT + value: "8443" + # TODO(https://github.com/knative/pkg/pull/953): Remove stackdriver specific config + - name: METRICS_DOMAIN + value: knative.dev/internal/serving + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + runAsNonRoot: true + capabilities: + drop: + - ALL + seccompProfile: + type: RuntimeDefault + ports: + - name: metrics + containerPort: 9090 + - name: profiling + containerPort: 8008 + - name: https-webhook + containerPort: 8443 + readinessProbe: + periodSeconds: 1 + httpGet: + scheme: HTTPS + port: 8443 + livenessProbe: + periodSeconds: 10 + httpGet: + scheme: HTTPS + port: 8443 + failureThreshold: 6 + initialDelaySeconds: 20 + # Our webhook should gracefully terminate by lame ducking first, set this to a sufficiently + # high value that we respect whatever value it has configured for the lame duck grace period. + terminationGracePeriodSeconds: 300 +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: webhook + role: webhook + app.kubernetes.io/component: webhook + app.kubernetes.io/version: "1.19.0" + app.kubernetes.io/name: knative-serving + name: webhook + namespace: knative-serving +spec: + ports: + # Define metrics and profiling for them to be accessible within service meshes. + - name: http-metrics + port: 9090 + targetPort: 9090 + - name: http-profiling + port: 8008 + targetPort: 8008 + - name: https-webhook + port: 443 + targetPort: 8443 + selector: + app: webhook + role: webhook + +--- +# Copyright 2020 The Knative 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 +# +# https://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. + +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: config.webhook.serving.knative.dev + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +webhooks: + - admissionReviewVersions: ["v1", "v1beta1"] + clientConfig: + service: + name: webhook + namespace: knative-serving + failurePolicy: Fail + sideEffects: None + name: config.webhook.serving.knative.dev + objectSelector: + matchExpressions: + - key: app.kubernetes.io/name + operator: In + values: ["knative-serving"] + - key: app.kubernetes.io/component + operator: In + values: ["autoscaler", "controller", "logging", "networking", "observability", "tracing", "net-certmanager"] + timeoutSeconds: 10 + +--- +# Copyright 2020 The Knative 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 +# +# https://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. + +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: webhook.serving.knative.dev + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +webhooks: + - admissionReviewVersions: ["v1", "v1beta1"] + clientConfig: + service: + name: webhook + namespace: knative-serving + failurePolicy: Fail + sideEffects: None + name: webhook.serving.knative.dev + timeoutSeconds: 10 + rules: + - apiGroups: + - autoscaling.internal.knative.dev + - networking.internal.knative.dev + - serving.knative.dev + apiVersions: + - "*" + operations: + - CREATE + - UPDATE + scope: "*" + resources: + - metrics + - podautoscalers + - certificates + - ingresses + - serverlessservices + - configurations + - revisions + - routes + - services + - domainmappings + - domainmappings/status + +--- +# Copyright 2020 The Knative 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 +# +# https://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. + +apiVersion: admissionregistration.k8s.io/v1 +kind: ValidatingWebhookConfiguration +metadata: + name: validation.webhook.serving.knative.dev + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +webhooks: + - admissionReviewVersions: ["v1", "v1beta1"] + clientConfig: + service: + name: webhook + namespace: knative-serving + failurePolicy: Fail + sideEffects: None + name: validation.webhook.serving.knative.dev + timeoutSeconds: 10 + rules: + - apiGroups: + - autoscaling.internal.knative.dev + - networking.internal.knative.dev + - serving.knative.dev + apiVersions: + - "*" + operations: + - CREATE + - UPDATE + - DELETE + scope: "*" + resources: + - metrics + - podautoscalers + - certificates + - ingresses + - serverlessservices + - configurations + - revisions + - routes + - services + - domainmappings + - domainmappings/status + +--- +# Copyright 2020 The Knative 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 +# +# https://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. + +apiVersion: v1 +kind: Secret +metadata: + name: webhook-certs + namespace: knative-serving + labels: + app.kubernetes.io/component: webhook + app.kubernetes.io/name: knative-serving + app.kubernetes.io/version: "1.19.0" +# The data is populated at install time. + +--- diff --git a/integration/fixtures/knative/04-serving-tests-namespace.yaml b/integration/fixtures/knative/04-serving-tests-namespace.yaml new file mode 100644 index 000000000..0a6d3c67d --- /dev/null +++ b/integration/fixtures/knative/04-serving-tests-namespace.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: serving-tests diff --git a/integration/fixtures/knative/tools.go b/integration/fixtures/knative/tools.go new file mode 100644 index 000000000..02790a0e8 --- /dev/null +++ b/integration/fixtures/knative/tools.go @@ -0,0 +1,14 @@ +//go:build tools + +package tools + +// The following dependencies are required by the Knative conformance tests. +// They allow to download the test_images when calling "go mod vendor". +import ( + _ "knative.dev/networking/test/test_images/grpc-ping" + _ "knative.dev/networking/test/test_images/httpproxy" + _ "knative.dev/networking/test/test_images/retry" + _ "knative.dev/networking/test/test_images/runtime" + _ "knative.dev/networking/test/test_images/timeout" + _ "knative.dev/networking/test/test_images/wsserver" +) diff --git a/integration/fixtures/knative/upload-test-images.sh b/integration/fixtures/knative/upload-test-images.sh new file mode 100755 index 000000000..cbbacb9b8 --- /dev/null +++ b/integration/fixtures/knative/upload-test-images.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +# Copyright 2020 The Knative 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. + +set -o errexit + +function upload_test_images() { + echo ">> Publishing test images" + ( + # Script needs to be executed from repo root + cd "$( dirname "$0")/../../../" + echo "Current working directory: $(pwd)" + local image_dir="vendor/knative.dev/networking/test/test_images" + local docker_tag=$1 + local tag_option="" + if [ -n "${docker_tag}" ]; then + tag_option="--tags $docker_tag,latest" + fi + + # ko resolve is being used for the side-effect of publishing images, + # so the resulting yaml produced is ignored. + # shellcheck disable=SC2086 + ko resolve --jobs=4 ${tag_option} -RBf "${image_dir}" > /dev/null + ) +} + +: "${KO_DOCKER_REPO:?"You must set 'KO_DOCKER_REPO', see DEVELOPMENT.md"}" + +upload_test_images "$@" diff --git a/integration/fixtures/leasttime_server.toml b/integration/fixtures/leasttime_server.toml new file mode 100644 index 000000000..263fe906a --- /dev/null +++ b/integration/fixtures/leasttime_server.toml @@ -0,0 +1,36 @@ +[global] + checkNewVersion = false + sendAnonymousUsage = false + +[api] + insecure = true + +[log] + level = "DEBUG" + noColor = true + +[entryPoints] + + [entryPoints.web] + address = ":8000" + +[providers.file] + filename = "{{ .SelfFilename }}" + +## dynamic configuration ## + +[http.routers] + [http.routers.router] + service = "service1" + rule = "Path(`/whoami`)" + +[http.services] + + [http.services.service1.loadBalancer] + strategy = "leasttime" + [[http.services.service1.loadBalancer.servers]] + url = "{{ .Server1 }}" + weight = 1 + [[http.services.service1.loadBalancer.servers]] + url = "{{ .Server2 }}" + weight = 1 diff --git a/integration/fixtures/routing/multi_layer_auth.toml b/integration/fixtures/routing/multi_layer_auth.toml new file mode 100644 index 000000000..59d91c90f --- /dev/null +++ b/integration/fixtures/routing/multi_layer_auth.toml @@ -0,0 +1,51 @@ +[global] + checkNewVersion = false + sendAnonymousUsage = false + +[log] + level = "DEBUG" + noColor = true + +[entryPoints] + [entryPoints.web] + address = ":8000" + +[api] + insecure = true + +[providers.file] + filename = "{{ .SelfFilename }}" + +## Dynamic Configuration ## + +[http.middlewares] + [http.middlewares.auth-middleware.forwardAuth] + address = "http://127.0.0.1:{{ .AuthPort }}/auth" + authResponseHeaders = ["X-User-Role", "X-User-Name"] + +[http.services] + [http.services.admin-service.loadBalancer] + [[http.services.admin-service.loadBalancer.servers]] + url = "http://{{ .AdminIP }}:80" + + [http.services.developer-service.loadBalancer] + [[http.services.developer-service.loadBalancer.servers]] + url = "http://{{ .DeveloperIP }}:80" + +[http.routers] + # Parent router: matches path, applies auth middleware + [http.routers.parent-router] + rule = "PathPrefix(`/whoami`)" + middlewares = ["auth-middleware"] + + # Child router for admin role + [http.routers.admin-router] + rule = "Header(`X-User-Role`, `admin`)" + service = "admin-service" + parentRefs = ["parent-router"] + + # Child router for developer role + [http.routers.developer-router] + rule = "Header(`X-User-Role`, `developer`)" + service = "developer-service" + parentRefs = ["parent-router"] diff --git a/integration/fixtures/simple_deny.toml b/integration/fixtures/simple_deny.toml new file mode 100644 index 000000000..36168fd40 --- /dev/null +++ b/integration/fixtures/simple_deny.toml @@ -0,0 +1,20 @@ +[global] + checkNewVersion = false + sendAnonymousUsage = false + +[entryPoints] + [entryPoints.web] + address = ":8000" + +[api] + insecure = true + +[providers.file] + filename = "{{ .SelfFilename }}" + +## dynamic configuration ## + +[http.routers] + [http.routers.router] + service = "noop@internal" + rule = "Host(`deny.localhost`)" diff --git a/integration/fixtures/simple_encoded_chars.toml b/integration/fixtures/simple_encoded_chars.toml new file mode 100644 index 000000000..669c60b9b --- /dev/null +++ b/integration/fixtures/simple_encoded_chars.toml @@ -0,0 +1,40 @@ +[global] + checkNewVersion = false + sendAnonymousUsage = false + +[log] + level = "DEBUG" + +[entryPoints] + [entryPoints.strict] + address = ":8000" + [entryPoints.strict.http.encodedCharacters] + allowEncodedSlash = false + + [entryPoints.permissive] + address = ":8001" + # No config, default values should apply + + [entryPoints.permissive2] + address = ":8002" + # No config for allowEncodedSlash, default value is effectively true + [entryPoints.permissive.http.encodedCharacters] + allowEncodedBackSlash = false + +[api] + insecure = true + +[providers.file] + filename = "{{ .SelfFilename }}" + +## dynamic configuration ## + +[http.routers] + [http.routers.sameRouter] + service = "service1" + rule = "Host(`test.localhost`)" + +[http.services] + [http.services.service1.loadBalancer] + [[http.services.service1.loadBalancer.servers]] + url = "{{ .Server1 }}" diff --git a/integration/fixtures/tcp_healthcheck/simple.toml b/integration/fixtures/tcp_healthcheck/simple.toml new file mode 100644 index 000000000..4897052e9 --- /dev/null +++ b/integration/fixtures/tcp_healthcheck/simple.toml @@ -0,0 +1,52 @@ +[global] + checkNewVersion = false + sendAnonymousUsage = false + +[log] + level = "DEBUG" + noColor = true + +[entryPoints] + [entryPoints.tcp] + address = ":8093" + +[api] + insecure = true + +[providers.file] + filename = "{{ .SelfFilename }}" + +## dynamic configuration ## + +[tcp.routers] + [tcp.routers.router1] + rule = "HostSNI(`*`)" + service = "weightedservice" + +[tcp.services] + [tcp.services.weightedservice.weighted] + [tcp.services.weightedservice.weighted.healthCheck] + [[tcp.services.weightedservice.weighted.services]] + name = "service1" + weight = 1 + [[tcp.services.weightedservice.weighted.services]] + name = "service2" + weight = 1 + + [tcp.services.service1.loadBalancer] + [tcp.services.service1.loadBalancer.healthCheck] + interval = "500ms" + timeout = "500ms" + send = "PING" + expect = "Received: PING" + [[tcp.services.service1.loadBalancer.servers]] + address = "{{.Server1}}:8080" + + [tcp.services.service2.loadBalancer] + [tcp.services.service2.loadBalancer.healthCheck] + interval = "500ms" + timeout = "500ms" + send = "PING" + expect = "Received: PING" + [[tcp.services.service2.loadBalancer.servers]] + address = "{{.Server2}}:8080" diff --git a/integration/conformance-reports/v1.3.0/experimental-v3.5-default-report.yaml b/integration/gateway-api-conformance-reports/v1.4.0/experimental-v3.6-default-report.yaml similarity index 93% rename from integration/conformance-reports/v1.3.0/experimental-v3.5-default-report.yaml rename to integration/gateway-api-conformance-reports/v1.4.0/experimental-v3.6-default-report.yaml index 0facbd714..429592e4a 100644 --- a/integration/conformance-reports/v1.3.0/experimental-v3.5-default-report.yaml +++ b/integration/gateway-api-conformance-reports/v1.4.0/experimental-v3.6-default-report.yaml @@ -1,14 +1,14 @@ apiVersion: gateway.networking.k8s.io/v1 date: '-' gatewayAPIChannel: experimental -gatewayAPIVersion: v1.3.0 +gatewayAPIVersion: v1.4.0 implementation: contact: - '@traefik/maintainers' organization: traefik project: traefik url: https://traefik.io/ - version: v3.5 + version: v3.6 kind: ConformanceReport mode: default profiles: @@ -16,7 +16,7 @@ profiles: result: success statistics: Failed: 0 - Passed: 12 + Passed: 13 Skipped: 0 name: GATEWAY-GRPC summary: Core tests succeeded. @@ -52,6 +52,8 @@ profiles: - GatewayStaticAddresses - HTTPRouteBackendRequestHeaderModification - HTTPRouteBackendTimeout + - HTTPRouteCORS + - HTTPRouteNamedRouteRule - HTTPRouteParentRefPort - HTTPRouteRequestMirror - HTTPRouteRequestMultipleMirrors diff --git a/integration/k8s_conformance_test.go b/integration/gateway_api_conformance_test.go similarity index 83% rename from integration/k8s_conformance_test.go rename to integration/gateway_api_conformance_test.go index 1e03f223b..9a25125d6 100644 --- a/integration/k8s_conformance_test.go +++ b/integration/gateway_api_conformance_test.go @@ -1,3 +1,5 @@ +//go:build gatewayAPIConformance + package integration import ( @@ -37,15 +39,8 @@ import ( "sigs.k8s.io/yaml" ) -const ( - k3sImage = "docker.io/rancher/k3s:v1.29.3-k3s1" - traefikImage = "traefik/traefik:latest" - traefikDeployment = "deployments/traefik" - traefikNamespace = "traefik" -) - -// K8sConformanceSuite tests suite. -type K8sConformanceSuite struct { +// GatewayAPIConformanceSuite tests suite. +type GatewayAPIConformanceSuite struct { BaseSuite k3sContainer *k3s.K3sContainer @@ -54,15 +49,11 @@ type K8sConformanceSuite struct { clientSet *kclientset.Clientset } -func TestK8sConformanceSuite(t *testing.T) { - suite.Run(t, new(K8sConformanceSuite)) +func TestGatewayAPIConformanceSuite(t *testing.T) { + suite.Run(t, new(GatewayAPIConformanceSuite)) } -func (s *K8sConformanceSuite) SetupSuite() { - if !*k8sConformance { - s.T().Skip("Skip because it can take a long time to execute. To enable pass the `k8sConformance` flag.") - } - +func (s *GatewayAPIConformanceSuite) SetupSuite() { s.BaseSuite.SetupSuite() // Avoid panic. @@ -89,9 +80,9 @@ func (s *K8sConformanceSuite) SetupSuite() { s.k3sContainer, err = k3s.Run(ctx, k3sImage, - k3s.WithManifest("./fixtures/k8s-conformance/00-experimental-v1.3.0.yml"), - k3s.WithManifest("./fixtures/k8s-conformance/01-rbac.yml"), - k3s.WithManifest("./fixtures/k8s-conformance/02-traefik.yml"), + k3s.WithManifest("./fixtures/gateway-api-conformance/00-experimental-v1.4.0.yml"), + k3s.WithManifest("./fixtures/gateway-api-conformance/01-rbac.yml"), + k3s.WithManifest("./fixtures/gateway-api-conformance/02-traefik.yml"), network.WithNetwork(nil, s.network), ) if err != nil { @@ -144,7 +135,7 @@ func (s *K8sConformanceSuite) SetupSuite() { } } -func (s *K8sConformanceSuite) TearDownSuite() { +func (s *GatewayAPIConformanceSuite) TearDownSuite() { ctx := s.T().Context() if s.T().Failed() || *showLog { @@ -170,7 +161,7 @@ func (s *K8sConformanceSuite) TearDownSuite() { s.BaseSuite.TearDownSuite() } -func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() { +func (s *GatewayAPIConformanceSuite) TestK8sGatewayAPIConformance() { // Wait for traefik to start k3sContainerIP, err := s.k3sContainer.ContainerIP(s.T().Context()) require.NoError(s.T(), err) @@ -188,12 +179,12 @@ func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() { TimeoutConfig: config.DefaultTimeoutConfig(), ManifestFS: []fs.FS{&conformance.Manifests}, EnableAllSupportedFeatures: false, - RunTest: *k8sConformanceRunTest, + RunTest: *gatewayAPIConformanceRunTest, Implementation: v1.Implementation{ Organization: "traefik", Project: "traefik", URL: "https://traefik.io/", - Version: *k8sConformanceTraefikVersion, + Version: *traefikVersion, Contact: []string{"@traefik/maintainers"}, }, ConformanceProfiles: sets.New( @@ -227,8 +218,8 @@ func (s *K8sConformanceSuite) TestK8sGatewayAPIConformance() { require.NoError(s.T(), err) s.T().Logf("Conformance report:\n%s", string(rawReport)) - require.NoError(s.T(), os.MkdirAll("./conformance-reports/"+report.GatewayAPIVersion, 0o755)) - outFile := filepath.Join("conformance-reports/"+report.GatewayAPIVersion, fmt.Sprintf("%s-%s-%s-report.yaml", report.GatewayAPIChannel, report.Version, report.Mode)) + require.NoError(s.T(), os.MkdirAll("./gateway-api-conformance-reports/"+report.GatewayAPIVersion, 0o755)) + outFile := filepath.Join("gateway-api-conformance-reports/"+report.GatewayAPIVersion, fmt.Sprintf("%s-%s-%s-report.yaml", report.GatewayAPIChannel, report.Version, report.Mode)) require.NoError(s.T(), os.WriteFile(outFile, rawReport, 0o600)) s.T().Logf("Report written to: %s", outFile) } diff --git a/integration/healthcheck_test.go b/integration/healthcheck_test.go index 1276f09dd..6ab2ac618 100644 --- a/integration/healthcheck_test.go +++ b/integration/healthcheck_test.go @@ -108,6 +108,53 @@ func (s *HealthCheckSuite) TestSimpleConfiguration() { assert.Equal(s.T(), http.StatusNotFound, resp.StatusCode) } +func (s *HealthCheckSuite) TestSimpleConfiguration_Passive() { + file := s.adaptFile("fixtures/healthcheck/simple_passive.toml", struct { + Server1 string + }{s.whoami1IP}) + + s.traefikCmd(withConfigFile(file)) + + // wait for traefik + err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("Host(`test.localhost`)")) + require.NoError(s.T(), err) + + frontendHealthReq, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/health", nil) + require.NoError(s.T(), err) + frontendHealthReq.Host = "test.localhost" + + err = try.Request(frontendHealthReq, 500*time.Millisecond, try.StatusCodeIs(http.StatusOK)) + require.NoError(s.T(), err) + + // Fix all whoami health to 500 + client := &http.Client{} + whoamiHosts := []string{s.whoami1IP, s.whoami2IP} + for _, whoami := range whoamiHosts { + statusInternalServerErrorReq, err := http.NewRequest(http.MethodPost, "http://"+whoami+"/health", bytes.NewBufferString("500")) + require.NoError(s.T(), err) + _, err = client.Do(statusInternalServerErrorReq) + require.NoError(s.T(), err) + } + + // First call, the passive health check is not yet triggered, so we expect a 500. + err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusInternalServerError)) + require.NoError(s.T(), err) + + // Verify no backend service is available due to failing health checks + err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusServiceUnavailable)) + require.NoError(s.T(), err) + + // Change one whoami health to 200 + statusOKReq1, err := http.NewRequest(http.MethodPost, "http://"+s.whoami1IP+"/health", bytes.NewBufferString("200")) + require.NoError(s.T(), err) + _, err = client.Do(statusOKReq1) + require.NoError(s.T(), err) + + // Verify frontend health : after + err = try.Request(frontendHealthReq, 3*time.Second, try.StatusCodeIs(http.StatusOK)) + require.NoError(s.T(), err) +} + func (s *HealthCheckSuite) TestMultipleEntrypoints() { file := s.adaptFile("fixtures/healthcheck/multiple-entrypoints.toml", struct { Server1 string diff --git a/integration/integration_test.go b/integration/integration_test.go index bb0fc2bfd..9ef4f8782 100644 --- a/integration/integration_test.go +++ b/integration/integration_test.go @@ -37,12 +37,17 @@ import ( var ( showLog = flag.Bool("tlog", false, "always show Traefik logs") - k8sConformance = flag.Bool("k8sConformance", false, "run K8s Gateway API conformance test") - k8sConformanceRunTest = flag.String("k8sConformanceRunTest", "", "run a specific K8s Gateway API conformance test") - k8sConformanceTraefikVersion = flag.String("k8sConformanceTraefikVersion", "dev", "specify the Traefik version for the K8s Gateway API conformance report") + gatewayAPIConformanceRunTest = flag.String("gatewayAPIConformanceRunTest", "", "runs a specific Gateway API conformance test") + traefikVersion = flag.String("traefikVersion", "dev", "defines the Traefik version") ) -const tailscaleSecretFilePath = "tailscale.secret" +const ( + k3sImage = "docker.io/rancher/k3s:v1.34.2-k3s1" + traefikImage = "traefik/traefik:latest" + traefikDeployment = "deployments/traefik" + traefikNamespace = "traefik" + tailscaleSecretFilePath = "tailscale.secret" +) type composeConfig struct { Services map[string]composeService `yaml:"services"` diff --git a/integration/k8s_test.go b/integration/k8s_test.go index 396913538..3e81d8b0b 100644 --- a/integration/k8s_test.go +++ b/integration/k8s_test.go @@ -199,9 +199,9 @@ func matchesConfig(wantConfig string, buf *bytes.Buffer) try.ResponseCondition { sanitizedExpected := rxURL.ReplaceAll(bytes.TrimSpace(expected), []byte(`"$1": "XXXX"`)) sanitizedGot := rxURL.ReplaceAll(got, []byte(`"$1": "XXXX"`)) - rxServerStatus := regexp.MustCompile(`"http://.*?":\s+(".*")`) - sanitizedExpected = rxServerStatus.ReplaceAll(sanitizedExpected, []byte(`"http://XXXX": $1`)) - sanitizedGot = rxServerStatus.ReplaceAll(sanitizedGot, []byte(`"http://XXXX": $1`)) + rxServerStatus := regexp.MustCompile(`"(http://)?.*?":\s+(".*")`) + sanitizedExpected = rxServerStatus.ReplaceAll(sanitizedExpected, []byte(`"XXXX": $1`)) + sanitizedGot = rxServerStatus.ReplaceAll(sanitizedGot, []byte(`"XXXX": $1`)) if bytes.Equal(sanitizedExpected, sanitizedGot) { return nil diff --git a/integration/knative_conformance_test.go b/integration/knative_conformance_test.go new file mode 100644 index 000000000..89ffa323a --- /dev/null +++ b/integration/knative_conformance_test.go @@ -0,0 +1,178 @@ +// Use a build tag to include and run Knative conformance tests. +// The Knative conformance toolkit redefines the skip-tests flag, +// which conflicts with the testing library and causes a panic. +//go:build knativeConformance + +package integration + +import ( + "flag" + "io" + "os" + "slices" + "testing" + "time" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/modules/k3s" + "github.com/testcontainers/testcontainers-go/network" + "github.com/traefik/traefik/v3/integration/try" + "knative.dev/networking/test/conformance/ingress" + klog "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +const knativeNamespace = "knative-serving" + +var imageNames = []string{ + traefikImage, + "ko.local/grpc-ping:latest", + "ko.local/httpproxy:latest", + "ko.local/retry:latest", + "ko.local/runtime:latest", + "ko.local/wsserver:latest", + "ko.local/timeout:latest", +} + +type KnativeConformanceSuite struct { + BaseSuite + + k3sContainer *k3s.K3sContainer +} + +func TestKnativeConformanceSuite(t *testing.T) { + suite.Run(t, new(KnativeConformanceSuite)) +} + +func (s *KnativeConformanceSuite) SetupSuite() { + s.BaseSuite.SetupSuite() + + // Avoid panic. + klog.SetLogger(zap.New()) + + provider, err := testcontainers.ProviderDocker.GetProvider() + if err != nil { + s.T().Fatal(err) + } + + ctx := s.T().Context() + + // Ensure image is available locally. + images, err := provider.ListImages(ctx) + if err != nil { + s.T().Fatal(err) + } + + if !slices.ContainsFunc(images, func(img testcontainers.ImageInfo) bool { + return img.Name == traefikImage + }) { + s.T().Fatal("Traefik image is not present") + } + + s.k3sContainer, err = k3s.Run(ctx, + k3sImage, + k3s.WithManifest("./fixtures/knative/00-knative-crd-v1.19.0.yml"), + k3s.WithManifest("./fixtures/knative/01-rbac.yml"), + k3s.WithManifest("./fixtures/knative/02-traefik.yml"), + k3s.WithManifest("./fixtures/knative/03-knative-serving-v1.19.0.yaml"), + k3s.WithManifest("./fixtures/knative/04-serving-tests-namespace.yaml"), + network.WithNetwork(nil, s.network), + ) + if err != nil { + s.T().Fatal(err) + } + + for _, imageName := range imageNames { + if err = s.k3sContainer.LoadImages(ctx, imageName); err != nil { + s.T().Fatal(err) + } + } + + exitCode, _, err := s.k3sContainer.Exec(ctx, []string{"kubectl", "wait", "-n", traefikNamespace, traefikDeployment, "--for=condition=Available", "--timeout=10s"}) + if err != nil || exitCode > 0 { + s.T().Fatalf("Traefik pod is not ready: %v", err) + } + + exitCode, _, err = s.k3sContainer.Exec(ctx, []string{"kubectl", "wait", "-n", knativeNamespace, "deployment/activator", "--for=condition=Available", "--timeout=10s"}) + if err != nil || exitCode > 0 { + s.T().Fatalf("Activator pod is not ready: %v", err) + } + + exitCode, _, err = s.k3sContainer.Exec(ctx, []string{"kubectl", "wait", "-n", knativeNamespace, "deployment/controller", "--for=condition=Available", "--timeout=10s"}) + if err != nil || exitCode > 0 { + s.T().Fatalf("Controller pod is not ready: %v", err) + } + + exitCode, _, err = s.k3sContainer.Exec(ctx, []string{"kubectl", "wait", "-n", knativeNamespace, "deployment/autoscaler", "--for=condition=Available", "--timeout=10s"}) + if err != nil || exitCode > 0 { + s.T().Fatalf("Autoscaler pod is not ready: %v", err) + } + + exitCode, _, err = s.k3sContainer.Exec(ctx, []string{"kubectl", "wait", "-n", knativeNamespace, "deployment/webhook", "--for=condition=Available", "--timeout=10s"}) + if err != nil || exitCode > 0 { + s.T().Fatalf("Webhook pod is not ready: %v", err) + } +} + +func (s *KnativeConformanceSuite) TearDownSuite() { + ctx := s.T().Context() + + if s.T().Failed() || *showLog { + k3sLogs, err := s.k3sContainer.Logs(ctx) + if err == nil { + if res, err := io.ReadAll(k3sLogs); err == nil { + s.T().Log(string(res)) + } + } + + exitCode, result, err := s.k3sContainer.Exec(ctx, []string{"kubectl", "logs", "-n", traefikNamespace, traefikDeployment}) + if err == nil || exitCode == 0 { + if res, err := io.ReadAll(result); err == nil { + s.T().Log(string(res)) + } + } + } + + if err := s.k3sContainer.Terminate(ctx); err != nil { + s.T().Fatal(err) + } + + s.BaseSuite.TearDownSuite() +} + +func (s *KnativeConformanceSuite) TestKnativeConformance() { + // Wait for traefik to start + k3sContainerIP, err := s.k3sContainer.ContainerIP(s.T().Context()) + require.NoError(s.T(), err) + + err = try.GetRequest("http://"+k3sContainerIP+":9000/api/entrypoints", 10*time.Second, try.BodyContains(`"name":"pweb"`)) + require.NoError(s.T(), err) + + kubeconfig, err := s.k3sContainer.GetKubeConfig(s.T().Context()) + if err != nil { + s.T().Fatal(err) + } + + // Write the kubeconfig.yaml in a temp file. + kubeconfigFile := s.T().TempDir() + "/kubeconfig.yaml" + + if err = os.WriteFile(kubeconfigFile, kubeconfig, 0o644); err != nil { + s.T().Fatal(err) + } + + if err = flag.CommandLine.Set("kubeconfig", kubeconfigFile); err != nil { + s.T().Fatal(err) + } + + if err = flag.CommandLine.Set("ingressClass", "traefik.ingress.networking.knative.dev"); err != nil { + s.T().Fatal(err) + } + + if err = flag.CommandLine.Set("skip-tests", "headers/probe"); err != nil { + s.T().Fatal(err) + } + + ingress.RunConformance(s.T()) +} diff --git a/integration/resources/compose/docker.yml b/integration/resources/compose/docker.yml index 7ee4492cf..783d5be38 100644 --- a/integration/resources/compose/docker.yml +++ b/integration/resources/compose/docker.yml @@ -36,13 +36,8 @@ services: traefik.http.Routers.Super.Rule: Host(`my.super.host`) traefik.http.Services.powpow.LoadBalancer.server.Port: 2375 - wrr-server: + nonRunning: image: traefik/whoami labels: - traefik.http.Routers.wrr-server.Rule: Host(`my.wrr.host`) - traefik.http.Services.wrr-server.LoadBalancer.server.Weight: 4 - wrr-server2: - image: traefik/whoami - labels: - traefik.http.Routers.wrr-server.Rule: Host(`my.wrr.host`) - traefik.http.Services.wrr-server.LoadBalancer.server.Weight: 1 + traefik.http.Routers.NonRunning.Rule: Host(`non.running.host`) + traefik.docker.allownonrunning: "true" diff --git a/integration/resources/compose/k8s.yml b/integration/resources/compose/k8s.yml index 51e46b0be..81c098d98 100644 --- a/integration/resources/compose/k8s.yml +++ b/integration/resources/compose/k8s.yml @@ -1,6 +1,6 @@ services: server: - image: rancher/k3s:v1.21.14-k3s1 + image: rancher/k3s:v1.34.2-k3s1 privileged: true command: - server @@ -25,7 +25,7 @@ services: - ./fixtures/k8s:/var/lib/rancher/k3s/server/manifests node: - image: rancher/k3s:v1.21.14-k3s1 + image: rancher/k3s:v1.34.2-k3s1 privileged: true environment: K3S_TOKEN: somethingtotallyrandom diff --git a/integration/resources/compose/pebble.yml b/integration/resources/compose/pebble.yml index 4e9eac58b..621c6910f 100644 --- a/integration/resources/compose/pebble.yml +++ b/integration/resources/compose/pebble.yml @@ -1,8 +1,7 @@ services: pebble: - image: letsencrypt/pebble:v2.3.1 + image: ghcr.io/letsencrypt/pebble:2.8.0 command: - - pebble - --dnsserver - host.docker.internal:5053 environment: diff --git a/integration/resources/compose/routing.yml b/integration/resources/compose/routing.yml new file mode 100644 index 000000000..88bbc6d93 --- /dev/null +++ b/integration/resources/compose/routing.yml @@ -0,0 +1,8 @@ +services: + whoami-admin: + image: traefik/whoami + hostname: whoami-admin + + whoami-developer: + image: traefik/whoami + hostname: whoami-developer \ No newline at end of file diff --git a/integration/resources/compose/tcp_healthcheck.yml b/integration/resources/compose/tcp_healthcheck.yml new file mode 100644 index 000000000..fe7d93b68 --- /dev/null +++ b/integration/resources/compose/tcp_healthcheck.yml @@ -0,0 +1,12 @@ +services: + whoamitcp1: + image: traefik/whoamitcp + command: + - -name + - whoamitcp1 + + whoamitcp2: + image: traefik/whoamitcp + command: + - -name + - whoamitcp2 diff --git a/integration/routing_test.go b/integration/routing_test.go new file mode 100644 index 000000000..8f643802b --- /dev/null +++ b/integration/routing_test.go @@ -0,0 +1,154 @@ +package integration + +import ( + "net" + "net/http" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "github.com/traefik/traefik/v3/integration/try" +) + +// RoutingSuite tests multi-layer routing with authentication middleware. +type RoutingSuite struct{ BaseSuite } + +func TestRoutingSuite(t *testing.T) { + suite.Run(t, new(RoutingSuite)) +} + +func (s *RoutingSuite) SetupSuite() { + s.BaseSuite.SetupSuite() + + s.createComposeProject("routing") + s.composeUp() +} + +func (s *RoutingSuite) TearDownSuite() { + s.BaseSuite.TearDownSuite() +} + +// authHandler implements the ForwardAuth protocol. +// It validates Bearer tokens and adds X-User-Role and X-User-Name headers. +func authHandler(w http.ResponseWriter, r *http.Request) { + authHeader := r.Header.Get("Authorization") + if authHeader == "" { + w.WriteHeader(http.StatusUnauthorized) + return + } + + if !strings.HasPrefix(authHeader, "Bearer ") { + w.WriteHeader(http.StatusUnauthorized) + return + } + + token := strings.TrimPrefix(authHeader, "Bearer ") + role, username, ok := getUserByToken(token) + if !ok { + w.WriteHeader(http.StatusUnauthorized) + return + } + + // Set headers that will be forwarded by Traefik + w.Header().Set("X-User-Role", role) + w.Header().Set("X-User-Name", username) + w.WriteHeader(http.StatusOK) +} + +// getUserByToken returns the role and username for a given token. +func getUserByToken(token string) (role, username string, ok bool) { + users := map[string]struct { + role string + username string + }{ + "bob-token": {role: "admin", username: "bob"}, + "jack-token": {role: "developer", username: "jack"}, + "alice-token": {role: "guest", username: "alice"}, + } + + u, exists := users[token] + return u.role, u.username, exists +} + +// TestMultiLayerRoutingWithAuth tests the complete multi layer routing scenario: +// - Parent router matches path and applies authentication middleware +// - Auth middleware validates token and adds role header +// - Child routers route based on the role header added by the middleware +func (s *RoutingSuite) TestMultiLayerRoutingWithAuth() { + listener, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(s.T(), err) + defer listener.Close() + + _, authPort, err := net.SplitHostPort(listener.Addr().String()) + require.NoError(s.T(), err) + + go func() { + _ = http.Serve(listener, http.HandlerFunc(authHandler)) + }() + + adminIP := s.getComposeServiceIP("whoami-admin") + require.NotEmpty(s.T(), adminIP) + + developerIP := s.getComposeServiceIP("whoami-developer") + require.NotEmpty(s.T(), developerIP) + + file := s.adaptFile("fixtures/routing/multi_layer_auth.toml", struct { + AuthPort string + AdminIP string + DeveloperIP string + }{ + AuthPort: authPort, + AdminIP: adminIP, + DeveloperIP: developerIP, + }) + + s.traefikCmd(withConfigFile(file)) + + err = try.GetRequest("http://127.0.0.1:8080/api/rawdata", 2*time.Second, try.BodyContains("parent-router")) + require.NoError(s.T(), err) + + // Test 1: bob (admin role) routes to admin-service + req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil) + require.NoError(s.T(), err) + req.Header.Set("Authorization", "Bearer bob-token") + + err = try.Request(req, 2*time.Second, + try.StatusCodeIs(http.StatusOK), + try.BodyContains("whoami-admin")) + require.NoError(s.T(), err) + + // Test 2: jack (developer role) routes to developer-service + req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil) + require.NoError(s.T(), err) + req.Header.Set("Authorization", "Bearer jack-token") + + err = try.Request(req, 2*time.Second, + try.StatusCodeIs(http.StatusOK), + try.BodyContains("whoami-developer")) + require.NoError(s.T(), err) + + // Test 3: Invalid token returns 401 + req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil) + require.NoError(s.T(), err) + req.Header.Set("Authorization", "Bearer invalid-token") + + err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusUnauthorized)) + require.NoError(s.T(), err) + + // Test 4: Missing token returns 401 + req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil) + require.NoError(s.T(), err) + + err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusUnauthorized)) + require.NoError(s.T(), err) + + // Test 5: Valid auth but role has no matching child router returns 404 + req, err = http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil) + require.NoError(s.T(), err) + req.Header.Set("Authorization", "Bearer alice-token") + + err = try.Request(req, 2*time.Second, try.StatusCodeIs(http.StatusNotFound)) + require.NoError(s.T(), err) +} diff --git a/integration/simple_test.go b/integration/simple_test.go index 675e21c3d..16c743829 100644 --- a/integration/simple_test.go +++ b/integration/simple_test.go @@ -885,6 +885,109 @@ func (s *SimpleSuite) TestWRRServer() { assert.Equal(s.T(), 1, repartition[whoami2IP]) } +func (s *SimpleSuite) TestLeastTimeServer() { + s.createComposeProject("base") + + s.composeUp() + defer s.composeDown() + + whoami1IP := s.getComposeServiceIP("whoami1") + whoami2IP := s.getComposeServiceIP("whoami2") + + file := s.adaptFile("fixtures/leasttime_server.toml", struct { + Server1 string + Server2 string + }{Server1: "http://" + whoami1IP, Server2: "http://" + whoami2IP}) + + s.traefikCmd(withConfigFile(file)) + + err := try.GetRequest("http://127.0.0.1:8080/api/http/services", 1000*time.Millisecond, try.BodyContains("service1")) + require.NoError(s.T(), err) + + // Verify leasttime strategy is configured + err = try.GetRequest("http://127.0.0.1:8080/api/http/services", 1000*time.Millisecond, try.BodyContains("leasttime")) + require.NoError(s.T(), err) + + // Make requests and verify both servers respond + repartition := map[string]int{} + for range 10 { + req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil) + require.NoError(s.T(), err) + + response, err := http.DefaultClient.Do(req) + require.NoError(s.T(), err) + assert.Equal(s.T(), http.StatusOK, response.StatusCode) + + body, err := io.ReadAll(response.Body) + require.NoError(s.T(), err) + + if strings.Contains(string(body), whoami1IP) { + repartition[whoami1IP]++ + } + if strings.Contains(string(body), whoami2IP) { + repartition[whoami2IP]++ + } + } + + // Both servers should have received requests + assert.Positive(s.T(), repartition[whoami1IP]) + assert.Positive(s.T(), repartition[whoami2IP]) +} + +func (s *SimpleSuite) TestLeastTimeHeterogeneousPerformance() { + // Create test servers with different response times + var fastServerCalls, slowServerCalls atomic.Int32 + + fastServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + fastServerCalls.Add(1) + time.Sleep(10 * time.Millisecond) // Fast server + rw.WriteHeader(http.StatusOK) + _, _ = rw.Write([]byte("fast-server")) + })) + defer fastServer.Close() + + slowServer := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + slowServerCalls.Add(1) + time.Sleep(100 * time.Millisecond) // Slow server + rw.WriteHeader(http.StatusOK) + _, _ = rw.Write([]byte("slow-server")) + })) + defer slowServer.Close() + + file := s.adaptFile("fixtures/leasttime_server.toml", struct { + Server1 string + Server2 string + }{Server1: fastServer.URL, Server2: slowServer.URL}) + + s.traefikCmd(withConfigFile(file)) + + err := try.GetRequest("http://127.0.0.1:8080/api/http/services", 1000*time.Millisecond, try.BodyContains("service1")) + require.NoError(s.T(), err) + + // Make 20 requests to build up response time statistics + for range 20 { + req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil) + require.NoError(s.T(), err) + + response, err := http.DefaultClient.Do(req) + require.NoError(s.T(), err) + assert.Equal(s.T(), http.StatusOK, response.StatusCode) + _, _ = io.ReadAll(response.Body) + response.Body.Close() + } + + // Verify that the fast server received significantly more requests (>70%) + fastCalls := fastServerCalls.Load() + slowCalls := slowServerCalls.Load() + totalCalls := fastCalls + slowCalls + + assert.Equal(s.T(), int32(20), totalCalls) + + // Fast server should get >70% of traffic due to lower response time + fastPercentage := float64(fastCalls) / float64(totalCalls) * 100 + assert.Greater(s.T(), fastPercentage, 70.0) +} + func (s *SimpleSuite) TestWRR() { s.createComposeProject("base") @@ -1217,6 +1320,74 @@ func (s *SimpleSuite) TestMirrorCanceled() { assert.Equal(s.T(), int32(0), val2) } +func (s *SimpleSuite) TestHighestRandomWeight() { + var count1, count2, count3 int32 + + service1 := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + atomic.AddInt32(&count1, 1) + rw.WriteHeader(http.StatusOK) + })) + + service2 := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + atomic.AddInt32(&count2, 1) + rw.WriteHeader(http.StatusOK) + })) + + service3 := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + atomic.AddInt32(&count3, 1) + rw.WriteHeader(http.StatusOK) + })) + + service1Server := service1.URL + service2Server := service2.URL + service3Server := service3.URL + + file := s.adaptFile("fixtures/highest_random_weight.toml", struct { + Service1Server string + Service2Server string + Service3Server string + }{Service1Server: service1Server, Service2Server: service2Server, Service3Server: service3Server}) + + s.traefikCmd(withConfigFile(file)) + + err := try.GetRequest("http://127.0.0.1:8080/api/http/services", 3*time.Second, try.BodyContains("service1", "service2", "service3", "hrw")) + require.NoError(s.T(), err) + + // Make 10 requests from the same client (127.0.0.1) - should all go to the same service + client := &http.Client{} + for range 10 { + req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil) + require.NoError(s.T(), err) + + response, err := client.Do(req) + require.NoError(s.T(), err) + assert.Equal(s.T(), http.StatusOK, response.StatusCode) + response.Body.Close() + } + + // Check if all requests went to the same service + val1 := atomic.LoadInt32(&count1) + val2 := atomic.LoadInt32(&count2) + val3 := atomic.LoadInt32(&count3) + + // All requests should have been handled (total should be 10) + assert.Equal(s.T(), int32(10), val1+val2+val3) + + // All requests from same remoteAddr (127.0.0.1) should go to exactly one service + servicesUsed := 0 + if val1 > 0 { + servicesUsed++ + } + if val2 > 0 { + servicesUsed++ + } + if val3 > 0 { + servicesUsed++ + } + + assert.Equal(s.T(), 1, servicesUsed, "All requests from same remoteAddr should go to exactly one service") +} + func (s *SimpleSuite) TestSecureAPI() { s.createComposeProject("base") @@ -1524,16 +1695,15 @@ func (s *SimpleSuite) TestDenyFragment() { s.composeUp() defer s.composeDown() - s.traefikCmd(withConfigFile("fixtures/simple_default.toml")) + s.traefikCmd(withConfigFile(s.adaptFile("fixtures/simple_deny.toml", struct{}{}))) - // Expected a 404 as we did not configure anything - err := try.GetRequest("http://127.0.0.1:8000/", 1*time.Second, try.StatusCodeIs(http.StatusNotFound)) + err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`deny.localhost`)")) require.NoError(s.T(), err) conn, err := net.Dial("tcp", "127.0.0.1:8000") require.NoError(s.T(), err) - _, err = conn.Write([]byte("GET /#/?bar=toto;boo=titi HTTP/1.1\nHost: other.localhost\n\n")) + _, err = conn.Write([]byte("GET /#/?bar=toto;boo=titi HTTP/1.1\nHost: deny.localhost\n\n")) require.NoError(s.T(), err) resp, err := http.ReadResponse(bufio.NewReader(conn), nil) @@ -1922,3 +2092,84 @@ func (s *SimpleSuite) TestSanitizePathSyntaxV2() { } } } + +// TestEncodedCharactersDifferentEntryPoints verifies that router handler caching does not interfere with +// per-entry-point encoded characters configuration. +// The same router should behave differently on different entry points. +func (s *SimpleSuite) TestEncodedCharactersDifferentEntryPoints() { + s.createComposeProject("base") + + s.composeUp() + defer s.composeDown() + + whoami1URL := "http://" + net.JoinHostPort(s.getComposeServiceIP("whoami1"), "80") + + file := s.adaptFile("fixtures/simple_encoded_chars.toml", struct { + Server1 string + }{whoami1URL}) + + s.traefikCmd(withConfigFile(file)) + + err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 1*time.Second, try.BodyContains("Host(`test.localhost`)")) + require.NoError(s.T(), err) + + testCases := []struct { + desc string + request string + target string + expected int + }{ + { + desc: "Encoded slash should be REJECTED on strict entry point", + request: "GET /path%2Fwith%2Fslash HTTP/1.1\r\nHost: test.localhost\r\n\r\n", + target: "127.0.0.1:8000", // strict entry point + expected: http.StatusBadRequest, + }, + { + desc: "Encoded slash should be ALLOWED on permissive entry point", + request: "GET /path%2Fwith%2Fslash HTTP/1.1\r\nHost: test.localhost\r\n\r\n", + target: "127.0.0.1:8001", // permissive entry point + expected: http.StatusOK, + }, + { + desc: "Encoded slash should be ALLOWED on permissive2 entry point", + request: "GET /path%2Fwith%2Fslash HTTP/1.1\r\nHost: test.localhost\r\n\r\n", + target: "127.0.0.1:8002", // permissive2 entry point + expected: http.StatusOK, + }, + { + desc: "Regular path should work on strict entry point", + request: "GET /regular/path HTTP/1.1\r\nHost: test.localhost\r\n\r\n", + target: "127.0.0.1:8000", + expected: http.StatusOK, + }, + { + desc: "Regular path should work on permissive entry point", + request: "GET /regular/path HTTP/1.1\r\nHost: test.localhost\r\n\r\n", + target: "127.0.0.1:8001", + expected: http.StatusOK, + }, + { + desc: "Regular path should work on permissive2 entry point", + request: "GET /regular/path HTTP/1.1\r\nHost: test.localhost\r\n\r\n", + target: "127.0.0.1:8002", + expected: http.StatusOK, + }, + } + + for _, test := range testCases { + conn, err := net.Dial("tcp", test.target) + require.NoError(s.T(), err) + + _, err = conn.Write([]byte(test.request)) + require.NoError(s.T(), err) + + resp, err := http.ReadResponse(bufio.NewReader(conn), nil) + require.NoError(s.T(), err) + + assert.Equalf(s.T(), test.expected, resp.StatusCode, "%s failed with %d instead of %d", test.desc, resp.StatusCode, test.expected) + + err = conn.Close() + require.NoError(s.T(), err) + } +} diff --git a/integration/tcp_healthcheck_test.go b/integration/tcp_healthcheck_test.go new file mode 100644 index 000000000..d79fb610b --- /dev/null +++ b/integration/tcp_healthcheck_test.go @@ -0,0 +1,114 @@ +package integration + +import ( + "net" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "github.com/traefik/traefik/v3/integration/try" +) + +// TCPHealthCheckSuite test suite for TCP health checks. +type TCPHealthCheckSuite struct { + BaseSuite + whoamitcp1IP string + whoamitcp2IP string +} + +func TestTCPHealthCheckSuite(t *testing.T) { + suite.Run(t, new(TCPHealthCheckSuite)) +} + +func (s *TCPHealthCheckSuite) SetupSuite() { + s.BaseSuite.SetupSuite() + + s.createComposeProject("tcp_healthcheck") + s.composeUp() + + s.whoamitcp1IP = s.getComposeServiceIP("whoamitcp1") + s.whoamitcp2IP = s.getComposeServiceIP("whoamitcp2") +} + +func (s *TCPHealthCheckSuite) TearDownSuite() { + s.BaseSuite.TearDownSuite() +} + +func (s *TCPHealthCheckSuite) TestSimpleConfiguration() { + file := s.adaptFile("fixtures/tcp_healthcheck/simple.toml", struct { + Server1 string + Server2 string + }{s.whoamitcp1IP, s.whoamitcp2IP}) + + s.traefikCmd(withConfigFile(file)) + + // Wait for Traefik. + err := try.GetRequest("http://127.0.0.1:8080/api/rawdata", 60*time.Second, try.BodyContains("HostSNI(`*`)")) + require.NoError(s.T(), err) + + // Test that we can consistently reach servers through load balancing. + var ( + successfulConnectionsWhoamitcp1 int + successfulConnectionsWhoamitcp2 int + ) + for range 4 { + out := s.whoIs("127.0.0.1:8093") + require.NoError(s.T(), err) + + if strings.Contains(out, "whoamitcp1") { + successfulConnectionsWhoamitcp1++ + } + if strings.Contains(out, "whoamitcp2") { + successfulConnectionsWhoamitcp2++ + } + } + + assert.Equal(s.T(), 2, successfulConnectionsWhoamitcp1) + assert.Equal(s.T(), 2, successfulConnectionsWhoamitcp2) + + // Stop one whoamitcp2 containers to simulate health check failure. + conn, err := net.DialTimeout("tcp", s.whoamitcp2IP+":8080", time.Second) + require.NoError(s.T(), err) + + s.T().Cleanup(func() { + _ = conn.Close() + }) + + s.composeStop("whoamitcp2") + + // Wait for the health check to detect the failure. + time.Sleep(1 * time.Second) + + // Verify that the remaining server still responds. + for range 3 { + out := s.whoIs("127.0.0.1:8093") + require.NoError(s.T(), err) + assert.Contains(s.T(), out, "whoamitcp1") + } +} + +// connectTCP connects to the given TCP address and returns the response. +func (s *TCPHealthCheckSuite) whoIs(addr string) string { + s.T().Helper() + + conn, err := net.DialTimeout("tcp", addr, time.Second) + require.NoError(s.T(), err) + + s.T().Cleanup(func() { + _ = conn.Close() + }) + + _, err = conn.Write([]byte("WHO")) + require.NoError(s.T(), err) + + _ = conn.SetReadDeadline(time.Now().Add(2 * time.Second)) + + buffer := make([]byte, 1024) + n, err := conn.Read(buffer) + require.NoError(s.T(), err) + + return string(buffer[:n]) +} diff --git a/integration/testdata/rawdata-crd.json b/integration/testdata/rawdata-crd.json index 9f1b2d33c..ece3a7dd3 100644 --- a/integration/testdata/rawdata-crd.json +++ b/integration/testdata/rawdata-crd.json @@ -78,6 +78,24 @@ "web" ] }, + "default-test4-route-466d8d3a547de55dfe8a@kubernetescrd": { + "entryPoints": [ + "web" + ], + "service": "default-hrw1", + "rule": "Host(`foo.com`) \u0026\u0026 PathPrefix(`/hrw1`)", + "priority": 38, + "observability": { + "accessLogs": true, + "metrics": true, + "tracing": true, + "traceVerbosity": "minimal" + }, + "status": "enabled", + "using": [ + "web" + ] + }, "default-testst-route-60ad45fcb5fc1f5f3629@kubernetescrd": { "entryPoints": [ "web" @@ -157,6 +175,24 @@ "dashboard@internal": { "status": "enabled" }, + "default-hrw1@kubernetescrd": { + "highestRandomWeight": { + "services": [ + { + "name": "default-whoami-80", + "weight": 10 + }, + { + "name": "default-whoami-80", + "weight": 20 + } + ] + }, + "status": "enabled", + "usedBy": [ + "default-test4-route-466d8d3a547de55dfe8a@kubernetescrd" + ] + }, "default-mirror1@kubernetescrd": { "mirroring": { "service": "default-whoami-80", @@ -326,7 +362,10 @@ } ] }, - "status": "enabled" + "status": "enabled", + "serverStatus": { + "domain.com:9090": "UP" + } }, "default-test3.route-673acf455cb2dab0b43a-whoamitcp-8080@kubernetescrd": { "loadBalancer": { @@ -339,7 +378,11 @@ } ] }, - "status": "enabled" + "status": "enabled", + "serverStatus": { + "10.42.0.2:8080": "UP", + "10.42.0.6:8080": "UP" + } }, "default-test3.route-673acf455cb2dab0b43a@kubernetescrd": { "weighted": { diff --git a/integration/testdata/rawdata-gateway.json b/integration/testdata/rawdata-gateway.json index 8a6e1bc8c..0b1f9fc20 100644 --- a/integration/testdata/rawdata-gateway.json +++ b/integration/testdata/rawdata-gateway.json @@ -233,7 +233,11 @@ } ] }, - "status": "enabled" + "status": "enabled", + "serverStatus": { + "10.42.0.2:8080": "UP", + "10.42.0.6:8080": "UP" + } }, "tcproute-default-tcp-app-1-gw-default-my-tcp-gateway-ep-footcp-0-e3b0c44298fc1c149afb-wrr@kubernetesgateway": { "weighted": { diff --git a/pkg/api/dashboard/dashboard.go b/pkg/api/dashboard/dashboard.go index b44a14bba..dd8c335b5 100644 --- a/pkg/api/dashboard/dashboard.go +++ b/pkg/api/dashboard/dashboard.go @@ -79,7 +79,13 @@ func Append(router *mux.Router, basePath string, customAssets fs.FS) error { router.Methods(http.MethodGet). Path(basePath). HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - prefix := strings.TrimSuffix(req.Header.Get("X-Forwarded-Prefix"), "/") + xfPrefix := req.Header.Get("X-Forwarded-Prefix") + if strings.Contains(xfPrefix, "//") { + log.Error().Msgf("X-Forwarded-Prefix contains an invalid value: %s, defaulting to empty prefix", xfPrefix) + xfPrefix = "" + } + + prefix := strings.TrimSuffix(xfPrefix, "/") http.Redirect(resp, req, prefix+dashboardPath, http.StatusFound) }) diff --git a/pkg/api/dashboard/dashboard_test.go b/pkg/api/dashboard/dashboard_test.go index b07bff4f6..f7d387e0c 100644 --- a/pkg/api/dashboard/dashboard_test.go +++ b/pkg/api/dashboard/dashboard_test.go @@ -9,7 +9,9 @@ import ( "testing/fstest" "time" + "github.com/gorilla/mux" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_ContentSecurityPolicy(t *testing.T) { @@ -60,6 +62,52 @@ func Test_ContentSecurityPolicy(t *testing.T) { } } +func Test_XForwardedPrefix(t *testing.T) { + testCases := []struct { + desc string + prefix string + expected string + }{ + { + desc: "location in X-Forwarded-Prefix", + prefix: "//foobar/test", + expected: "/dashboard/", + }, + { + desc: "scheme in X-Forwarded-Prefix", + prefix: "http://foobar", + expected: "/dashboard/", + }, + { + desc: "path in X-Forwarded-Prefix", + prefix: "foobar", + expected: "/foobar/dashboard/", + }, + } + + router := mux.NewRouter() + err := Append(router, "/", fstest.MapFS{"index.html": &fstest.MapFile{ + Mode: 0o755, + ModTime: time.Now(), + }}) + require.NoError(t, err) + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + req := httptest.NewRequest(http.MethodGet, "/", http.NoBody) + req.Header.Set("X-Forwarded-Prefix", test.prefix) + rw := httptest.NewRecorder() + + router.ServeHTTP(rw, req) + + assert.Equal(t, http.StatusFound, rw.Code) + assert.Equal(t, test.expected, rw.Result().Header.Get("Location")) + }) + } +} + type errorFS struct{} func (e errorFS) Open(name string) (fs.File, error) { diff --git a/pkg/api/handler.go b/pkg/api/handler.go index 6c99ca12e..6a2f82270 100644 --- a/pkg/api/handler.go +++ b/pkg/api/handler.go @@ -33,16 +33,53 @@ type serviceInfoRepresentation struct { ServerStatus map[string]string `json:"serverStatus,omitempty"` } +type tcpServiceInfoRepresentation struct { + *runtime.TCPServiceInfo + ServerStatus map[string]string `json:"serverStatus,omitempty"` +} + // RunTimeRepresentation is the configuration information exposed by the API handler. type RunTimeRepresentation struct { - Routers map[string]*runtime.RouterInfo `json:"routers,omitempty"` - Middlewares map[string]*runtime.MiddlewareInfo `json:"middlewares,omitempty"` - Services map[string]*serviceInfoRepresentation `json:"services,omitempty"` - TCPRouters map[string]*runtime.TCPRouterInfo `json:"tcpRouters,omitempty"` - TCPMiddlewares map[string]*runtime.TCPMiddlewareInfo `json:"tcpMiddlewares,omitempty"` - TCPServices map[string]*runtime.TCPServiceInfo `json:"tcpServices,omitempty"` - UDPRouters map[string]*runtime.UDPRouterInfo `json:"udpRouters,omitempty"` - UDPServices map[string]*runtime.UDPServiceInfo `json:"udpServices,omitempty"` + Routers map[string]*runtime.RouterInfo `json:"routers,omitempty"` + Middlewares map[string]*runtime.MiddlewareInfo `json:"middlewares,omitempty"` + Services map[string]*serviceInfoRepresentation `json:"services,omitempty"` + TCPRouters map[string]*runtime.TCPRouterInfo `json:"tcpRouters,omitempty"` + TCPMiddlewares map[string]*runtime.TCPMiddlewareInfo `json:"tcpMiddlewares,omitempty"` + TCPServices map[string]*tcpServiceInfoRepresentation `json:"tcpServices,omitempty"` + UDPRouters map[string]*runtime.UDPRouterInfo `json:"udpRouters,omitempty"` + UDPServices map[string]*runtime.UDPServiceInfo `json:"udpServices,omitempty"` +} + +// TODO: think about this for longer +func GetRunTimeRepresentation(runtimeConfiguration *runtime.Configuration) *RunTimeRepresentation { + siRepr := make(map[string]*serviceInfoRepresentation, len(runtimeConfiguration.Services)) + for k, v := range runtimeConfiguration.Services { + siRepr[k] = &serviceInfoRepresentation{ + ServiceInfo: v, + ServerStatus: v.GetAllStatus(), + } + } + + tcpSIRepr := make(map[string]*tcpServiceInfoRepresentation, len(h.runtimeConfiguration.Services)) + for k, v := range h.runtimeConfiguration.TCPServices { + tcpSIRepr[k] = &tcpServiceInfoRepresentation{ + TCPServiceInfo: v, + ServerStatus: v.GetAllStatus(), + } + } + + result := RunTimeRepresentation{ + Routers: runtimeConfiguration.Routers, + Middlewares: runtimeConfiguration.Middlewares, + Services: siRepr, + TCPRouters: runtimeConfiguration.TCPRouters, + TCPMiddlewares: runtimeConfiguration.TCPMiddlewares, + TCPServices: tcpSIRepr, + UDPRouters: runtimeConfiguration.UDPRouters, + UDPServices: runtimeConfiguration.UDPServices, + } + + return &result } // Handler serves the configuration and status of Traefik on API endpoints. @@ -119,24 +156,7 @@ func (h Handler) createRouter() *mux.Router { } func (h Handler) getRuntimeConfiguration(rw http.ResponseWriter, request *http.Request) { - siRepr := make(map[string]*serviceInfoRepresentation, len(h.runtimeConfiguration.Services)) - for k, v := range h.runtimeConfiguration.Services { - siRepr[k] = &serviceInfoRepresentation{ - ServiceInfo: v, - ServerStatus: v.GetAllStatus(), - } - } - - result := RunTimeRepresentation{ - Routers: h.runtimeConfiguration.Routers, - Middlewares: h.runtimeConfiguration.Middlewares, - Services: siRepr, - TCPRouters: h.runtimeConfiguration.TCPRouters, - TCPMiddlewares: h.runtimeConfiguration.TCPMiddlewares, - TCPServices: h.runtimeConfiguration.TCPServices, - UDPRouters: h.runtimeConfiguration.UDPRouters, - UDPServices: h.runtimeConfiguration.UDPServices, - } + result := GetRunTimeRepresentation(h.runtimeConfiguration) rw.Header().Set("Content-Type", "application/json") diff --git a/pkg/api/handler_http.go b/pkg/api/handler_http.go index 9439d8ab8..a0ca84ce7 100644 --- a/pkg/api/handler_http.go +++ b/pkg/api/handler_http.go @@ -34,10 +34,10 @@ func newRouterRepresentation(name string, rt *runtime.RouterInfo) routerRepresen type serviceRepresentation struct { *runtime.ServiceInfo - ServerStatus map[string]string `json:"serverStatus,omitempty"` Name string `json:"name,omitempty"` Provider string `json:"provider,omitempty"` Type string `json:"type,omitempty"` + ServerStatus map[string]string `json:"serverStatus,omitempty"` } func newServiceRepresentation(name string, si *runtime.ServiceInfo) serviceRepresentation { @@ -45,8 +45,8 @@ func newServiceRepresentation(name string, si *runtime.ServiceInfo) serviceRepre ServiceInfo: si, Name: name, Provider: getProviderName(name), - ServerStatus: si.GetAllStatus(), Type: strings.ToLower(extractType(si.Service)), + ServerStatus: si.GetAllStatus(), } } diff --git a/pkg/api/handler_support_dump_test.go b/pkg/api/handler_support_dump_test.go index f1c4d507a..8de6e766a 100644 --- a/pkg/api/handler_support_dump_test.go +++ b/pkg/api/handler_support_dump_test.go @@ -76,7 +76,7 @@ func TestHandler_SupportDump(t *testing.T) { assert.Contains(t, string(files["version.json"]), `"version":"dev"`) // Verify static config contains entry points - assert.Contains(t, string(files["static-config.json"]), `"entryPoints":{"web":{"address":"xxxx","http":{}}}`) + assert.Contains(t, string(files["static-config.json"]), `"entryPoints":{"web":{"address":"xxxx","http":{}`) // Verify runtime config contains services assert.Contains(t, string(files["runtime-config.json"]), `"services":`) diff --git a/pkg/api/handler_tcp.go b/pkg/api/handler_tcp.go index 3ad0fb5b7..cc8f9aeed 100644 --- a/pkg/api/handler_tcp.go +++ b/pkg/api/handler_tcp.go @@ -29,9 +29,10 @@ func newTCPRouterRepresentation(name string, rt *runtime.TCPRouterInfo) tcpRoute type tcpServiceRepresentation struct { *runtime.TCPServiceInfo - Name string `json:"name,omitempty"` - Provider string `json:"provider,omitempty"` - Type string `json:"type,omitempty"` + Name string `json:"name,omitempty"` + Provider string `json:"provider,omitempty"` + Type string `json:"type,omitempty"` + ServerStatus map[string]string `json:"serverStatus,omitempty"` } func newTCPServiceRepresentation(name string, si *runtime.TCPServiceInfo) tcpServiceRepresentation { @@ -40,6 +41,7 @@ func newTCPServiceRepresentation(name string, si *runtime.TCPServiceInfo) tcpSer Name: name, Provider: getProviderName(name), Type: strings.ToLower(extractType(si.TCPService)), + ServerStatus: si.GetAllStatus(), } } diff --git a/pkg/api/handler_tcp_test.go b/pkg/api/handler_tcp_test.go index 387c39050..b0ee4cc2f 100644 --- a/pkg/api/handler_tcp_test.go +++ b/pkg/api/handler_tcp_test.go @@ -355,45 +355,57 @@ func TestHandler_TCP(t *testing.T) { path: "/api/tcp/services", conf: runtime.Configuration{ TCPServices: map[string]*runtime.TCPServiceInfo{ - "bar@myprovider": { - TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "127.0.0.1:2345", + "bar@myprovider": func() *runtime.TCPServiceInfo { + si := &runtime.TCPServiceInfo{ + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.1:2345", + }, }, }, }, - }, - UsedBy: []string{"foo@myprovider", "test@myprovider"}, - Status: runtime.StatusEnabled, - }, - "baz@myprovider": { - TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "127.0.0.2:2345", + UsedBy: []string{"foo@myprovider", "test@myprovider"}, + Status: runtime.StatusEnabled, + } + si.UpdateServerStatus("127.0.0.1:2345", "UP") + return si + }(), + "baz@myprovider": func() *runtime.TCPServiceInfo { + si := &runtime.TCPServiceInfo{ + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.2:2345", + }, }, }, }, - }, - UsedBy: []string{"foo@myprovider"}, - Status: runtime.StatusWarning, - }, - "foz@myprovider": { - TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "127.0.0.2:2345", + UsedBy: []string{"foo@myprovider"}, + Status: runtime.StatusWarning, + } + si.UpdateServerStatus("127.0.0.2:2345", "UP") + return si + }(), + "foz@myprovider": func() *runtime.TCPServiceInfo { + si := &runtime.TCPServiceInfo{ + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.3:2345", + }, }, }, }, - }, - UsedBy: []string{"foo@myprovider"}, - Status: runtime.StatusDisabled, - }, + UsedBy: []string{"foo@myprovider"}, + Status: runtime.StatusDisabled, + } + si.UpdateServerStatus("127.0.0.3:2345", "UP") + return si + }(), }, }, expected: expected{ @@ -407,45 +419,57 @@ func TestHandler_TCP(t *testing.T) { path: "/api/tcp/services?status=enabled", conf: runtime.Configuration{ TCPServices: map[string]*runtime.TCPServiceInfo{ - "bar@myprovider": { - TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "127.0.0.1:2345", + "bar@myprovider": func() *runtime.TCPServiceInfo { + si := &runtime.TCPServiceInfo{ + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.1:2345", + }, }, }, }, - }, - UsedBy: []string{"foo@myprovider", "test@myprovider"}, - Status: runtime.StatusEnabled, - }, - "baz@myprovider": { - TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "127.0.0.2:2345", + UsedBy: []string{"foo@myprovider", "test@myprovider"}, + Status: runtime.StatusEnabled, + } + si.UpdateServerStatus("127.0.0.1:2345", "UP") + return si + }(), + "baz@myprovider": func() *runtime.TCPServiceInfo { + si := &runtime.TCPServiceInfo{ + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.2:2345", + }, }, }, }, - }, - UsedBy: []string{"foo@myprovider"}, - Status: runtime.StatusWarning, - }, - "foz@myprovider": { - TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "127.0.0.2:2345", + UsedBy: []string{"foo@myprovider"}, + Status: runtime.StatusWarning, + } + si.UpdateServerStatus("127.0.0.2:2345", "UP") + return si + }(), + "foz@myprovider": func() *runtime.TCPServiceInfo { + si := &runtime.TCPServiceInfo{ + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.3:2345", + }, }, }, }, - }, - UsedBy: []string{"foo@myprovider"}, - Status: runtime.StatusDisabled, - }, + UsedBy: []string{"foo@myprovider"}, + Status: runtime.StatusDisabled, + } + si.UpdateServerStatus("127.0.0.3:2345", "UP") + return si + }(), }, }, expected: expected{ @@ -459,45 +483,57 @@ func TestHandler_TCP(t *testing.T) { path: "/api/tcp/services?search=baz@my", conf: runtime.Configuration{ TCPServices: map[string]*runtime.TCPServiceInfo{ - "bar@myprovider": { - TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "127.0.0.1:2345", + "bar@myprovider": func() *runtime.TCPServiceInfo { + si := &runtime.TCPServiceInfo{ + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.1:2345", + }, }, }, }, - }, - UsedBy: []string{"foo@myprovider", "test@myprovider"}, - Status: runtime.StatusEnabled, - }, - "baz@myprovider": { - TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "127.0.0.2:2345", + UsedBy: []string{"foo@myprovider", "test@myprovider"}, + Status: runtime.StatusEnabled, + } + si.UpdateServerStatus("127.0.0.1:2345", "UP") + return si + }(), + "baz@myprovider": func() *runtime.TCPServiceInfo { + si := &runtime.TCPServiceInfo{ + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.2:2345", + }, }, }, }, - }, - UsedBy: []string{"foo@myprovider"}, - Status: runtime.StatusWarning, - }, - "foz@myprovider": { - TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "127.0.0.2:2345", + UsedBy: []string{"foo@myprovider"}, + Status: runtime.StatusWarning, + } + si.UpdateServerStatus("127.0.0.2:2345", "UP") + return si + }(), + "foz@myprovider": func() *runtime.TCPServiceInfo { + si := &runtime.TCPServiceInfo{ + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.3:2345", + }, }, }, }, - }, - UsedBy: []string{"foo@myprovider"}, - Status: runtime.StatusDisabled, - }, + UsedBy: []string{"foo@myprovider"}, + Status: runtime.StatusDisabled, + } + si.UpdateServerStatus("127.0.0.3:2345", "UP") + return si + }(), }, }, expected: expected{ @@ -511,41 +547,53 @@ func TestHandler_TCP(t *testing.T) { path: "/api/tcp/services?page=2&per_page=1", conf: runtime.Configuration{ TCPServices: map[string]*runtime.TCPServiceInfo{ - "bar@myprovider": { - TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "127.0.0.1:2345", + "bar@myprovider": func() *runtime.TCPServiceInfo { + si := &runtime.TCPServiceInfo{ + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.1:2345", + }, }, }, }, - }, - UsedBy: []string{"foo@myprovider", "test@myprovider"}, - }, - "baz@myprovider": { - TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "127.0.0.2:2345", + UsedBy: []string{"foo@myprovider", "test@myprovider"}, + } + si.UpdateServerStatus("127.0.0.1:2345", "UP") + return si + }(), + "baz@myprovider": func() *runtime.TCPServiceInfo { + si := &runtime.TCPServiceInfo{ + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.2:2345", + }, }, }, }, - }, - UsedBy: []string{"foo@myprovider"}, - }, - "test@myprovider": { - TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "127.0.0.3:2345", + UsedBy: []string{"foo@myprovider"}, + } + si.UpdateServerStatus("127.0.0.2:2345", "UP") + return si + }(), + "test@myprovider": func() *runtime.TCPServiceInfo { + si := &runtime.TCPServiceInfo{ + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.3:2345", + }, }, }, }, - }, - }, + } + si.UpdateServerStatus("127.0.0.3:2345", "UP") + return si + }(), }, }, expected: expected{ @@ -559,18 +607,22 @@ func TestHandler_TCP(t *testing.T) { path: "/api/tcp/services/bar@myprovider", conf: runtime.Configuration{ TCPServices: map[string]*runtime.TCPServiceInfo{ - "bar@myprovider": { - TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "127.0.0.1:2345", + "bar@myprovider": func() *runtime.TCPServiceInfo { + si := &runtime.TCPServiceInfo{ + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.1:2345", + }, }, }, }, - }, - UsedBy: []string{"foo@myprovider", "test@myprovider"}, - }, + UsedBy: []string{"foo@myprovider", "test@myprovider"}, + } + si.UpdateServerStatus("127.0.0.1:2345", "UP") + return si + }(), }, }, expected: expected{ @@ -583,18 +635,22 @@ func TestHandler_TCP(t *testing.T) { path: "/api/tcp/services/" + url.PathEscape("foo / bar@myprovider"), conf: runtime.Configuration{ TCPServices: map[string]*runtime.TCPServiceInfo{ - "foo / bar@myprovider": { - TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "127.0.0.1:2345", + "foo / bar@myprovider": func() *runtime.TCPServiceInfo { + si := &runtime.TCPServiceInfo{ + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.1:2345", + }, }, }, }, - }, - UsedBy: []string{"foo@myprovider", "test@myprovider"}, - }, + UsedBy: []string{"foo@myprovider", "test@myprovider"}, + } + si.UpdateServerStatus("127.0.0.1:2345", "UP") + return si + }(), }, }, expected: expected{ @@ -607,18 +663,22 @@ func TestHandler_TCP(t *testing.T) { path: "/api/tcp/services/nono@myprovider", conf: runtime.Configuration{ TCPServices: map[string]*runtime.TCPServiceInfo{ - "bar@myprovider": { - TCPService: &dynamic.TCPService{ - LoadBalancer: &dynamic.TCPServersLoadBalancer{ - Servers: []dynamic.TCPServer{ - { - Address: "127.0.0.1:2345", + "bar@myprovider": func() *runtime.TCPServiceInfo { + si := &runtime.TCPServiceInfo{ + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "127.0.0.1:2345", + }, }, }, }, - }, - UsedBy: []string{"foo@myprovider", "test@myprovider"}, - }, + UsedBy: []string{"foo@myprovider", "test@myprovider"}, + } + si.UpdateServerStatus("127.0.0.1:2345", "UP") + return si + }(), }, }, expected: expected{ diff --git a/pkg/api/testdata/entrypoint-bar.json b/pkg/api/testdata/entrypoint-bar.json index 897b16e00..e68aa5d99 100644 --- a/pkg/api/testdata/entrypoint-bar.json +++ b/pkg/api/testdata/entrypoint-bar.json @@ -2,4 +2,4 @@ "address": ":81", "http": {}, "name": "bar" -} \ No newline at end of file +} diff --git a/pkg/api/testdata/entrypoints-many-lastpage.json b/pkg/api/testdata/entrypoints-many-lastpage.json index 3e0f438e5..4d2917405 100644 --- a/pkg/api/testdata/entrypoints-many-lastpage.json +++ b/pkg/api/testdata/entrypoints-many-lastpage.json @@ -24,4 +24,4 @@ "http": {}, "name": "ep18" } -] \ No newline at end of file +] diff --git a/pkg/api/testdata/entrypoints-page2.json b/pkg/api/testdata/entrypoints-page2.json index 2d674dc6d..b98ced751 100644 --- a/pkg/api/testdata/entrypoints-page2.json +++ b/pkg/api/testdata/entrypoints-page2.json @@ -4,4 +4,4 @@ "http": {}, "name": "web2" } -] \ No newline at end of file +] diff --git a/pkg/api/testdata/tcpservice-bar.json b/pkg/api/testdata/tcpservice-bar.json index ade480a92..1c0476407 100644 --- a/pkg/api/testdata/tcpservice-bar.json +++ b/pkg/api/testdata/tcpservice-bar.json @@ -8,6 +8,9 @@ }, "name": "bar@myprovider", "provider": "myprovider", + "serverStatus": { + "127.0.0.1:2345": "UP" + }, "status": "enabled", "type": "loadbalancer", "usedBy": [ diff --git a/pkg/api/testdata/tcpservice-foo-slash-bar.json b/pkg/api/testdata/tcpservice-foo-slash-bar.json index b250966d5..ee36ad8b8 100644 --- a/pkg/api/testdata/tcpservice-foo-slash-bar.json +++ b/pkg/api/testdata/tcpservice-foo-slash-bar.json @@ -8,6 +8,9 @@ }, "name": "foo / bar@myprovider", "provider": "myprovider", + "serverStatus": { + "127.0.0.1:2345": "UP" + }, "status": "enabled", "type": "loadbalancer", "usedBy": [ diff --git a/pkg/api/testdata/tcpservices-filtered-search.json b/pkg/api/testdata/tcpservices-filtered-search.json index 130d5eace..653cfb884 100644 --- a/pkg/api/testdata/tcpservices-filtered-search.json +++ b/pkg/api/testdata/tcpservices-filtered-search.json @@ -9,6 +9,9 @@ }, "name": "baz@myprovider", "provider": "myprovider", + "serverStatus": { + "127.0.0.2:2345": "UP" + }, "status": "warning", "type": "loadbalancer", "usedBy": [ diff --git a/pkg/api/testdata/tcpservices-filtered-status.json b/pkg/api/testdata/tcpservices-filtered-status.json index 03ec085a0..61fe14b45 100644 --- a/pkg/api/testdata/tcpservices-filtered-status.json +++ b/pkg/api/testdata/tcpservices-filtered-status.json @@ -9,6 +9,9 @@ }, "name": "bar@myprovider", "provider": "myprovider", + "serverStatus": { + "127.0.0.1:2345": "UP" + }, "status": "enabled", "type": "loadbalancer", "usedBy": [ diff --git a/pkg/api/testdata/tcpservices-page2.json b/pkg/api/testdata/tcpservices-page2.json index 414e0f37d..ff3025fc7 100644 --- a/pkg/api/testdata/tcpservices-page2.json +++ b/pkg/api/testdata/tcpservices-page2.json @@ -9,6 +9,9 @@ }, "name": "baz@myprovider", "provider": "myprovider", + "serverStatus": { + "127.0.0.2:2345": "UP" + }, "status": "enabled", "type": "loadbalancer", "usedBy": [ diff --git a/pkg/api/testdata/tcpservices.json b/pkg/api/testdata/tcpservices.json index c3f9f7ea6..e741f119e 100644 --- a/pkg/api/testdata/tcpservices.json +++ b/pkg/api/testdata/tcpservices.json @@ -9,6 +9,9 @@ }, "name": "bar@myprovider", "provider": "myprovider", + "serverStatus": { + "127.0.0.1:2345": "UP" + }, "status": "enabled", "type": "loadbalancer", "usedBy": [ @@ -26,6 +29,9 @@ }, "name": "baz@myprovider", "provider": "myprovider", + "serverStatus": { + "127.0.0.2:2345": "UP" + }, "status": "warning", "type": "loadbalancer", "usedBy": [ @@ -36,12 +42,15 @@ "loadBalancer": { "servers": [ { - "address": "127.0.0.2:2345" + "address": "127.0.0.3:2345" } ] }, "name": "foz@myprovider", "provider": "myprovider", + "serverStatus": { + "127.0.0.3:2345": "UP" + }, "status": "disabled", "type": "loadbalancer", "usedBy": [ diff --git a/pkg/cli/deprecation.go b/pkg/cli/deprecation.go index c7c262ba7..1277e52c6 100644 --- a/pkg/cli/deprecation.go +++ b/pkg/cli/deprecation.go @@ -2,31 +2,39 @@ package cli import ( "errors" + "fmt" "os" "reflect" + "strconv" "strings" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "github.com/traefik/paerser/cli" + "github.com/traefik/paerser/env" + "github.com/traefik/paerser/file" "github.com/traefik/paerser/flag" "github.com/traefik/paerser/parser" ) type DeprecationLoader struct{} -func (d DeprecationLoader) Load(args []string, cmd *cli.Command) (bool, error) { - if logDeprecation(cmd.Configuration, args) { - return true, errors.New("incompatible deprecated static option found") +func (d DeprecationLoader) Load(args []string, _ *cli.Command) (bool, error) { + hasIncompatibleOptions, err := logDeprecations(args) + if err != nil { + log.Error().Err(err).Msg("Deprecated install configuration options analysis failed") + return false, nil } + if hasIncompatibleOptions { + return true, errors.New("incompatible deprecated install configuration option found") + } return false, nil } -// logDeprecation prints deprecation hints and returns whether incompatible deprecated options need to be removed. -func logDeprecation(traefikConfiguration interface{}, arguments []string) bool { - // This part doesn't handle properly a flag defined like this: - // --accesslog true +// logDeprecations prints deprecation hints and returns whether incompatible deprecated options need to be removed. +func logDeprecations(arguments []string) (bool, error) { + // This part doesn't handle properly a flag defined like this: --accesslog true // where `true` could be considered as a new argument. // This is not really an issue with the deprecation loader since it will filter the unknown nodes later in this function. var args []string @@ -35,86 +43,153 @@ func logDeprecation(traefikConfiguration interface{}, arguments []string) bool { args = append(args, arg+"=true") continue } - args = append(args, arg) } - labels, err := flag.Parse(args, nil) + // ARGS + // Parse arguments to labels. + argsLabels, err := flag.Parse(args, nil) if err != nil { - log.Error().Err(err).Msg("deprecated static options analysis failed") - return false + return false, fmt.Errorf("parsing arguments to labels: %w", err) } - node, err := parser.DecodeToNode(labels, "traefik") + config, err := parseDeprecatedConfig(argsLabels) if err != nil { - log.Error().Err(err).Msg("deprecated static options analysis failed") - return false + return false, fmt.Errorf("parsing deprecated config from args: %w", err) } - if node != nil && len(node.Children) > 0 { - config := &configuration{} - filterUnknownNodes(reflect.TypeOf(config), node) - - if len(node.Children) > 0 { - // Telling parser to look for the label struct tag to allow empty values. - err = parser.AddMetadata(config, node, parser.MetadataOpts{TagName: "label"}) - if err != nil { - log.Error().Err(err).Msg("deprecated static options analysis failed") - return false - } - - err = parser.Fill(config, node, parser.FillerOpts{}) - if err != nil { - log.Error().Err(err).Msg("deprecated static options analysis failed") - return false - } - - if config.deprecationNotice(log.With().Str("loader", "FLAG").Logger()) { - return true - } - - // No further deprecation parsing and logging, - // as args configuration contains at least one deprecated option. - return false - } + if config.deprecationNotice(log.With().Str("loader", "args").Logger()) { + return true, nil } // FILE - ref, err := flag.Parse(args, traefikConfiguration) + // Find the config file using the same logic as the normal file loader. + finder := cli.Finder{ + BasePaths: []string{"/etc/traefik/traefik", "$XDG_CONFIG_HOME/traefik", "$HOME/.config/traefik", "./traefik"}, + Extensions: []string{"toml", "yaml", "yml"}, + } + + configFile, ok := argsLabels["traefik.configfile"] + if !ok { + configFile = argsLabels["traefik.configFile"] + } + + filePath, err := finder.Find(configFile) if err != nil { - log.Error().Err(err).Msg("deprecated static options analysis failed") - return false + return false, fmt.Errorf("finding configuration file: %w", err) } - configFileFlag := "traefik.configfile" - if _, ok := ref["traefik.configFile"]; ok { - configFileFlag = "traefik.configFile" - } + if filePath != "" { + // We don't rely on the Parser file loader here to avoid issues with unknown fields. + // Parse file content into a generic map. + var fileConfig map[string]interface{} + if err := file.Decode(filePath, &fileConfig); err != nil { + return false, fmt.Errorf("decoding configuration file %s: %w", filePath, err) + } - config := &configuration{} - _, err = loadConfigFiles(ref[configFileFlag], config) + // Convert the file config to label format. + fileLabels := make(map[string]string) + flattenToLabels(fileConfig, "", fileLabels) - if err == nil { - if config.deprecationNotice(log.With().Str("loader", "FILE").Logger()) { - return true + config, err := parseDeprecatedConfig(fileLabels) + if err != nil { + return false, fmt.Errorf("parsing deprecated config from file: %w", err) + } + + if config.deprecationNotice(log.With().Str("loader", "file").Logger()) { + return true, nil } } - config = &configuration{} - l := EnvLoader{} - _, err = l.Load(os.Args, &cli.Command{ - Configuration: config, - }) + // ENV + vars := env.FindPrefixedEnvVars(os.Environ(), env.DefaultNamePrefix, &configuration{}) + if len(vars) > 0 { + // We don't rely on the Parser env loader here to avoid issues with unknown fields. + // Decode environment variables to a generic map. + var envConfig map[string]interface{} + if err := env.Decode(vars, env.DefaultNamePrefix, &envConfig); err != nil { + return false, fmt.Errorf("decoding environment variables: %w", err) + } - if err == nil { - if config.deprecationNotice(log.With().Str("loader", "ENV").Logger()) { - return true + // Convert the env config to label format. + envLabels := make(map[string]string) + flattenToLabels(envConfig, "", envLabels) + + config, err := parseDeprecatedConfig(envLabels) + if err != nil { + return false, fmt.Errorf("parsing deprecated config from environment variables: %w", err) + } + + if config.deprecationNotice(log.With().Str("loader", "env").Logger()) { + return true, nil } } - return false + return false, nil } +// flattenToLabels recursively flattens a nested map into label key-value pairs. +// Example: {"experimental": {"http3": true}} -> {"traefik.experimental.http3": "true"}. +func flattenToLabels(config interface{}, currKey string, labels map[string]string) { + switch v := config.(type) { + case map[string]interface{}: + for key, value := range v { + newKey := key + if currKey != "" { + newKey = currKey + "." + key + } + flattenToLabels(value, newKey, labels) + } + case []interface{}: + for i, item := range v { + newKey := currKey + "[" + strconv.Itoa(i) + "]" + flattenToLabels(item, newKey, labels) + } + default: + // Convert value to string and create label with traefik prefix. + labels["traefik."+currKey] = fmt.Sprintf("%v", v) + } +} + +// parseDeprecatedConfig parses command-line arguments using the deprecation configuration struct, +// filtering unknown nodes and checking for deprecated options. +// Returns true if incompatible deprecated options are found. +func parseDeprecatedConfig(labels map[string]string) (*configuration, error) { + // If no config, we can return without error to allow other loaders to proceed. + if len(labels) == 0 { + return nil, nil + } + + // Convert labels to node tree. + node, err := parser.DecodeToNode(labels, "traefik") + if err != nil { + return nil, fmt.Errorf("decoding to node: %w", err) + } + + // Filter unknown nodes and check for deprecated options. + config := &configuration{} + filterUnknownNodes(reflect.TypeOf(config), node) + + // If no config remains we can return without error, to allow other loaders to proceed. + if node == nil || len(node.Children) == 0 { + return nil, nil + } + + // Telling parser to look for the label struct tag to allow empty values. + err = parser.AddMetadata(config, node, parser.MetadataOpts{TagName: "label"}) + if err != nil { + return nil, fmt.Errorf("adding metadata to node: %w", err) + } + + err = parser.Fill(config, node, parser.FillerOpts{}) + if err != nil { + return nil, fmt.Errorf("filling configuration: %w", err) + } + + return config, nil +} + +// filterUnknownNodes removes from the node tree all nodes that do not correspond to any field in the given type. func filterUnknownNodes(fType reflect.Type, node *parser.Node) bool { var children []*parser.Node for _, child := range node.Children { @@ -127,6 +202,7 @@ func filterUnknownNodes(fType reflect.Type, node *parser.Node) bool { return len(node.Children) > 0 } +// hasKnownNodes checks whether the given node corresponds to a known field in the given type. func hasKnownNodes(rootType reflect.Type, node *parser.Node) bool { rType := rootType if rootType.Kind() == reflect.Pointer { @@ -177,7 +253,7 @@ func findTypedField(rType reflect.Type, node *parser.Node) (reflect.StructField, return reflect.StructField{}, false } -// configuration holds the static configuration removed/deprecated options. +// configuration holds the install configuration removed/deprecated options. type configuration struct { Core *core `json:"core,omitempty" toml:"core,omitempty" yaml:"core,omitempty" label:"allowEmpty" file:"allowEmpty"` Experimental *experimental `json:"experimental,omitempty" toml:"experimental,omitempty" yaml:"experimental,omitempty" label:"allowEmpty" file:"allowEmpty"` @@ -194,8 +270,8 @@ func (c *configuration) deprecationNotice(logger zerolog.Logger) bool { var incompatible bool if c.Pilot != nil { incompatible = true - logger.Error().Msg("Pilot configuration has been removed in v3, please remove all Pilot-related static configuration for Traefik to start." + - " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#pilot") + logger.Error().Msg("Pilot configuration has been removed in v3, please remove all Pilot-related install configuration for Traefik to start." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#pilot") } incompatibleCore := c.Core.deprecationNotice(logger) @@ -213,7 +289,7 @@ func (c *core) deprecationNotice(logger zerolog.Logger) bool { if c != nil && c.DefaultRuleSyntax != "" { logger.Error().Msg("`Core.DefaultRuleSyntax` option has been deprecated in v3.4, and will be removed in the next major version." + " Please consider migrating all router rules to v3 syntax." + - " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v3/#rule-syntax") + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v3/#rule-syntax") } return false @@ -242,14 +318,14 @@ func (p *providers) deprecationNotice(logger zerolog.Logger) bool { if p.Marathon != nil { incompatible = true - logger.Error().Msg("Marathon provider has been removed in v3, please remove all Marathon-related static configuration for Traefik to start." + - " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#marathon-provider") + logger.Error().Msg("Marathon provider has been removed in v3, please remove all Marathon-related install configuration for Traefik to start." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#marathon-provider") } if p.Rancher != nil { incompatible = true - logger.Error().Msg("Rancher provider has been removed in v3, please remove all Rancher-related static configuration for Traefik to start." + - " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#rancher-v1-provider") + logger.Error().Msg("Rancher provider has been removed in v3, please remove all Rancher-related install configuration for Traefik to start." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#rancher-v1-provider") } dockerIncompatible := p.Docker.deprecationNotice(logger) @@ -291,14 +367,14 @@ func (d *docker) deprecationNotice(logger zerolog.Logger) bool { if d.SwarmMode != nil { incompatible = true logger.Error().Msg("Docker provider `swarmMode` option has been removed in v3, please use the Swarm Provider instead." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#docker-docker-swarm") + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#docker-docker-swarm") } if d.TLS != nil && d.TLS.CAOptional != nil { incompatible = true logger.Error().Msg("Docker provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." + - "Please remove all occurrences from the static configuration for Traefik to start." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tlscaoptional") + " Please remove all occurrences from the install configuration for Traefik to start." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tlscaoptional") } return incompatible @@ -318,7 +394,7 @@ func (s *swarm) deprecationNotice(logger zerolog.Logger) bool { if s.TLS != nil && s.TLS.CAOptional != nil { incompatible = true logger.Error().Msg("Swarm provider `tls.CAOptional` option does not exist, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." + - "Please remove all occurrences from the static configuration for Traefik to start.") + " Please remove all occurrences from the install configuration for Traefik to start.") } return incompatible @@ -338,8 +414,8 @@ func (e *etcd) deprecationNotice(logger zerolog.Logger) bool { if e.TLS != nil && e.TLS.CAOptional != nil { incompatible = true logger.Error().Msg("ETCD provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." + - "Please remove all occurrences from the static configuration for Traefik to start." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tlscaoptional_3") + " Please remove all occurrences from the install configuration for Traefik to start." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tlscaoptional_3") } return incompatible @@ -359,8 +435,8 @@ func (r *redis) deprecationNotice(logger zerolog.Logger) bool { if r.TLS != nil && r.TLS.CAOptional != nil { incompatible = true logger.Error().Msg("Redis provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." + - "Please remove all occurrences from the static configuration for Traefik to start." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tlscaoptional_4") + " Please remove all occurrences from the install configuration for Traefik to start." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tlscaoptional_4") } return incompatible @@ -381,14 +457,14 @@ func (c *consul) deprecationNotice(logger zerolog.Logger) bool { if c.Namespace != nil { incompatible = true logger.Error().Msg("Consul provider `namespace` option has been removed, please use the `namespaces` option instead." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#consul-provider") + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#consul-provider") } if c.TLS != nil && c.TLS.CAOptional != nil { incompatible = true logger.Error().Msg("Consul provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." + - "Please remove all occurrences from the static configuration for Traefik to start." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tlscaoptional_1") + " Please remove all occurrences from the install configuration for Traefik to start." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tlscaoptional_1") } return incompatible @@ -413,14 +489,14 @@ func (c *consulCatalog) deprecationNotice(logger zerolog.Logger) bool { if c.Namespace != nil { incompatible = true logger.Error().Msg("ConsulCatalog provider `namespace` option has been removed, please use the `namespaces` option instead." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#consulcatalog-provider") + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#consulcatalog-provider") } if c.Endpoint != nil && c.Endpoint.TLS != nil && c.Endpoint.TLS.CAOptional != nil { incompatible = true logger.Error().Msg("ConsulCatalog provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." + - "Please remove all occurrences from the static configuration for Traefik to start." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#endpointtlscaoptional") + " Please remove all occurrences from the install configuration for Traefik to start." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#endpointtlscaoptional") } return incompatible @@ -441,14 +517,14 @@ func (n *nomad) deprecationNotice(logger zerolog.Logger) bool { if n.Namespace != nil { incompatible = true logger.Error().Msg("Nomad provider `namespace` option has been removed, please use the `namespaces` option instead." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#nomad-provider") + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#nomad-provider") } if n.Endpoint != nil && n.Endpoint.TLS != nil && n.Endpoint.TLS.CAOptional != nil { incompatible = true logger.Error().Msg("Nomad provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." + - "Please remove all occurrences from the static configuration for Traefik to start." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#endpointtlscaoptional_1") + " Please remove all occurrences from the install configuration for Traefik to start." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#endpointtlscaoptional_1") } return incompatible @@ -468,8 +544,8 @@ func (h *http) deprecationNotice(logger zerolog.Logger) bool { if h.TLS != nil && h.TLS.CAOptional != nil { incompatible = true logger.Error().Msg("HTTP provider `tls.CAOptional` option has been removed in v3, as TLS client authentication is a server side option (see https://github.com/golang/go/blob/740a490f71d026bb7d2d13cb8fa2d6d6e0572b70/src/crypto/tls/common.go#L634)." + - "Please remove all occurrences from the static configuration for Traefik to start." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tlscaoptional_2") + " Please remove all occurrences from the install configuration for Traefik to start." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tlscaoptional_2") } return incompatible @@ -486,14 +562,15 @@ func (i *ingress) deprecationNotice(logger zerolog.Logger) { if i.DisableIngressClassLookup != nil { logger.Error().Msg("Kubernetes Ingress provider `disableIngressClassLookup` option has been deprecated in v3.1, and will be removed in the next major version." + - "Please use the `disableClusterScopeResources` option instead." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v3/#ingressclasslookup") + " Please use the `disableClusterScopeResources` option instead." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v3/#ingressclasslookup") } } type experimental struct { - HTTP3 *bool `json:"http3,omitempty" toml:"http3,omitempty" yaml:"http3,omitempty"` - KubernetesGateway *bool `json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty"` + HTTP3 *bool `json:"http3,omitempty" toml:"http3,omitempty" yaml:"http3,omitempty"` + KubernetesGateway *bool `json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty"` + KubernetesIngressNGINX *bool `json:"kubernetesIngressNGINX,omitempty" toml:"kubernetesIngressNGINX,omitempty" yaml:"kubernetesIngressNGINX,omitempty"` } func (e *experimental) deprecationNotice(logger zerolog.Logger) bool { @@ -503,16 +580,22 @@ func (e *experimental) deprecationNotice(logger zerolog.Logger) bool { if e.HTTP3 != nil { logger.Error().Msg("HTTP3 is not an experimental feature in v3 and the associated enablement has been removed." + - "Please remove its usage from the static configuration for Traefik to start." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3-details/#http3") + " Please remove its usage from the install configuration for Traefik to start." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3-details/#http3") return true } if e.KubernetesGateway != nil { logger.Error().Msg("KubernetesGateway provider is not an experimental feature starting with v3.1." + - "Please remove its usage from the static configuration." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v3/#gateway-api-kubernetesgateway-provider") + " Please remove its usage from the install configuration." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v3/#gateway-api-kubernetesgateway-provider") + } + + if e.KubernetesIngressNGINX != nil { + logger.Error().Msg("KubernetesIngressNGINX provider is not an experimental feature starting with v3.6.2." + + " Please remove its usage from the install configuration." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v3/#ingress-nginx-provider") } return false @@ -539,57 +622,57 @@ func (t *tracing) deprecationNotice(logger zerolog.Logger) bool { if t.SpanNameLimit != nil { incompatible = true logger.Error().Msg("SpanNameLimit option for Tracing has been removed in v3, as Span names are now of a fixed length." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tracing") + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tracing") } if t.GlobalAttributes != nil { log.Warn().Msgf("tracing.globalAttributes option is now deprecated, please use tracing.resourceAttributes instead.") logger.Error().Msg("`tracing.globalAttributes` option has been deprecated in v3.3, and will be removed in the next major version." + - "Please use the `tracing.resourceAttributes` option instead." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v3/#tracing-global-attributes") + " Please use the `tracing.resourceAttributes` option instead." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v3/#tracing-global-attributes") } if t.Jaeger != nil { incompatible = true - logger.Error().Msg("Jaeger Tracing backend has been removed in v3, please remove all Jaeger-related Tracing static configuration for Traefik to start." + - "In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tracing") + logger.Error().Msg("Jaeger Tracing backend has been removed in v3, please remove all Jaeger-related Tracing install configuration for Traefik to start." + + " In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tracing") } if t.Zipkin != nil { incompatible = true - logger.Error().Msg("Zipkin Tracing backend has been removed in v3, please remove all Zipkin-related Tracing static configuration for Traefik to start." + - "In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tracing") + logger.Error().Msg("Zipkin Tracing backend has been removed in v3, please remove all Zipkin-related Tracing install configuration for Traefik to start." + + " In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tracing") } if t.Datadog != nil { incompatible = true - logger.Error().Msg("Datadog Tracing backend has been removed in v3, please remove all Datadog-related Tracing static configuration for Traefik to start." + - "In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tracing") + logger.Error().Msg("Datadog Tracing backend has been removed in v3, please remove all Datadog-related Tracing install configuration for Traefik to start." + + " In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tracing") } if t.Instana != nil { incompatible = true - logger.Error().Msg("Instana Tracing backend has been removed in v3, please remove all Instana-related Tracing static configuration for Traefik to start." + - "In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tracing") + logger.Error().Msg("Instana Tracing backend has been removed in v3, please remove all Instana-related Tracing install configuration for Traefik to start." + + " In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tracing") } if t.Haystack != nil { incompatible = true - logger.Error().Msg("Haystack Tracing backend has been removed in v3, please remove all Haystack-related Tracing static configuration for Traefik to start." + - "In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tracing") + logger.Error().Msg("Haystack Tracing backend has been removed in v3, please remove all Haystack-related Tracing install configuration for Traefik to start." + + " In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tracing") } if t.Elastic != nil { incompatible = true - logger.Error().Msg("Elastic Tracing backend has been removed in v3, please remove all Elastic-related Tracing static configuration for Traefik to start." + - "In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + - "For more information please read the migration guide: https://doc.traefik.io/traefik/v3.5/migration/v2-to-v3/#tracing") + logger.Error().Msg("Elastic Tracing backend has been removed in v3, please remove all Elastic-related Tracing install configuration for Traefik to start." + + " In v3, Open Telemetry replaces specific tracing backend implementations, and an collector/exporter can be used to export metrics in a vendor specific format." + + " For more information please read the migration guide: https://doc.traefik.io/traefik/v3.6/migration/v2-to-v3/#tracing") } return incompatible diff --git a/pkg/cli/fixtures/traefik_deprecated.toml b/pkg/cli/fixtures/traefik_deprecated.toml index 21fa1d1c1..6ac4fdb69 100644 --- a/pkg/cli/fixtures/traefik_deprecated.toml +++ b/pkg/cli/fixtures/traefik_deprecated.toml @@ -3,3 +3,6 @@ [entrypoints.test.http.tls] [providers.marathon] + +[experimental] + futureUnknownFlag = true diff --git a/pkg/cli/fixtures/traefik_multiple_deprecated.toml b/pkg/cli/fixtures/traefik_multiple_deprecated.toml index 0847e9da3..8ab616205 100644 --- a/pkg/cli/fixtures/traefik_multiple_deprecated.toml +++ b/pkg/cli/fixtures/traefik_multiple_deprecated.toml @@ -6,3 +6,6 @@ [pilot] token="xxx" + +[experimental] + futureUnknownFlag = true diff --git a/pkg/cli/fixtures/traefik_no_deprecated.toml b/pkg/cli/fixtures/traefik_no_deprecated.toml index 282d2f873..44cb175d7 100644 --- a/pkg/cli/fixtures/traefik_no_deprecated.toml +++ b/pkg/cli/fixtures/traefik_no_deprecated.toml @@ -1,3 +1,6 @@ [accesslog] [entrypoints.test.http.tls] + +[experimental] + futureUnknownFlag = true diff --git a/pkg/config/dynamic/http_config.go b/pkg/config/dynamic/http_config.go index 96e16f7b4..97b08f5d6 100644 --- a/pkg/config/dynamic/http_config.go +++ b/pkg/config/dynamic/http_config.go @@ -44,20 +44,22 @@ type HTTPConfiguration struct { // Model holds model configuration. type Model struct { - Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"` - TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"` - Observability RouterObservabilityConfig `json:"observability,omitempty" toml:"observability,omitempty" yaml:"observability,omitempty" export:"true"` - DefaultRuleSyntax string `json:"-" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"` + Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"` + TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"` + Observability RouterObservabilityConfig `json:"observability,omitempty" toml:"observability,omitempty" yaml:"observability,omitempty" export:"true"` + DeniedEncodedPathCharacters *RouterDeniedEncodedPathCharacters `json:"-" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"` + DefaultRuleSyntax string `json:"-" toml:"-" yaml:"-" label:"-" file:"-" kv:"-" export:"true"` } // +k8s:deepcopy-gen=true // Service holds a service configuration (can only be of one type at the same time). type Service struct { - LoadBalancer *ServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty" export:"true"` - Weighted *WeightedRoundRobin `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-" export:"true"` - Mirroring *Mirroring `json:"mirroring,omitempty" toml:"mirroring,omitempty" yaml:"mirroring,omitempty" label:"-" export:"true"` - Failover *Failover `json:"failover,omitempty" toml:"failover,omitempty" yaml:"failover,omitempty" label:"-" export:"true"` + LoadBalancer *ServersLoadBalancer `json:"loadBalancer,omitempty" toml:"loadBalancer,omitempty" yaml:"loadBalancer,omitempty" export:"true"` + HighestRandomWeight *HighestRandomWeight `json:"highestRandomWeight,omitempty" toml:"highestRandomWeight,omitempty" yaml:"highestRandomWeight,omitempty" label:"-" export:"true"` + Weighted *WeightedRoundRobin `json:"weighted,omitempty" toml:"weighted,omitempty" yaml:"weighted,omitempty" label:"-" export:"true"` + Mirroring *Mirroring `json:"mirroring,omitempty" toml:"mirroring,omitempty" yaml:"mirroring,omitempty" label:"-" export:"true"` + Failover *Failover `json:"failover,omitempty" toml:"failover,omitempty" yaml:"failover,omitempty" label:"-" export:"true"` } // +k8s:deepcopy-gen=true @@ -68,12 +70,60 @@ type Router struct { Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"` Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"` Rule string `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"` + ParentRefs []string `json:"parentRefs,omitempty" toml:"parentRefs,omitempty" yaml:"parentRefs,omitempty" label:"-" export:"true"` // Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. - RuleSyntax string `json:"ruleSyntax,omitempty" toml:"ruleSyntax,omitempty" yaml:"ruleSyntax,omitempty" export:"true"` - Priority int `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty" export:"true"` - TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"` - Observability *RouterObservabilityConfig `json:"observability,omitempty" toml:"observability,omitempty" yaml:"observability,omitempty" export:"true"` - DefaultRule bool `json:"-" toml:"-" yaml:"-" label:"-" file:"-"` + RuleSyntax string `json:"ruleSyntax,omitempty" toml:"ruleSyntax,omitempty" yaml:"ruleSyntax,omitempty" export:"true"` + Priority int `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty" export:"true"` + TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"` + Observability *RouterObservabilityConfig `json:"observability,omitempty" toml:"observability,omitempty" yaml:"observability,omitempty" export:"true"` + DefaultRule bool `json:"-" toml:"-" yaml:"-" label:"-" file:"-"` + DeniedEncodedPathCharacters *RouterDeniedEncodedPathCharacters `json:"-" toml:"-" yaml:"-" label:"-" file:"-" kv:"-"` +} + +// +k8s:deepcopy-gen=true + +// RouterDeniedEncodedPathCharacters configures which encoded characters are allowed in the request path. +type RouterDeniedEncodedPathCharacters struct { + AllowEncodedSlash bool `description:"Defines whether requests with encoded slash characters in the path are allowed." json:"allowEncodedSlash,omitempty" toml:"allowEncodedSlash,omitempty" yaml:"allowEncodedSlash,omitempty" export:"true"` + AllowEncodedBackSlash bool `description:"Defines whether requests with encoded back slash characters in the path are allowed." json:"allowEncodedBackSlash,omitempty" toml:"allowEncodedBackSlash,omitempty" yaml:"allowEncodedBackSlash,omitempty" export:"true"` + AllowEncodedNullCharacter bool `description:"Defines whether requests with encoded null characters in the path are allowed." json:"allowEncodedNullCharacter,omitempty" toml:"allowEncodedNullCharacter,omitempty" yaml:"allowEncodedNullCharacter,omitempty" export:"true"` + AllowEncodedSemicolon bool `description:"Defines whether requests with encoded semicolon characters in the path are allowed." json:"allowEncodedSemicolon,omitempty" toml:"allowEncodedSemicolon,omitempty" yaml:"allowEncodedSemicolon,omitempty" export:"true"` + AllowEncodedPercent bool `description:"Defines whether requests with encoded percent characters in the path are allowed." json:"allowEncodedPercent,omitempty" toml:"allowEncodedPercent,omitempty" yaml:"allowEncodedPercent,omitempty" export:"true"` + AllowEncodedQuestionMark bool `description:"Defines whether requests with encoded question mark characters in the path are allowed." json:"allowEncodedQuestionMark,omitempty" toml:"allowEncodedQuestionMark,omitempty" yaml:"allowEncodedQuestionMark,omitempty" export:"true"` + AllowEncodedHash bool `description:"Defines whether requests with encoded hash characters in the path are allowed." json:"allowEncodedHash,omitempty" toml:"allowEncodedHash,omitempty" yaml:"allowEncodedHash,omitempty" export:"true"` +} + +// Map returns a map of unallowed encoded characters. +func (r *RouterDeniedEncodedPathCharacters) Map() map[string]struct{} { + characters := make(map[string]struct{}) + + if !r.AllowEncodedSlash { + characters["%2F"] = struct{}{} + characters["%2f"] = struct{}{} + } + if !r.AllowEncodedBackSlash { + characters["%5C"] = struct{}{} + characters["%5c"] = struct{}{} + } + if !r.AllowEncodedNullCharacter { + characters["%00"] = struct{}{} + } + if !r.AllowEncodedSemicolon { + characters["%3B"] = struct{}{} + characters["%3b"] = struct{}{} + } + if !r.AllowEncodedPercent { + characters["%25"] = struct{}{} + } + if !r.AllowEncodedQuestionMark { + characters["%3F"] = struct{}{} + characters["%3f"] = struct{}{} + } + if !r.AllowEncodedHash { + characters["%23"] = struct{}{} + } + + return characters } // +k8s:deepcopy-gen=true @@ -158,11 +208,28 @@ type WeightedRoundRobin struct { // +k8s:deepcopy-gen=true +// HighestRandomWeight is a weighted sticky load-balancer of services. +type HighestRandomWeight struct { + Services []HRWService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty" export:"true"` + // HealthCheck enables automatic self-healthcheck for this service, i.e. + // whenever one of its children is reported as down, this service becomes aware of it, + // and takes it into account (i.e. it ignores the down child) when running the + // load-balancing algorithm. In addition, if the parent of this service also has + // HealthCheck enabled, this service reports to its parent any status change. + HealthCheck *HealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"` +} + +// +k8s:deepcopy-gen=true + // WRRService is a reference to a service load-balanced with weighted round-robin. type WRRService struct { Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty" export:"true"` Weight *int `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty" export:"true"` + // Headers defines the HTTP headers that should be added to the request when calling the service. + // This is required by the Knative implementation which expects specific headers to be sent. + Headers map[string]string `json:"-" toml:"-" yaml:"-" label:"-" file:"-"` + // Status defines an HTTP status code that should be returned when calling the service. // This is required by the Gateway API implementation which expects specific HTTP status to be returned. Status *int `json:"-" toml:"-" yaml:"-" label:"-" file:"-"` @@ -179,6 +246,20 @@ func (w *WRRService) SetDefaults() { // +k8s:deepcopy-gen=true +// HRWService is a reference to a service load-balanced with highest random weight. +type HRWService struct { + Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty" export:"true"` + Weight *int `json:"weight,omitempty" toml:"weight,omitempty" yaml:"weight,omitempty" export:"true"` +} + +// SetDefaults Default values for a HRWService. +func (w *HRWService) SetDefaults() { + defaultWeight := 1 + w.Weight = &defaultWeight +} + +// +k8s:deepcopy-gen=true + type GRPCStatus struct { Code codes.Code `json:"code,omitempty" toml:"code,omitempty" yaml:"code,omitempty" export:"true"` Msg string `json:"msg,omitempty" toml:"msg,omitempty" yaml:"msg,omitempty" export:"true"` @@ -232,6 +313,10 @@ const ( BalancerStrategyWRR BalancerStrategy = "wrr" // BalancerStrategyP2C is the power of two choices strategy. BalancerStrategyP2C BalancerStrategy = "p2c" + // BalancerStrategyHRW is the highest random weight strategy. + BalancerStrategyHRW BalancerStrategy = "hrw" + // BalancerStrategyLeastTime is the least-time strategy. + BalancerStrategyLeastTime BalancerStrategy = "leasttime" ) // +k8s:deepcopy-gen=true @@ -245,10 +330,12 @@ type ServersLoadBalancer struct { // children servers of this load-balancer. To propagate status changes (e.g. all // servers of this service are down) upwards, HealthCheck must also be enabled on // the parent(s) of this service. - HealthCheck *ServerHealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty" export:"true"` - PassHostHeader *bool `json:"passHostHeader" toml:"passHostHeader" yaml:"passHostHeader" export:"true"` - ResponseForwarding *ResponseForwarding `json:"responseForwarding,omitempty" toml:"responseForwarding,omitempty" yaml:"responseForwarding,omitempty" export:"true"` - ServersTransport string `json:"serversTransport,omitempty" toml:"serversTransport,omitempty" yaml:"serversTransport,omitempty" export:"true"` + HealthCheck *ServerHealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty" export:"true"` + // PassiveHealthCheck enables passive health checks for children servers of this load-balancer. + PassiveHealthCheck *PassiveServerHealthCheck `json:"passiveHealthCheck,omitempty" toml:"passiveHealthCheck,omitempty" yaml:"passiveHealthCheck,omitempty" export:"true"` + PassHostHeader *bool `json:"passHostHeader" toml:"passHostHeader" yaml:"passHostHeader" export:"true"` + ResponseForwarding *ResponseForwarding `json:"responseForwarding,omitempty" toml:"responseForwarding,omitempty" yaml:"responseForwarding,omitempty" export:"true"` + ServersTransport string `json:"serversTransport,omitempty" toml:"serversTransport,omitempty" yaml:"serversTransport,omitempty" export:"true"` } // Mergeable tells if the given service is mergeable. @@ -337,6 +424,20 @@ func (h *ServerHealthCheck) SetDefaults() { // +k8s:deepcopy-gen=true +type PassiveServerHealthCheck struct { + // FailureWindow defines the time window during which the failed attempts must occur for the server to be marked as unhealthy. It also defines for how long the server will be considered unhealthy. + FailureWindow ptypes.Duration `json:"failureWindow,omitempty" toml:"failureWindow,omitempty" yaml:"failureWindow,omitempty" export:"true"` + // MaxFailedAttempts is the number of consecutive failed attempts allowed within the failure window before marking the server as unhealthy. + MaxFailedAttempts int `json:"maxFailedAttempts,omitempty" toml:"maxFailedAttempts,omitempty" yaml:"maxFailedAttempts,omitempty" export:"true"` +} + +func (p *PassiveServerHealthCheck) SetDefaults() { + p.FailureWindow = ptypes.Duration(10 * time.Second) + p.MaxFailedAttempts = 1 +} + +// +k8s:deepcopy-gen=true + // HealthCheck controls healthcheck awareness and propagation at the services level. type HealthCheck struct{} diff --git a/pkg/config/dynamic/http_config_test.go b/pkg/config/dynamic/http_config_test.go new file mode 100644 index 000000000..c8cda437d --- /dev/null +++ b/pkg/config/dynamic/http_config_test.go @@ -0,0 +1,165 @@ +package dynamic + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestEncodedCharactersMap(t *testing.T) { + tests := []struct { + name string + config RouterDeniedEncodedPathCharacters + expected map[string]struct{} + }{ + { + name: "Handles empty configuration", + expected: map[string]struct{}{ + "%2F": {}, + "%2f": {}, + "%5C": {}, + "%5c": {}, + "%00": {}, + "%3B": {}, + "%3b": {}, + "%25": {}, + "%3F": {}, + "%3f": {}, + "%23": {}, + }, + }, + { + name: "Exclude encoded slash when allowed", + config: RouterDeniedEncodedPathCharacters{ + AllowEncodedSlash: true, + }, + expected: map[string]struct{}{ + "%5C": {}, + "%5c": {}, + "%00": {}, + "%3B": {}, + "%3b": {}, + "%25": {}, + "%3F": {}, + "%3f": {}, + "%23": {}, + }, + }, + + { + name: "Exclude encoded backslash when allowed", + config: RouterDeniedEncodedPathCharacters{ + AllowEncodedBackSlash: true, + }, + expected: map[string]struct{}{ + "%2F": {}, + "%2f": {}, + "%00": {}, + "%3B": {}, + "%3b": {}, + "%25": {}, + "%3F": {}, + "%3f": {}, + "%23": {}, + }, + }, + + { + name: "Exclude encoded null character when allowed", + config: RouterDeniedEncodedPathCharacters{ + AllowEncodedNullCharacter: true, + }, + expected: map[string]struct{}{ + "%2F": {}, + "%2f": {}, + "%5C": {}, + "%5c": {}, + "%3B": {}, + "%3b": {}, + "%25": {}, + "%3F": {}, + "%3f": {}, + "%23": {}, + }, + }, + { + name: "Exclude encoded semicolon when allowed", + config: RouterDeniedEncodedPathCharacters{ + AllowEncodedSemicolon: true, + }, + expected: map[string]struct{}{ + "%2F": {}, + "%2f": {}, + "%5C": {}, + "%5c": {}, + "%00": {}, + "%25": {}, + "%3F": {}, + "%3f": {}, + "%23": {}, + }, + }, + { + name: "Exclude encoded percent when allowed", + config: RouterDeniedEncodedPathCharacters{ + AllowEncodedPercent: true, + }, + expected: map[string]struct{}{ + "%2F": {}, + "%2f": {}, + "%5C": {}, + "%5c": {}, + "%00": {}, + "%3B": {}, + "%3b": {}, + "%3F": {}, + "%3f": {}, + "%23": {}, + }, + }, + { + name: "Exclude encoded question mark when allowed", + config: RouterDeniedEncodedPathCharacters{ + AllowEncodedQuestionMark: true, + }, + expected: map[string]struct{}{ + "%2F": {}, + "%2f": {}, + "%5C": {}, + "%5c": {}, + "%00": {}, + "%3B": {}, + "%3b": {}, + "%25": {}, + "%23": {}, + }, + }, + { + name: "Exclude encoded hash when allowed", + config: RouterDeniedEncodedPathCharacters{ + AllowEncodedHash: true, + }, + expected: map[string]struct{}{ + "%2F": {}, + "%2f": {}, + "%5C": {}, + "%5c": {}, + "%00": {}, + "%3B": {}, + "%3b": {}, + "%25": {}, + "%3F": {}, + "%3f": {}, + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + result := test.config.Map() + require.Equal(t, test.expected, result) + }) + } +} diff --git a/pkg/config/dynamic/middlewares.go b/pkg/config/dynamic/middlewares.go index 02f371948..fa10c302b 100644 --- a/pkg/config/dynamic/middlewares.go +++ b/pkg/config/dynamic/middlewares.go @@ -77,7 +77,7 @@ type ContentType struct { // AddPrefix holds the add prefix middleware configuration. // This middleware updates the path of a request before forwarding it. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/addprefix/ +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/addprefix/ type AddPrefix struct { // Prefix is the string to add before the current path in the requested URL. // It should include a leading slash (/). @@ -89,7 +89,7 @@ type AddPrefix struct { // BasicAuth holds the basic auth middleware configuration. // This middleware restricts access to your services to known users. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/basicauth/ +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/basicauth/ type BasicAuth struct { // Users is an array of authorized users. // Each user must be declared using the name:hashed-password format. @@ -104,7 +104,7 @@ type BasicAuth struct { // Default: false. RemoveHeader bool `json:"removeHeader,omitempty" toml:"removeHeader,omitempty" yaml:"removeHeader,omitempty" export:"true"` // HeaderField defines a header field to store the authenticated user. - // More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/basicauth/#headerfield + // More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/basicauth/#headerfield HeaderField string `json:"headerField,omitempty" toml:"headerField,omitempty" yaml:"headerField,omitempty" export:"true"` } @@ -112,7 +112,7 @@ type BasicAuth struct { // Buffering holds the buffering middleware configuration. // This middleware retries or limits the size of requests that can be forwarded to backends. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/buffering/#maxrequestbodybytes +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/buffering/#maxrequestbodybytes type Buffering struct { // MaxRequestBodyBytes defines the maximum allowed body size for the request (in bytes). // If the request exceeds the allowed size, it is not forwarded to the service, and the client gets a 413 (Request Entity Too Large) response. @@ -130,7 +130,7 @@ type Buffering struct { MemResponseBodyBytes int64 `json:"memResponseBodyBytes,omitempty" toml:"memResponseBodyBytes,omitempty" yaml:"memResponseBodyBytes,omitempty" export:"true"` // RetryExpression defines the retry conditions. // It is a logical combination of functions with operators AND (&&) and OR (||). - // More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/buffering/#retryexpression + // More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/buffering/#retryexpression RetryExpression string `json:"retryExpression,omitempty" toml:"retryExpression,omitempty" yaml:"retryExpression,omitempty" export:"true"` } @@ -147,7 +147,7 @@ type Chain struct { // CircuitBreaker holds the circuit breaker middleware configuration. // This middleware protects the system from stacking requests to unhealthy services, resulting in cascading failures. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/circuitbreaker/ +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/circuitbreaker/ type CircuitBreaker struct { // Expression defines the expression that, once matched, opens the circuit breaker and applies the fallback mechanism instead of calling the services. Expression string `json:"expression,omitempty" toml:"expression,omitempty" yaml:"expression,omitempty" export:"true"` @@ -197,7 +197,7 @@ func (c *Compress) SetDefaults() { // DigestAuth holds the digest auth middleware configuration. // This middleware restricts access to your services to known users. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/digestauth/ +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/digestauth/ type DigestAuth struct { // Users defines the authorized users. // Each user should be declared using the name:realm:encoded-password format. @@ -210,7 +210,7 @@ type DigestAuth struct { // Default: traefik. Realm string `json:"realm,omitempty" toml:"realm,omitempty" yaml:"realm,omitempty"` // HeaderField defines a header field to store the authenticated user. - // More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/basicauth/#headerfield + // More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/basicauth/#headerfield HeaderField string `json:"headerField,omitempty" toml:"headerField,omitempty" yaml:"headerField,omitempty" export:"true"` } @@ -241,7 +241,7 @@ type ErrorPage struct { // ForwardAuth holds the forward auth middleware configuration. // This middleware delegates the request authentication to a Service. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/forwardauth/ +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/forwardauth/ type ForwardAuth struct { // Address defines the authentication server address. Address string `json:"address,omitempty" toml:"address,omitempty" yaml:"address,omitempty"` @@ -252,7 +252,7 @@ type ForwardAuth struct { // AuthResponseHeaders defines the list of headers to copy from the authentication server response and set on forwarded request, replacing any existing conflicting headers. AuthResponseHeaders []string `json:"authResponseHeaders,omitempty" toml:"authResponseHeaders,omitempty" yaml:"authResponseHeaders,omitempty" export:"true"` // AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex. - // More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/forwardauth/#authresponseheadersregex + // More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/forwardauth/#authresponseheadersregex AuthResponseHeadersRegex string `json:"authResponseHeadersRegex,omitempty" toml:"authResponseHeadersRegex,omitempty" yaml:"authResponseHeadersRegex,omitempty" export:"true"` // AuthRequestHeaders defines the list of the headers to copy from the request to the authentication server. // If not set or empty then all request headers are passed. @@ -260,7 +260,7 @@ type ForwardAuth struct { // AddAuthCookiesToResponse defines the list of cookies to copy from the authentication server response to the response. AddAuthCookiesToResponse []string `json:"addAuthCookiesToResponse,omitempty" toml:"addAuthCookiesToResponse,omitempty" yaml:"addAuthCookiesToResponse,omitempty" export:"true"` // HeaderField defines a header field to store the authenticated user. - // More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/forwardauth/#headerfield + // More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/forwardauth/#headerfield HeaderField string `json:"headerField,omitempty" toml:"headerField,omitempty" yaml:"headerField,omitempty" export:"true"` // ForwardBody defines whether to send the request body to the authentication server. ForwardBody bool `json:"forwardBody,omitempty" toml:"forwardBody,omitempty" yaml:"forwardBody,omitempty" export:"true"` @@ -295,7 +295,7 @@ type ClientTLS struct { // Headers holds the headers middleware configuration. // This middleware manages the requests and responses headers. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/headers/#customrequestheaders +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/headers/#customrequestheaders type Headers struct { // CustomRequestHeaders defines the header names and values to apply to the request. CustomRequestHeaders map[string]string `json:"customRequestHeaders,omitempty" toml:"customRequestHeaders,omitempty" yaml:"customRequestHeaders,omitempty" export:"true"` @@ -425,7 +425,7 @@ func (h *Headers) HasSecureHeadersDefined() bool { // +k8s:deepcopy-gen=true // IPStrategy holds the IP strategy configuration used by Traefik to determine the client IP. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/#ipstrategy +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipallowlist/#ipstrategy type IPStrategy struct { // Depth tells Traefik to use the X-Forwarded-For header and take the IP located at the depth position (starting from the right). // +kubebuilder:validation:Minimum=0 @@ -480,7 +480,7 @@ func (s *IPStrategy) Get() (ip.Strategy, error) { // IPWhiteList holds the IP whitelist middleware configuration. // This middleware limits allowed requests based on the client IP. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipwhitelist/ +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipwhitelist/ // Deprecated: please use IPAllowList instead. type IPWhiteList struct { // SourceRange defines the set of allowed IPs (or ranges of allowed IPs by using CIDR notation). Required. @@ -492,7 +492,7 @@ type IPWhiteList struct { // IPAllowList holds the IP allowlist middleware configuration. // This middleware limits allowed requests based on the client IP. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/ipallowlist/ +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/ipallowlist/ type IPAllowList struct { // SourceRange defines the set of allowed IPs (or ranges of allowed IPs by using CIDR notation). SourceRange []string `json:"sourceRange,omitempty" toml:"sourceRange,omitempty" yaml:"sourceRange,omitempty"` @@ -506,7 +506,7 @@ type IPAllowList struct { // InFlightReq holds the in-flight request middleware configuration. // This middleware limits the number of requests being processed and served concurrently. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/inflightreq/ +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/inflightreq/ type InFlightReq struct { // Amount defines the maximum amount of allowed simultaneous in-flight request. // The middleware responds with HTTP 429 Too Many Requests if there are already amount requests in progress (based on the same sourceCriterion strategy). @@ -515,7 +515,7 @@ type InFlightReq struct { // SourceCriterion defines what criterion is used to group requests as originating from a common source. // If several strategies are defined at the same time, an error will be raised. // If none are set, the default is to use the requestHost. - // More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/inflightreq/#sourcecriterion + // More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/inflightreq/#sourcecriterion SourceCriterion *SourceCriterion `json:"sourceCriterion,omitempty" toml:"sourceCriterion,omitempty" yaml:"sourceCriterion,omitempty" export:"true"` } @@ -523,7 +523,7 @@ type InFlightReq struct { // PassTLSClientCert holds the pass TLS client cert middleware configuration. // This middleware adds the selected data from the passed client TLS certificate to a header. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/passtlsclientcert/ +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/passtlsclientcert/ type PassTLSClientCert struct { // PEM sets the X-Forwarded-Tls-Client-Cert header with the certificate. PEM bool `json:"pem,omitempty" toml:"pem,omitempty" yaml:"pem,omitempty" export:"true"` @@ -635,7 +635,7 @@ func (r *Redis) SetDefaults() { // RedirectRegex holds the redirect regex middleware configuration. // This middleware redirects a request using regex matching and replacement. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/redirectregex/#regex +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/redirectregex/#regex type RedirectRegex struct { // Regex defines the regex used to match and capture elements from the request URL. Regex string `json:"regex,omitempty" toml:"regex,omitempty" yaml:"regex,omitempty"` @@ -649,21 +649,26 @@ type RedirectRegex struct { // RedirectScheme holds the redirect scheme middleware configuration. // This middleware redirects requests from a scheme/port to another. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/redirectscheme/ +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/redirectscheme/ type RedirectScheme struct { // Scheme defines the scheme of the new URL. Scheme string `json:"scheme,omitempty" toml:"scheme,omitempty" yaml:"scheme,omitempty" export:"true"` // Port defines the port of the new URL. Port string `json:"port,omitempty" toml:"port,omitempty" yaml:"port,omitempty" export:"true"` - // Permanent defines whether the redirection is permanent (308). + // Permanent defines whether the redirection is permanent. + // For HTTP GET requests a 301 is returned, otherwise a 308 is returned. Permanent bool `json:"permanent,omitempty" toml:"permanent,omitempty" yaml:"permanent,omitempty" export:"true"` + // ForcePermanentRedirect is an internal field (not exposed in configuration). + // When set to true, this forces the use of permanent redirects 308, regardless of the request method. + // Used by the provider ingress-ngin. + ForcePermanentRedirect bool `json:"-" toml:"-" yaml:"-" label:"-"` } // +k8s:deepcopy-gen=true // ReplacePath holds the replace path middleware configuration. // This middleware replaces the path of the request URL and store the original path in an X-Replaced-Path header. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/replacepath/ +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/replacepath/ type ReplacePath struct { // Path defines the path to use as replacement in the request URL. Path string `json:"path,omitempty" toml:"path,omitempty" yaml:"path,omitempty" export:"true"` @@ -673,7 +678,7 @@ type ReplacePath struct { // ReplacePathRegex holds the replace path regex middleware configuration. // This middleware replaces the path of a URL using regex matching and replacement. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/replacepathregex/ +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/replacepathregex/ type ReplacePathRegex struct { // Regex defines the regular expression used to match and capture the path from the request URL. Regex string `json:"regex,omitempty" toml:"regex,omitempty" yaml:"regex,omitempty" export:"true"` @@ -686,7 +691,7 @@ type ReplacePathRegex struct { // Retry holds the retry middleware configuration. // This middleware reissues requests a given number of times to a backend server if that server does not reply. // As soon as the server answers, the middleware stops retrying, regardless of the response status. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/retry/ +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/retry/ type Retry struct { // Attempts defines how many times the request should be retried. Attempts int `json:"attempts,omitempty" toml:"attempts,omitempty" yaml:"attempts,omitempty" export:"true"` @@ -702,7 +707,7 @@ type Retry struct { // StripPrefix holds the strip prefix middleware configuration. // This middleware removes the specified prefixes from the URL path. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/stripprefix/ +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/stripprefix/ type StripPrefix struct { // Prefixes defines the prefixes to strip from the request URL. Prefixes []string `json:"prefixes,omitempty" toml:"prefixes,omitempty" yaml:"prefixes,omitempty" export:"true"` @@ -717,7 +722,7 @@ type StripPrefix struct { // StripPrefixRegex holds the strip prefix regex middleware configuration. // This middleware removes the matching prefixes from the URL path. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/http/stripprefixregex/ +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/http/stripprefixregex/ type StripPrefixRegex struct { // Regex defines the regular expression to match the path prefix from the request URL. Regex []string `json:"regex,omitempty" toml:"regex,omitempty" yaml:"regex,omitempty" export:"true"` diff --git a/pkg/config/dynamic/tcp_config.go b/pkg/config/dynamic/tcp_config.go index 207c90045..a68be77b9 100644 --- a/pkg/config/dynamic/tcp_config.go +++ b/pkg/config/dynamic/tcp_config.go @@ -39,7 +39,8 @@ type TCPService struct { // TCPWeightedRoundRobin is a weighted round robin tcp load-balancer of services. type TCPWeightedRoundRobin struct { - Services []TCPWRRService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty" export:"true"` + Services []TCPWRRService `json:"services,omitempty" toml:"services,omitempty" yaml:"services,omitempty" export:"true"` + HealthCheck *HealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"` } // +k8s:deepcopy-gen=true @@ -86,7 +87,6 @@ type RouterTCPTLSConfig struct { type TCPServersLoadBalancer struct { Servers []TCPServer `json:"servers,omitempty" toml:"servers,omitempty" yaml:"servers,omitempty" label-slice-as-struct:"server" export:"true"` ServersTransport string `json:"serversTransport,omitempty" toml:"serversTransport,omitempty" yaml:"serversTransport,omitempty" export:"true"` - // ProxyProtocol holds the PROXY Protocol configuration. // Deprecated: use ServersTransport to configure ProxyProtocol instead. ProxyProtocol *ProxyProtocol `json:"proxyProtocol,omitempty" toml:"proxyProtocol,omitempty" yaml:"proxyProtocol,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"` @@ -96,7 +96,8 @@ type TCPServersLoadBalancer struct { // connection. It is a duration in milliseconds, defaulting to 100. A negative value // means an infinite deadline (i.e. the reading capability is never closed). // Deprecated: use ServersTransport to configure the TerminationDelay instead. - TerminationDelay *int `json:"terminationDelay,omitempty" toml:"terminationDelay,omitempty" yaml:"terminationDelay,omitempty" export:"true"` + TerminationDelay *int `json:"terminationDelay,omitempty" toml:"terminationDelay,omitempty" yaml:"terminationDelay,omitempty" export:"true"` + HealthCheck *TCPServerHealthCheck `json:"healthCheck,omitempty" toml:"healthCheck,omitempty" yaml:"healthCheck,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"` } // Mergeable tells if the given service is mergeable. @@ -128,7 +129,7 @@ type TCPServer struct { // +k8s:deepcopy-gen=true // ProxyProtocol holds the PROXY Protocol configuration. -// More info: https://doc.traefik.io/traefik/v3.5/routing/services/#proxy-protocol +// More info: https://doc.traefik.io/traefik/v3.6/routing/services/#proxy-protocol type ProxyProtocol struct { // Version defines the PROXY Protocol version to use. // +kubebuilder:validation:Minimum=1 @@ -176,3 +177,21 @@ type TLSClientConfig struct { PeerCertURI string `description:"Defines the URI used to match against SAN URI during the peer certificate verification." json:"peerCertURI,omitempty" toml:"peerCertURI,omitempty" yaml:"peerCertURI,omitempty" export:"true"` Spiffe *Spiffe `description:"Defines the SPIFFE TLS configuration." json:"spiffe,omitempty" toml:"spiffe,omitempty" yaml:"spiffe,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` } + +// +k8s:deepcopy-gen=true + +// TCPServerHealthCheck holds the HealthCheck configuration. +type TCPServerHealthCheck struct { + Port int `json:"port,omitempty" toml:"port,omitempty,omitzero" yaml:"port,omitempty" export:"true"` + Send string `json:"send,omitempty" toml:"send,omitempty" yaml:"send,omitempty" export:"true"` + Expect string `json:"expect,omitempty" toml:"expect,omitempty" yaml:"expect,omitempty" export:"true"` + Interval ptypes.Duration `json:"interval,omitempty" toml:"interval,omitempty" yaml:"interval,omitempty" export:"true"` + UnhealthyInterval *ptypes.Duration `json:"unhealthyInterval,omitempty" toml:"unhealthyInterval,omitempty" yaml:"unhealthyInterval,omitempty" export:"true"` + Timeout ptypes.Duration `json:"timeout,omitempty" toml:"timeout,omitempty" yaml:"timeout,omitempty" export:"true"` +} + +// SetDefaults sets the default values for a TCPServerHealthCheck. +func (t *TCPServerHealthCheck) SetDefaults() { + t.Interval = DefaultHealthCheckInterval + t.Timeout = DefaultHealthCheckTimeout +} diff --git a/pkg/config/dynamic/tcp_middlewares.go b/pkg/config/dynamic/tcp_middlewares.go index 3d9624d08..1f20e8ca2 100644 --- a/pkg/config/dynamic/tcp_middlewares.go +++ b/pkg/config/dynamic/tcp_middlewares.go @@ -15,7 +15,7 @@ type TCPMiddleware struct { // TCPInFlightConn holds the TCP InFlightConn middleware configuration. // This middleware prevents services from being overwhelmed with high load, // by limiting the number of allowed simultaneous connections for one IP. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/tcp/inflightconn/ +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/tcp/inflightconn/ type TCPInFlightConn struct { // Amount defines the maximum amount of allowed simultaneous connections. // The middleware closes the connection if there are already amount connections opened. @@ -36,7 +36,7 @@ type TCPIPWhiteList struct { // TCPIPAllowList holds the TCP IPAllowList middleware configuration. // This middleware limits allowed requests based on the client IP. -// More info: https://doc.traefik.io/traefik/v3.5/middlewares/tcp/ipallowlist/ +// More info: https://doc.traefik.io/traefik/v3.6/middlewares/tcp/ipallowlist/ type TCPIPAllowList struct { // SourceRange defines the allowed IPs (or ranges of allowed IPs by using CIDR notation). SourceRange []string `json:"sourceRange,omitempty" toml:"sourceRange,omitempty" yaml:"sourceRange,omitempty"` diff --git a/pkg/config/dynamic/zz_generated.deepcopy.go b/pkg/config/dynamic/zz_generated.deepcopy.go index fb4cea3e9..6074f0998 100644 --- a/pkg/config/dynamic/zz_generated.deepcopy.go +++ b/pkg/config/dynamic/zz_generated.deepcopy.go @@ -4,7 +4,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -449,6 +449,27 @@ func (in *GrpcWeb) DeepCopy() *GrpcWeb { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HRWService) DeepCopyInto(out *HRWService) { + *out = *in + if in.Weight != nil { + in, out := &in.Weight, &out.Weight + *out = new(int) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HRWService. +func (in *HRWService) DeepCopy() *HRWService { + if in == nil { + return nil + } + out := new(HRWService) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HTTPConfiguration) DeepCopyInto(out *HTTPConfiguration) { *out = *in @@ -688,6 +709,34 @@ func (in *HealthCheck) DeepCopy() *HealthCheck { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HighestRandomWeight) DeepCopyInto(out *HighestRandomWeight) { + *out = *in + if in.Services != nil { + in, out := &in.Services, &out.Services + *out = make([]HRWService, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.HealthCheck != nil { + in, out := &in.HealthCheck, &out.HealthCheck + *out = new(HealthCheck) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HighestRandomWeight. +func (in *HighestRandomWeight) DeepCopy() *HighestRandomWeight { + if in == nil { + return nil + } + out := new(HighestRandomWeight) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IPAllowList) DeepCopyInto(out *IPAllowList) { *out = *in @@ -1037,6 +1086,11 @@ func (in *Model) DeepCopyInto(out *Model) { (*in).DeepCopyInto(*out) } in.Observability.DeepCopyInto(&out.Observability) + if in.DeniedEncodedPathCharacters != nil { + in, out := &in.DeniedEncodedPathCharacters, &out.DeniedEncodedPathCharacters + *out = new(RouterDeniedEncodedPathCharacters) + **out = **in + } return } @@ -1071,6 +1125,22 @@ func (in *PassTLSClientCert) DeepCopy() *PassTLSClientCert { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PassiveServerHealthCheck) DeepCopyInto(out *PassiveServerHealthCheck) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PassiveServerHealthCheck. +func (in *PassiveServerHealthCheck) DeepCopy() *PassiveServerHealthCheck { + if in == nil { + return nil + } + out := new(PassiveServerHealthCheck) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ProxyProtocol) DeepCopyInto(out *ProxyProtocol) { *out = *in @@ -1304,6 +1374,11 @@ func (in *Router) DeepCopyInto(out *Router) { *out = make([]string, len(*in)) copy(*out, *in) } + if in.ParentRefs != nil { + in, out := &in.ParentRefs, &out.ParentRefs + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.TLS != nil { in, out := &in.TLS, &out.TLS *out = new(RouterTLSConfig) @@ -1314,6 +1389,11 @@ func (in *Router) DeepCopyInto(out *Router) { *out = new(RouterObservabilityConfig) (*in).DeepCopyInto(*out) } + if in.DeniedEncodedPathCharacters != nil { + in, out := &in.DeniedEncodedPathCharacters, &out.DeniedEncodedPathCharacters + *out = new(RouterDeniedEncodedPathCharacters) + **out = **in + } return } @@ -1327,6 +1407,22 @@ func (in *Router) DeepCopy() *Router { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouterDeniedEncodedPathCharacters) DeepCopyInto(out *RouterDeniedEncodedPathCharacters) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouterDeniedEncodedPathCharacters. +func (in *RouterDeniedEncodedPathCharacters) DeepCopy() *RouterDeniedEncodedPathCharacters { + if in == nil { + return nil + } + out := new(RouterDeniedEncodedPathCharacters) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RouterObservabilityConfig) DeepCopyInto(out *RouterObservabilityConfig) { *out = *in @@ -1478,6 +1574,11 @@ func (in *ServersLoadBalancer) DeepCopyInto(out *ServersLoadBalancer) { *out = new(ServerHealthCheck) (*in).DeepCopyInto(*out) } + if in.PassiveHealthCheck != nil { + in, out := &in.PassiveHealthCheck, &out.PassiveHealthCheck + *out = new(PassiveServerHealthCheck) + **out = **in + } if in.PassHostHeader != nil { in, out := &in.PassHostHeader, &out.PassHostHeader *out = new(bool) @@ -1545,6 +1646,11 @@ func (in *Service) DeepCopyInto(out *Service) { *out = new(ServersLoadBalancer) (*in).DeepCopyInto(*out) } + if in.HighestRandomWeight != nil { + in, out := &in.HighestRandomWeight, &out.HighestRandomWeight + *out = new(HighestRandomWeight) + (*in).DeepCopyInto(*out) + } if in.Weighted != nil { in, out := &in.Weighted, &out.Weighted *out = new(WeightedRoundRobin) @@ -1926,6 +2032,27 @@ func (in *TCPServer) DeepCopy() *TCPServer { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TCPServerHealthCheck) DeepCopyInto(out *TCPServerHealthCheck) { + *out = *in + if in.UnhealthyInterval != nil { + in, out := &in.UnhealthyInterval, &out.UnhealthyInterval + *out = new(paersertypes.Duration) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPServerHealthCheck. +func (in *TCPServerHealthCheck) DeepCopy() *TCPServerHealthCheck { + if in == nil { + return nil + } + out := new(TCPServerHealthCheck) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TCPServersLoadBalancer) DeepCopyInto(out *TCPServersLoadBalancer) { *out = *in @@ -1944,6 +2071,11 @@ func (in *TCPServersLoadBalancer) DeepCopyInto(out *TCPServersLoadBalancer) { *out = new(int) **out = **in } + if in.HealthCheck != nil { + in, out := &in.HealthCheck, &out.HealthCheck + *out = new(TCPServerHealthCheck) + (*in).DeepCopyInto(*out) + } return } @@ -2040,6 +2172,11 @@ func (in *TCPWeightedRoundRobin) DeepCopyInto(out *TCPWeightedRoundRobin) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.HealthCheck != nil { + in, out := &in.HealthCheck, &out.HealthCheck + *out = new(HealthCheck) + **out = **in + } return } @@ -2416,6 +2553,13 @@ func (in *WRRService) DeepCopyInto(out *WRRService) { *out = new(int) **out = **in } + if in.Headers != nil { + in, out := &in.Headers, &out.Headers + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } if in.Status != nil { in, out := &in.Status, &out.Status *out = new(int) diff --git a/pkg/config/runtime/runtime_http.go b/pkg/config/runtime/runtime_http.go index 69d7e540f..f8f33fcec 100644 --- a/pkg/config/runtime/runtime_http.go +++ b/pkg/config/runtime/runtime_http.go @@ -43,7 +43,8 @@ func (c *Configuration) GetRoutersByEntryPoints(ctx context.Context, entryPoints entryPointsRouters[entryPointName][rtName] = rt } - if entryPointsCount == 0 { + // Root routers must have at least one entry point. + if entryPointsCount == 0 && rt.ParentRefs == nil { rt.AddError(errors.New("no valid entryPoint for this router"), true) logger.Error().Msg("No valid entryPoint for this router") } @@ -80,6 +81,11 @@ type RouterInfo struct { // It is the caller's responsibility to set the initial status. Status string `json:"status,omitempty"` Using []string `json:"using,omitempty"` // Effective entry points used by that router. + + // ChildRefs contains the names of child routers. + // This field is only filled during multi-layer routing computation of parentRefs, + // and used when building the runtime configuration. + ChildRefs []string `json:"-"` } // AddError adds err to r.Err, if it does not already exist. diff --git a/pkg/config/runtime/runtime_tcp.go b/pkg/config/runtime/runtime_tcp.go index b8bc08981..07473b51e 100644 --- a/pkg/config/runtime/runtime_tcp.go +++ b/pkg/config/runtime/runtime_tcp.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "slices" + "sync" "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/dynamic" @@ -87,6 +88,9 @@ type TCPServiceInfo struct { // It is the caller's responsibility to set the initial status. Status string `json:"status,omitempty"` UsedBy []string `json:"usedBy,omitempty"` // list of routers using that service + + serverStatusMu sync.RWMutex + serverStatus map[string]string // keyed by server address } // AddError adds err to s.Err, if it does not already exist. @@ -110,6 +114,33 @@ func (s *TCPServiceInfo) AddError(err error, critical bool) { } } +// UpdateServerStatus sets the status of the server in the TCPServiceInfo. +func (s *TCPServiceInfo) UpdateServerStatus(server, status string) { + s.serverStatusMu.Lock() + defer s.serverStatusMu.Unlock() + + if s.serverStatus == nil { + s.serverStatus = make(map[string]string) + } + s.serverStatus[server] = status +} + +// GetAllStatus returns all the statuses of all the servers in TCPServiceInfo. +func (s *TCPServiceInfo) GetAllStatus() map[string]string { + s.serverStatusMu.RLock() + defer s.serverStatusMu.RUnlock() + + if len(s.serverStatus) == 0 { + return nil + } + + allStatus := make(map[string]string, len(s.serverStatus)) + for k, v := range s.serverStatus { + allStatus[k] = v + } + return allStatus +} + // TCPMiddlewareInfo holds information about a currently running middleware. type TCPMiddlewareInfo struct { *dynamic.TCPMiddleware // dynamic configuration diff --git a/pkg/config/static/entrypoints.go b/pkg/config/static/entrypoints.go index ed1eb008b..f76ce08dc 100644 --- a/pkg/config/static/entrypoints.go +++ b/pkg/config/static/entrypoints.go @@ -65,12 +65,13 @@ func (ep *EntryPoint) SetDefaults() { // HTTPConfig is the HTTP configuration of an entry point. type HTTPConfig struct { - Redirections *Redirections `description:"Set of redirection" json:"redirections,omitempty" toml:"redirections,omitempty" yaml:"redirections,omitempty" export:"true"` - Middlewares []string `description:"Default middlewares for the routers linked to the entry point." json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"` - TLS *TLSConfig `description:"Default TLS configuration for the routers linked to the entry point." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - EncodeQuerySemicolons bool `description:"Defines whether request query semicolons should be URLEncoded." json:"encodeQuerySemicolons,omitempty" toml:"encodeQuerySemicolons,omitempty" yaml:"encodeQuerySemicolons,omitempty"` - SanitizePath *bool `description:"Defines whether to enable request path sanitization (removal of /./, /../ and multiple slash sequences)." json:"sanitizePath,omitempty" toml:"sanitizePath,omitempty" yaml:"sanitizePath,omitempty" export:"true"` - MaxHeaderBytes int `description:"Maximum size of request headers in bytes." json:"maxHeaderBytes,omitempty" toml:"maxHeaderBytes,omitempty" yaml:"maxHeaderBytes,omitempty" export:"true"` + Redirections *Redirections `description:"Set of redirection" json:"redirections,omitempty" toml:"redirections,omitempty" yaml:"redirections,omitempty" export:"true"` + Middlewares []string `description:"Default middlewares for the routers linked to the entry point." json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"` + TLS *TLSConfig `description:"Default TLS configuration for the routers linked to the entry point." json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + EncodedCharacters *EncodedCharacters `description:"Defines which encoded characters are allowed in the request path." json:"encodedCharacters,omitempty" toml:"encodedCharacters,omitempty" yaml:"encodedCharacters,omitempty" export:"true"` + EncodeQuerySemicolons bool `description:"Defines whether request query semicolons should be URLEncoded." json:"encodeQuerySemicolons,omitempty" toml:"encodeQuerySemicolons,omitempty" yaml:"encodeQuerySemicolons,omitempty" export:"true"` + SanitizePath *bool `description:"Defines whether to enable request path sanitization (removal of /./, /../ and multiple slash sequences)." json:"sanitizePath,omitempty" toml:"sanitizePath,omitempty" yaml:"sanitizePath,omitempty" export:"true"` + MaxHeaderBytes int `description:"Maximum size of request headers in bytes." json:"maxHeaderBytes,omitempty" toml:"maxHeaderBytes,omitempty" yaml:"maxHeaderBytes,omitempty" export:"true"` } // SetDefaults sets the default values. @@ -80,14 +81,39 @@ func (c *HTTPConfig) SetDefaults() { c.MaxHeaderBytes = http.DefaultMaxHeaderBytes } +// EncodedCharacters configures which encoded characters are allowed in the request path. +type EncodedCharacters struct { + AllowEncodedSlash bool `description:"Defines whether requests with encoded slash characters in the path are allowed." json:"allowEncodedSlash,omitempty" toml:"allowEncodedSlash,omitempty" yaml:"allowEncodedSlash,omitempty" export:"true"` + AllowEncodedBackSlash bool `description:"Defines whether requests with encoded back slash characters in the path are allowed." json:"allowEncodedBackSlash,omitempty" toml:"allowEncodedBackSlash,omitempty" yaml:"allowEncodedBackSlash,omitempty" export:"true"` + AllowEncodedNullCharacter bool `description:"Defines whether requests with encoded null characters in the path are allowed." json:"allowEncodedNullCharacter,omitempty" toml:"allowEncodedNullCharacter,omitempty" yaml:"allowEncodedNullCharacter,omitempty" export:"true"` + AllowEncodedSemicolon bool `description:"Defines whether requests with encoded semicolon characters in the path are allowed." json:"allowEncodedSemicolon,omitempty" toml:"allowEncodedSemicolon,omitempty" yaml:"allowEncodedSemicolon,omitempty" export:"true"` + AllowEncodedPercent bool `description:"Defines whether requests with encoded percent characters in the path are allowed." json:"allowEncodedPercent,omitempty" toml:"allowEncodedPercent,omitempty" yaml:"allowEncodedPercent,omitempty" export:"true"` + AllowEncodedQuestionMark bool `description:"Defines whether requests with encoded question mark characters in the path are allowed." json:"allowEncodedQuestionMark,omitempty" toml:"allowEncodedQuestionMark,omitempty" yaml:"allowEncodedQuestionMark,omitempty" export:"true"` + AllowEncodedHash bool `description:"Defines whether requests with encoded hash characters in the path are allowed." json:"allowEncodedHash,omitempty" toml:"allowEncodedHash,omitempty" yaml:"allowEncodedHash,omitempty" export:"true"` +} + +func (ec *EncodedCharacters) SetDefaults() { + ec.AllowEncodedSlash = true + ec.AllowEncodedBackSlash = true + ec.AllowEncodedNullCharacter = true + ec.AllowEncodedSemicolon = true + ec.AllowEncodedPercent = true + ec.AllowEncodedQuestionMark = true + ec.AllowEncodedHash = true +} + // HTTP2Config is the HTTP2 configuration of an entry point. type HTTP2Config struct { - MaxConcurrentStreams int32 `description:"Specifies the number of concurrent streams per connection that each client is allowed to initiate." json:"maxConcurrentStreams,omitempty" toml:"maxConcurrentStreams,omitempty" yaml:"maxConcurrentStreams,omitempty" export:"true"` + MaxConcurrentStreams int32 `description:"Specifies the number of concurrent streams per connection that each client is allowed to initiate." json:"maxConcurrentStreams,omitempty" toml:"maxConcurrentStreams,omitempty" yaml:"maxConcurrentStreams,omitempty" export:"true"` + MaxDecoderHeaderTableSize int32 `description:"Specifies the maximum size of the HTTP2 HPACK header table on the decoding (receiving from client) side." json:"maxDecoderHeaderTableSize,omitempty" toml:"maxDecoderHeaderTableSize,omitempty" yaml:"maxDecoderHeaderTableSize,omitempty" export:"true"` + MaxEncoderHeaderTableSize int32 `description:"Specifies the maximum size of the HTTP2 HPACK header table on the encoding (sending to client) side." json:"maxEncoderHeaderTableSize,omitempty" toml:"maxEncoderHeaderTableSize,omitempty" yaml:"maxEncoderHeaderTableSize,omitempty" export:"true"` } // SetDefaults sets the default values. func (c *HTTP2Config) SetDefaults() { - c.MaxConcurrentStreams = 250 // https://cs.opensource.google/go/x/net/+/cd36cc07:http2/server.go;l=58 + c.MaxConcurrentStreams = 250 // https://cs.opensource.google/go/x/net/+/cd36cc07:http2/server.go;l=58 + c.MaxDecoderHeaderTableSize = 4096 // https://cs.opensource.google/go/x/net/+/0e478a2a:http2/server.go;l=105 + c.MaxEncoderHeaderTableSize = 4096 // https://cs.opensource.google/go/x/net/+/0e478a2a:http2/server.go;l=111 } // HTTP3Config is the HTTP3 configuration of an entry point. diff --git a/pkg/config/static/experimental.go b/pkg/config/static/experimental.go index dba89fec8..32f131c14 100644 --- a/pkg/config/static/experimental.go +++ b/pkg/config/static/experimental.go @@ -4,13 +4,15 @@ import "github.com/traefik/traefik/v3/pkg/plugins" // Experimental experimental Traefik features. type Experimental struct { - Plugins map[string]plugins.Descriptor `description:"Plugins configuration." json:"plugins,omitempty" toml:"plugins,omitempty" yaml:"plugins,omitempty" export:"true"` - LocalPlugins map[string]plugins.LocalDescriptor `description:"Local plugins configuration." json:"localPlugins,omitempty" toml:"localPlugins,omitempty" yaml:"localPlugins,omitempty" export:"true"` - AbortOnPluginFailure bool `description:"Defines whether all plugins must be loaded successfully for Traefik to start." json:"abortOnPluginFailure,omitempty" toml:"abortOnPluginFailure,omitempty" yaml:"abortOnPluginFailure,omitempty" export:"true"` - FastProxy *FastProxyConfig `description:"Enables the FastProxy implementation." json:"fastProxy,omitempty" toml:"fastProxy,omitempty" yaml:"fastProxy,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` - OTLPLogs bool `description:"Enables the OpenTelemetry logs integration." json:"otlplogs,omitempty" toml:"otlplogs,omitempty" yaml:"otlplogs,omitempty" export:"true"` - KubernetesIngressNGINX bool `description:"Allow the Kubernetes Ingress NGINX provider usage." json:"kubernetesIngressNGINX,omitempty" toml:"kubernetesIngressNGINX,omitempty" yaml:"kubernetesIngressNGINX,omitempty" export:"true"` + Plugins map[string]plugins.Descriptor `description:"Plugins configuration." json:"plugins,omitempty" toml:"plugins,omitempty" yaml:"plugins,omitempty" export:"true"` + LocalPlugins map[string]plugins.LocalDescriptor `description:"Local plugins configuration." json:"localPlugins,omitempty" toml:"localPlugins,omitempty" yaml:"localPlugins,omitempty" export:"true"` + AbortOnPluginFailure bool `description:"Defines whether all plugins must be loaded successfully for Traefik to start." json:"abortOnPluginFailure,omitempty" toml:"abortOnPluginFailure,omitempty" yaml:"abortOnPluginFailure,omitempty" export:"true"` + FastProxy *FastProxyConfig `description:"Enables the FastProxy implementation." json:"fastProxy,omitempty" toml:"fastProxy,omitempty" yaml:"fastProxy,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + OTLPLogs bool `description:"Enables the OpenTelemetry logs integration." json:"otlplogs,omitempty" toml:"otlplogs,omitempty" yaml:"otlplogs,omitempty" export:"true"` + Knative bool `description:"Allow the Knative provider usage." json:"knative,omitempty" toml:"knative,omitempty" yaml:"knative,omitempty" export:"true"` + // Deprecated: KubernetesIngressNGINX provider is not an experimental feature starting with v3.6.2. Please remove its usage from the static configuration. + KubernetesIngressNGINX bool `description:"Allow the Kubernetes Ingress NGINX provider usage." json:"kubernetesIngressNGINX,omitempty" toml:"kubernetesIngressNGINX,omitempty" yaml:"kubernetesIngressNGINX,omitempty" export:"true"` // Deprecated: KubernetesGateway provider is not an experimental feature starting with v3.1. Please remove its usage from the static configuration. KubernetesGateway bool `description:"(Deprecated) Allow the Kubernetes gateway api provider usage." json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty" export:"true"` } diff --git a/pkg/config/static/static_config.go b/pkg/config/static/static_config.go index e64118963..49ced1695 100644 --- a/pkg/config/static/static_config.go +++ b/pkg/config/static/static_config.go @@ -24,6 +24,7 @@ import ( "github.com/traefik/traefik/v3/pkg/provider/kubernetes/gateway" "github.com/traefik/traefik/v3/pkg/provider/kubernetes/ingress" ingressnginx "github.com/traefik/traefik/v3/pkg/provider/kubernetes/ingress-nginx" + "github.com/traefik/traefik/v3/pkg/provider/kubernetes/knative" "github.com/traefik/traefik/v3/pkg/provider/kv/consul" "github.com/traefik/traefik/v3/pkg/provider/kv/etcd" "github.com/traefik/traefik/v3/pkg/provider/kv/redis" @@ -240,6 +241,7 @@ type Providers struct { KubernetesIngressNGINX *ingressnginx.Provider `description:"Enables Kubernetes Ingress NGINX provider." json:"kubernetesIngressNGINX,omitempty" toml:"kubernetesIngressNGINX,omitempty" yaml:"kubernetesIngressNGINX,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` KubernetesCRD *crd.Provider `description:"Enables Kubernetes CRD provider." json:"kubernetesCRD,omitempty" toml:"kubernetesCRD,omitempty" yaml:"kubernetesCRD,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` KubernetesGateway *gateway.Provider `description:"Enables Kubernetes Gateway API provider." json:"kubernetesGateway,omitempty" toml:"kubernetesGateway,omitempty" yaml:"kubernetesGateway,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` + Knative *knative.Provider `description:"Enables Knative provider." json:"knative,omitempty" toml:"knative,omitempty" yaml:"knative,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` Rest *rest.Provider `description:"Enables Rest provider." json:"rest,omitempty" toml:"rest,omitempty" yaml:"rest,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` ConsulCatalog *consulcatalog.ProviderBuilder `description:"Enables Consul Catalog provider." json:"consulCatalog,omitempty" toml:"consulCatalog,omitempty" yaml:"consulCatalog,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` Nomad *nomad.ProviderBuilder `description:"Enables Nomad provider." json:"nomad,omitempty" toml:"nomad,omitempty" yaml:"nomad,omitempty" label:"allowEmpty" file:"allowEmpty" export:"true"` @@ -313,6 +315,18 @@ func (c *Configuration) SetEffectiveConfiguration() { c.Providers.KubernetesGateway.EntryPoints = entryPoints } + // Configure Ingress NGINX provider. + if c.Providers.KubernetesIngressNGINX != nil { + var nonTLSEntryPoints []string + for epName, entryPoint := range c.EntryPoints { + if entryPoint.HTTP.TLS == nil { + nonTLSEntryPoints = append(nonTLSEntryPoints, epName) + } + } + + c.Providers.KubernetesIngressNGINX.NonTLSEntryPoints = nonTLSEntryPoints + } + // Defines the default rule syntax for the Kubernetes Ingress Provider. // This allows the provider to adapt the matcher syntax to the desired rule syntax version. if c.Core != nil && c.Providers.KubernetesIngress != nil { @@ -423,15 +437,17 @@ func (c *Configuration) ValidateConfiguration() error { } if c.Providers != nil && c.Providers.KubernetesIngressNGINX != nil { - if c.Experimental == nil || !c.Experimental.KubernetesIngressNGINX { - return errors.New("the experimental KubernetesIngressNGINX feature must be enabled to use the KubernetesIngressNGINX provider") - } - if c.Providers.KubernetesIngressNGINX.WatchNamespace != "" && c.Providers.KubernetesIngressNGINX.WatchNamespaceSelector != "" { return errors.New("watchNamespace and watchNamespaceSelector options are mutually exclusive") } } + if c.Providers != nil && c.Providers.Knative != nil { + if c.Experimental == nil || !c.Experimental.Knative { + return errors.New("the experimental Knative feature must be enabled to use the Knative provider") + } + } + if c.AccessLog != nil && c.AccessLog.OTLP != nil { if c.Experimental == nil || !c.Experimental.OTLPLogs { return errors.New("the experimental OTLPLogs feature must be enabled to use OTLP access logging") diff --git a/pkg/config/static/static_config_test.go b/pkg/config/static/static_config_test.go index a18c7c35e..b7ba5b729 100644 --- a/pkg/config/static/static_config_test.go +++ b/pkg/config/static/static_config_test.go @@ -74,7 +74,9 @@ func TestConfiguration_SetEffectiveConfiguration(t *testing.T) { MaxHeaderBytes: 1048576, }, HTTP2: &HTTP2Config{ - MaxConcurrentStreams: 250, + MaxConcurrentStreams: 250, + MaxDecoderHeaderTableSize: 4096, + MaxEncoderHeaderTableSize: 4096, }, HTTP3: nil, UDP: &UDPConfig{ @@ -120,7 +122,9 @@ func TestConfiguration_SetEffectiveConfiguration(t *testing.T) { MaxHeaderBytes: 1048576, }, HTTP2: &HTTP2Config{ - MaxConcurrentStreams: 250, + MaxConcurrentStreams: 250, + MaxDecoderHeaderTableSize: 4096, + MaxEncoderHeaderTableSize: 4096, }, HTTP3: nil, UDP: &UDPConfig{ @@ -177,7 +181,9 @@ func TestConfiguration_SetEffectiveConfiguration(t *testing.T) { MaxHeaderBytes: 1048576, }, HTTP2: &HTTP2Config{ - MaxConcurrentStreams: 250, + MaxConcurrentStreams: 250, + MaxDecoderHeaderTableSize: 4096, + MaxEncoderHeaderTableSize: 4096, }, HTTP3: nil, UDP: &UDPConfig{ @@ -238,7 +244,9 @@ func TestConfiguration_SetEffectiveConfiguration(t *testing.T) { MaxHeaderBytes: 1048576, }, HTTP2: &HTTP2Config{ - MaxConcurrentStreams: 250, + MaxConcurrentStreams: 250, + MaxDecoderHeaderTableSize: 4096, + MaxEncoderHeaderTableSize: 4096, }, HTTP3: nil, UDP: &UDPConfig{ diff --git a/pkg/healthcheck/healthcheck.go b/pkg/healthcheck/healthcheck.go index e2679d73c..e1be3343e 100644 --- a/pkg/healthcheck/healthcheck.go +++ b/pkg/healthcheck/healthcheck.go @@ -1,19 +1,24 @@ package healthcheck import ( + "bufio" "context" "errors" "fmt" "net" "net/http" + "net/http/httptrace" "net/url" "strconv" + "sync" "time" gokitmetrics "github.com/go-kit/kit/metrics" "github.com/rs/zerolog/log" + ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/config/runtime" + "golang.org/x/sync/singleflight" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials/insecure" @@ -322,3 +327,160 @@ func (shc *ServiceHealthChecker) checkHealthGRPC(ctx context.Context, serverURL return nil } + +type PassiveServiceHealthChecker struct { + serviceName string + balancer StatusSetter + metrics metricsHealthCheck + + maxFailedAttempts int + failureWindow ptypes.Duration + hasActiveHealthCheck bool + + failuresMu sync.RWMutex + failures map[string][]time.Time + + timersGroup singleflight.Group + timers sync.Map +} + +func NewPassiveHealthChecker(serviceName string, balancer StatusSetter, maxFailedAttempts int, failureWindow ptypes.Duration, hasActiveHealthCheck bool, metrics metricsHealthCheck) *PassiveServiceHealthChecker { + return &PassiveServiceHealthChecker{ + serviceName: serviceName, + balancer: balancer, + failures: make(map[string][]time.Time), + maxFailedAttempts: maxFailedAttempts, + failureWindow: failureWindow, + hasActiveHealthCheck: hasActiveHealthCheck, + metrics: metrics, + } +} + +func (p *PassiveServiceHealthChecker) WrapHandler(ctx context.Context, next http.Handler, targetURL string) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + var backendCalled bool + trace := &httptrace.ClientTrace{ + WroteHeaders: func() { + backendCalled = true + }, + WroteRequest: func(httptrace.WroteRequestInfo) { + backendCalled = true + }, + } + clientTraceCtx := httptrace.WithClientTrace(req.Context(), trace) + + codeCatcher := &codeCatcher{ + ResponseWriter: rw, + } + + next.ServeHTTP(codeCatcher, req.WithContext(clientTraceCtx)) + + if backendCalled && codeCatcher.statusCode < http.StatusInternalServerError { + p.failuresMu.Lock() + p.failures[targetURL] = nil + p.failuresMu.Unlock() + return + } + + p.failuresMu.Lock() + p.failures[targetURL] = append(p.failures[targetURL], time.Now()) + p.failuresMu.Unlock() + + if p.healthy(targetURL) { + return + } + + // We need to guarantee that only one goroutine (request) will update the status and create a timer for the target. + _, _, _ = p.timersGroup.Do(targetURL, func() (interface{}, error) { + // A timer is already running for this target; + // it means that the target is already considered unhealthy. + if _, ok := p.timers.Load(targetURL); ok { + return nil, nil + } + + p.balancer.SetStatus(ctx, targetURL, false) + p.metrics.ServiceServerUpGauge().With("service", p.serviceName, "url", targetURL).Set(0) + + // If the service has an active health check, the passive health checker should not reset the status. + // The active health check will handle the status updates. + if p.hasActiveHealthCheck { + return nil, nil + } + + go func() { + timer := time.NewTimer(time.Duration(p.failureWindow)) + defer timer.Stop() + + p.timers.Store(targetURL, timer) + + select { + case <-ctx.Done(): + case <-timer.C: + p.timers.Delete(targetURL) + + p.balancer.SetStatus(ctx, targetURL, true) + p.metrics.ServiceServerUpGauge().With("service", p.serviceName, "url", targetURL).Set(1) + } + }() + + return nil, nil + }) + }) +} + +func (p *PassiveServiceHealthChecker) healthy(targetURL string) bool { + windowStart := time.Now().Add(-time.Duration(p.failureWindow)) + + p.failuresMu.Lock() + defer p.failuresMu.Unlock() + + // Filter failures within the sliding window. + failures := p.failures[targetURL] + for i, t := range failures { + if t.After(windowStart) { + p.failures[targetURL] = failures[i:] + break + } + } + + // Check if failures exceed maxFailedAttempts. + return len(p.failures[targetURL]) < p.maxFailedAttempts +} + +type codeCatcher struct { + http.ResponseWriter + + statusCode int +} + +func (c *codeCatcher) WriteHeader(statusCode int) { + // Here we allow the overriding of the status code, + // for the health check we care about the last status code written. + c.statusCode = statusCode + c.ResponseWriter.WriteHeader(statusCode) +} + +func (c *codeCatcher) Write(bytes []byte) (int, error) { + // At the time of writing, if the status code is not set, + // or set to an informational status code (1xx), + // we set it to http.StatusOK (200). + if c.statusCode < http.StatusOK { + c.statusCode = http.StatusOK + } + + return c.ResponseWriter.Write(bytes) +} + +func (c *codeCatcher) Flush() { + if flusher, ok := c.ResponseWriter.(http.Flusher); ok { + flusher.Flush() + } +} + +func (c *codeCatcher) Hijack() (net.Conn, *bufio.ReadWriter, error) { + if h, ok := c.ResponseWriter.(http.Hijacker); ok { + return h.Hijack() + } + + return nil, nil, fmt.Errorf("not a hijacker: %T", c.ResponseWriter) +} diff --git a/pkg/healthcheck/healthcheck_tcp.go b/pkg/healthcheck/healthcheck_tcp.go new file mode 100644 index 000000000..4bab5c386 --- /dev/null +++ b/pkg/healthcheck/healthcheck_tcp.go @@ -0,0 +1,212 @@ +package healthcheck + +import ( + "context" + "errors" + "fmt" + "net" + "strconv" + "time" + + "github.com/rs/zerolog/log" + "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/config/runtime" + "github.com/traefik/traefik/v3/pkg/tcp" +) + +// maxPayloadSize is the maximum payload size that can be sent during health checks. +const maxPayloadSize = 65535 + +type TCPHealthCheckTarget struct { + Address string + TLS bool + Dialer tcp.Dialer +} +type ServiceTCPHealthChecker struct { + balancer StatusSetter + info *runtime.TCPServiceInfo + + config *dynamic.TCPServerHealthCheck + interval time.Duration + unhealthyInterval time.Duration + timeout time.Duration + + healthyTargets chan *TCPHealthCheckTarget + unhealthyTargets chan *TCPHealthCheckTarget + + serviceName string +} + +func NewServiceTCPHealthChecker(ctx context.Context, config *dynamic.TCPServerHealthCheck, service StatusSetter, info *runtime.TCPServiceInfo, targets []TCPHealthCheckTarget, serviceName string) *ServiceTCPHealthChecker { + logger := log.Ctx(ctx) + interval := time.Duration(config.Interval) + if interval <= 0 { + logger.Error().Msg("Health check interval smaller than zero, default value will be used instead.") + interval = time.Duration(dynamic.DefaultHealthCheckInterval) + } + + // If the unhealthyInterval option is not set, we use the interval option value, + // to check the unhealthy targets as often as the healthy ones. + var unhealthyInterval time.Duration + if config.UnhealthyInterval == nil { + unhealthyInterval = interval + } else { + unhealthyInterval = time.Duration(*config.UnhealthyInterval) + if unhealthyInterval <= 0 { + logger.Error().Msg("Health check unhealthy interval smaller than zero, default value will be used instead.") + unhealthyInterval = time.Duration(dynamic.DefaultHealthCheckInterval) + } + } + + timeout := time.Duration(config.Timeout) + if timeout <= 0 { + logger.Error().Msg("Health check timeout smaller than zero, default value will be used instead.") + timeout = time.Duration(dynamic.DefaultHealthCheckTimeout) + } + + if config.Send != "" && len(config.Send) > maxPayloadSize { + logger.Error().Msgf("Health check payload size exceeds maximum allowed size of %d bytes, falling back to connect only check.", maxPayloadSize) + config.Send = "" + } + + if config.Expect != "" && len(config.Expect) > maxPayloadSize { + logger.Error().Msgf("Health check expected response size exceeds maximum allowed size of %d bytes, falling back to close without response.", maxPayloadSize) + config.Expect = "" + } + + healthyTargets := make(chan *TCPHealthCheckTarget, len(targets)) + for _, target := range targets { + healthyTargets <- &target + } + unhealthyTargets := make(chan *TCPHealthCheckTarget, len(targets)) + + return &ServiceTCPHealthChecker{ + balancer: service, + info: info, + config: config, + interval: interval, + unhealthyInterval: unhealthyInterval, + timeout: timeout, + healthyTargets: healthyTargets, + unhealthyTargets: unhealthyTargets, + serviceName: serviceName, + } +} + +func (thc *ServiceTCPHealthChecker) Launch(ctx context.Context) { + go thc.healthcheck(ctx, thc.unhealthyTargets, thc.unhealthyInterval) + + thc.healthcheck(ctx, thc.healthyTargets, thc.interval) +} + +func (thc *ServiceTCPHealthChecker) healthcheck(ctx context.Context, targets chan *TCPHealthCheckTarget, interval time.Duration) { + ticker := time.NewTicker(interval) + defer ticker.Stop() + + for { + select { + case <-ctx.Done(): + return + + case <-ticker.C: + // We collect the targets to check once for all, + // to avoid rechecking a target that has been moved during the health check. + var targetsToCheck []*TCPHealthCheckTarget + hasMoreTargets := true + for hasMoreTargets { + select { + case <-ctx.Done(): + return + case target := <-targets: + targetsToCheck = append(targetsToCheck, target) + default: + hasMoreTargets = false + } + } + + // Now we can check the targets. + for _, target := range targetsToCheck { + select { + case <-ctx.Done(): + return + default: + } + + up := true + + if err := thc.executeHealthCheck(ctx, thc.config, target); err != nil { + // The context is canceled when the dynamic configuration is refreshed. + if errors.Is(err, context.Canceled) { + return + } + + log.Ctx(ctx).Warn(). + Str("targetAddress", target.Address). + Err(err). + Msg("Health check failed.") + + up = false + } + + thc.balancer.SetStatus(ctx, target.Address, up) + + var statusStr string + if up { + statusStr = runtime.StatusUp + thc.healthyTargets <- target + } else { + statusStr = runtime.StatusDown + thc.unhealthyTargets <- target + } + + thc.info.UpdateServerStatus(target.Address, statusStr) + + // TODO: add a TCP server up metric (like for HTTP). + } + } + } +} + +func (thc *ServiceTCPHealthChecker) executeHealthCheck(ctx context.Context, config *dynamic.TCPServerHealthCheck, target *TCPHealthCheckTarget) error { + addr := target.Address + if config.Port != 0 { + host, _, err := net.SplitHostPort(target.Address) + if err != nil { + return fmt.Errorf("parsing address %q: %w", target.Address, err) + } + + addr = net.JoinHostPort(host, strconv.Itoa(config.Port)) + } + + ctx, cancel := context.WithDeadline(ctx, time.Now().Add(time.Duration(config.Timeout))) + defer cancel() + + conn, err := target.Dialer.DialContext(ctx, "tcp", addr, nil) + if err != nil { + return fmt.Errorf("connecting to %s: %w", addr, err) + } + defer conn.Close() + + if err := conn.SetDeadline(time.Now().Add(thc.timeout)); err != nil { + return fmt.Errorf("setting timeout to %s: %w", thc.timeout, err) + } + + if config.Send != "" { + if _, err = conn.Write([]byte(config.Send)); err != nil { + return fmt.Errorf("sending to %s: %w", addr, err) + } + } + + if config.Expect != "" { + buf := make([]byte, len(config.Expect)) + if _, err = conn.Read(buf); err != nil { + return fmt.Errorf("reading from %s: %w", addr, err) + } + + if string(buf) != config.Expect { + return errors.New("unexpected heath check response") + } + } + + return nil +} diff --git a/pkg/healthcheck/healthcheck_tcp_test.go b/pkg/healthcheck/healthcheck_tcp_test.go new file mode 100644 index 000000000..70f91e8a8 --- /dev/null +++ b/pkg/healthcheck/healthcheck_tcp_test.go @@ -0,0 +1,759 @@ +package healthcheck + +import ( + "context" + "crypto/tls" + "crypto/x509" + "net" + "strings" + "sync" + "testing" + "time" + + "github.com/rs/zerolog/log" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + ptypes "github.com/traefik/paerser/types" + "github.com/traefik/traefik/v3/pkg/config/dynamic" + truntime "github.com/traefik/traefik/v3/pkg/config/runtime" + "github.com/traefik/traefik/v3/pkg/tcp" +) + +var localhostCert = []byte(`-----BEGIN CERTIFICATE----- +MIIDJzCCAg+gAwIBAgIUe3vnWg3cTbflL6kz2TyPUxmV8Y4wDQYJKoZIhvcNAQEL +BQAwFjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wIBcNMjUwMzA1MjAwOTM4WhgPMjA1 +NTAyMjYyMDA5MzhaMBYxFDASBgNVBAMMC2V4YW1wbGUuY29tMIIBIjANBgkqhkiG +9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4Mm4Sp6xzJvFZJWAv/KVmI1krywiuef8Fhlf +JR2M0caKixjBcNt4U8KwrzIrqL+8nilbps1QuwpQ09+6ztlbUXUL6DqR8ZC+4oCp +gOZ3yyVX2vhMigkATbQyJrX/WVjWSHD5rIUBP2BrsaYLt1qETnFP9wwQ3YEi7V4l +c4+jDrZOtJvrv+tRClt9gQJVgkr7Y30X+dx+rsh+ROaA2+/VTDX0qtoqd/4fjhcJ +OY9VLm0eU66VUMyOTNeUm6ZAXRBp/EonIM1FXOlj82S0pZQbPrvyWWqWoAjtPvLU +qRzqp/BQJqx3EHz1dP6s+xUjP999B+7jhiHoFhZ/bfVVlx8XkwIDAQABo2swaTAd +BgNVHQ4EFgQUhJiJ37LW6RODCpBPAApG1zQxFtAwHwYDVR0jBBgwFoAUhJiJ37LW +6RODCpBPAApG1zQxFtAwDwYDVR0TAQH/BAUwAwEB/zAWBgNVHREEDzANggtleGFt +cGxlLmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAfnDPHllA1TFlQ6zY46tqM20d68bR +kXeGMKLoaATFPbDea5H8/GM5CU6CPD7RUuEB9CvxvaM0aOInxkgstozG7BOr8hcs +WS9fMgM0oO5yGiSOv+Qa0Rc0BFb6A1fUJRta5MI5DTdTJLoyoRX/5aocSI34T67x +ULbkJvVXw6hnx/KZ65apNobfmVQSy7DR8Fo82eB4hSoaLpXyUUTLmctGgrRCoKof +GVUJfKsDJ4Ts8WIR1np74flSoxksWSHEOYk79AZOPANYgJwPMMiiZKsKm17GBoGu +DxI0om4eX8GaSSZAtG6TOt3O3v1oCjKNsAC+u585HN0x0MFA33TUzC15NA== +-----END CERTIFICATE-----`) + +var localhostKey = []byte(`-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDgybhKnrHMm8Vk +lYC/8pWYjWSvLCK55/wWGV8lHYzRxoqLGMFw23hTwrCvMiuov7yeKVumzVC7ClDT +37rO2VtRdQvoOpHxkL7igKmA5nfLJVfa+EyKCQBNtDImtf9ZWNZIcPmshQE/YGux +pgu3WoROcU/3DBDdgSLtXiVzj6MOtk60m+u/61EKW32BAlWCSvtjfRf53H6uyH5E +5oDb79VMNfSq2ip3/h+OFwk5j1UubR5TrpVQzI5M15SbpkBdEGn8SicgzUVc6WPz +ZLSllBs+u/JZapagCO0+8tSpHOqn8FAmrHcQfPV0/qz7FSM/330H7uOGIegWFn9t +9VWXHxeTAgMBAAECggEALinfGhv7Iaz/3cdCOKlGBZ1MBxmGTC2TPKqbOpEWAWLH +wwcjetznmjQKewBPrQkrYEPYGapioPbeYJS61Y4XzeO+vUOCA10ZhoSrytgJ1ANo +RoTlmxd8I3kVL5QCy8ONxjTFYaOy/OP9We9iypXhRAbLSE4HDKZfmOXTxSbDctql +Kq7uV3LX1KCfr9C6M8d79a0Rdr4p8IXp8MOg3tXq6n75vZbepRFyAujhg7o/kkTp +lgv87h89lrK97K+AjqtvCIT3X3VXfA+LYp3AoQFdOluKgyJT221MyHkTeI/7gggt +Z57lVGD71UJH/LGUJWrraJqXd9uDxZWprD/s66BIAQKBgQD8CtHUJ/VuS7gP0ebN +688zrmRtENj6Gqi+URm/Pwgr9b7wKKlf9jjhg5F/ue+BgB7/nK6N7yJ4Xx3JJ5ox +LqsRGLFa4fDBxogF/FN27obD8naOxe2wS1uTjM6LSrvdJ+HjeNEwHYhjuDjTAHj5 +VVEMagZWgkE4jBiFUYefiYLsAQKBgQDkUVdW8cXaYri5xxDW86JNUzI1tUPyd6I+ +AkOHV/V0y2zpwTHVLcETRpdVGpc5TH3J5vWf+5OvSz6RDTGjv7blDb8vB/kVkFmn +uXTi0dB9P+SYTsm+X3V7hOAFsyVYZ1D9IFsKUyMgxMdF+qgERjdPKx5IdLV/Jf3q +P9pQ922TkwKBgCKllhyU9Z8Y14+NKi4qeUxAb9uyUjFnUsT+vwxULNpmKL44yLfB +UCZoAKtPMwZZR2mZ70Dhm5pycNTDFeYm5Ssvesnkf0UT9oTkH9EcjvgGr5eGy9rN +MSSCWa46MsL/BYVQiWkU1jfnDiCrUvXrbX3IYWCo/TA5yfEhuQQMUiwBAoGADyzo +5TqEsBNHu/FjSSZAb2tMNw2pSoBxJDX6TxClm/G5d4AD0+uKncFfZaSy0HgpFDZp +tQx/sHML4ZBC8GNZwLe9MV8SS0Cg9Oj6v+i6Ntj8VLNH7YNix6b5TOevX8TeOTTh +WDpWZ2Ms65XRfRc9reFrzd0UAzN/QQaleCQ6AEkCgYBe4Ucows7JGbv7fNkz3nb1 +kyH+hk9ecnq/evDKX7UUxKO1wwTi74IYKgcRB2uPLpHKL35gPz+LAfCphCW5rwpR +lvDhS+Pi/1KCBJxLHMv+V/WrckDRgHFnAhDaBZ+2vI/s09rKDnpjcTzV7x22kL0b +XIJCEEE8JZ4AXIZ+IcB6LA== +-----END PRIVATE KEY-----`) + +func TestNewServiceTCPHealthChecker(t *testing.T) { + testCases := []struct { + desc string + config *dynamic.TCPServerHealthCheck + expectedInterval time.Duration + expectedTimeout time.Duration + }{ + { + desc: "default values", + config: &dynamic.TCPServerHealthCheck{}, + expectedInterval: time.Duration(dynamic.DefaultHealthCheckInterval), + expectedTimeout: time.Duration(dynamic.DefaultHealthCheckTimeout), + }, + { + desc: "out of range values", + config: &dynamic.TCPServerHealthCheck{ + Interval: ptypes.Duration(-time.Second), + Timeout: ptypes.Duration(-time.Second), + }, + expectedInterval: time.Duration(dynamic.DefaultHealthCheckInterval), + expectedTimeout: time.Duration(dynamic.DefaultHealthCheckTimeout), + }, + { + desc: "custom durations", + config: &dynamic.TCPServerHealthCheck{ + Interval: ptypes.Duration(time.Second * 10), + Timeout: ptypes.Duration(time.Second * 5), + }, + expectedInterval: time.Second * 10, + expectedTimeout: time.Second * 5, + }, + { + desc: "interval shorter than timeout", + config: &dynamic.TCPServerHealthCheck{ + Interval: ptypes.Duration(time.Second), + Timeout: ptypes.Duration(time.Second * 5), + }, + expectedInterval: time.Second, + expectedTimeout: time.Second * 5, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + healthChecker := NewServiceTCPHealthChecker(t.Context(), test.config, nil, nil, nil, "") + assert.Equal(t, test.expectedInterval, healthChecker.interval) + assert.Equal(t, test.expectedTimeout, healthChecker.timeout) + }) + } +} + +func TestServiceTCPHealthChecker_executeHealthCheck_connection(t *testing.T) { + testCases := []struct { + desc string + address string + config *dynamic.TCPServerHealthCheck + expectedAddress string + }{ + { + desc: "no port override - uses original address", + address: "127.0.0.1:8080", + config: &dynamic.TCPServerHealthCheck{Port: 0}, + expectedAddress: "127.0.0.1:8080", + }, + { + desc: "port override - uses overridden port", + address: "127.0.0.1:8080", + config: &dynamic.TCPServerHealthCheck{Port: 9090}, + expectedAddress: "127.0.0.1:9090", + }, + { + desc: "IPv6 address with port override", + address: "[::1]:8080", + config: &dynamic.TCPServerHealthCheck{Port: 9090}, + expectedAddress: "[::1]:9090", + }, + { + desc: "successful connection without port override", + address: "localhost:3306", + config: &dynamic.TCPServerHealthCheck{Port: 0}, + expectedAddress: "localhost:3306", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + // Create a mock dialer that records the address it was asked to dial. + var gotAddress string + mockDialer := &dialerMock{ + onDial: func(network, addr string) (net.Conn, error) { + gotAddress = addr + return &connMock{}, nil + }, + } + + targets := []TCPHealthCheckTarget{{ + Address: test.address, + Dialer: mockDialer, + }} + healthChecker := NewServiceTCPHealthChecker(t.Context(), test.config, nil, nil, targets, "test") + + // Execute a health check to see what address it tries to connect to. + err := healthChecker.executeHealthCheck(t.Context(), test.config, &targets[0]) + require.NoError(t, err) + + // Verify that the health check attempted to connect to the expected address. + assert.Equal(t, test.expectedAddress, gotAddress) + }) + } +} + +func TestServiceTCPHealthChecker_executeHealthCheck_payloadHandling(t *testing.T) { + testCases := []struct { + desc string + config *dynamic.TCPServerHealthCheck + mockResponse string + expectedSentData string + expectedSuccess bool + }{ + { + desc: "successful send and expect", + config: &dynamic.TCPServerHealthCheck{ + Send: "PING", + Expect: "PONG", + }, + mockResponse: "PONG", + expectedSentData: "PING", + expectedSuccess: true, + }, + { + desc: "send without expect", + config: &dynamic.TCPServerHealthCheck{ + Send: "STATUS", + Expect: "", + }, + expectedSentData: "STATUS", + expectedSuccess: true, + }, + { + desc: "send without expect, ignores response", + config: &dynamic.TCPServerHealthCheck{ + Send: "STATUS", + }, + mockResponse: strings.Repeat("A", maxPayloadSize+1), + expectedSentData: "STATUS", + expectedSuccess: true, + }, + { + desc: "expect without send", + config: &dynamic.TCPServerHealthCheck{ + Expect: "READY", + }, + mockResponse: "READY", + expectedSuccess: true, + }, + { + desc: "wrong response received", + config: &dynamic.TCPServerHealthCheck{ + Send: "PING", + Expect: "PONG", + }, + mockResponse: "WRONG", + expectedSentData: "PING", + expectedSuccess: false, + }, + { + desc: "send payload too large - gets truncated", + config: &dynamic.TCPServerHealthCheck{ + Send: strings.Repeat("A", maxPayloadSize+1), // Will be truncated to empty + Expect: "OK", + }, + mockResponse: "OK", + expectedSuccess: true, + }, + { + desc: "expect payload too large - gets truncated", + config: &dynamic.TCPServerHealthCheck{ + Send: "PING", + Expect: strings.Repeat("B", maxPayloadSize+1), // Will be truncated to empty + }, + expectedSentData: "PING", + expectedSuccess: true, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + var sentData []byte + mockConn := &connMock{ + writeFunc: func(data []byte) (int, error) { + sentData = append([]byte{}, data...) + return len(data), nil + }, + readFunc: func(buf []byte) (int, error) { + return copy(buf, test.mockResponse), nil + }, + } + + mockDialer := &dialerMock{ + onDial: func(network, addr string) (net.Conn, error) { + return mockConn, nil + }, + } + + targets := []TCPHealthCheckTarget{{ + Address: "127.0.0.1:8080", + TLS: false, + Dialer: mockDialer, + }} + + healthChecker := NewServiceTCPHealthChecker(t.Context(), test.config, nil, nil, targets, "test") + + err := healthChecker.executeHealthCheck(t.Context(), test.config, &targets[0]) + + if test.expectedSuccess { + assert.NoError(t, err, "Health check should succeed") + } else { + assert.Error(t, err, "Health check should fail") + } + + assert.Equal(t, test.expectedSentData, string(sentData), "Should send the expected data") + }) + } +} + +func TestServiceTCPHealthChecker_Launch(t *testing.T) { + testCases := []struct { + desc string + server *sequencedTCPServer + config *dynamic.TCPServerHealthCheck + expNumRemovedServers int + expNumUpsertedServers int + targetStatus string + }{ + { + desc: "connection-only healthy server staying healthy", + server: newTCPServer(t, + false, + tcpMockSequence{accept: true}, + tcpMockSequence{accept: true}, + tcpMockSequence{accept: true}, + ), + config: &dynamic.TCPServerHealthCheck{ + Interval: ptypes.Duration(time.Millisecond * 50), + Timeout: ptypes.Duration(time.Millisecond * 40), + }, + expNumRemovedServers: 0, + expNumUpsertedServers: 3, // 3 health check sequences + targetStatus: truntime.StatusUp, + }, + { + desc: "connection-only healthy server becoming unhealthy", + server: newTCPServer(t, + false, + tcpMockSequence{accept: true}, + tcpMockSequence{accept: false}, + ), + config: &dynamic.TCPServerHealthCheck{ + Interval: ptypes.Duration(time.Millisecond * 50), + Timeout: ptypes.Duration(time.Millisecond * 40), + }, + expNumRemovedServers: 1, + expNumUpsertedServers: 1, + targetStatus: truntime.StatusDown, + }, + { + desc: "connection-only server toggling unhealthy to healthy", + server: newTCPServer(t, + false, + tcpMockSequence{accept: false}, + tcpMockSequence{accept: true}, + ), + config: &dynamic.TCPServerHealthCheck{ + Interval: ptypes.Duration(time.Millisecond * 50), + Timeout: ptypes.Duration(time.Millisecond * 40), + }, + expNumRemovedServers: 1, // 1 failure call + expNumUpsertedServers: 1, // 1 success call + targetStatus: truntime.StatusUp, + }, + { + desc: "connection-only server toggling healthy to unhealthy to healthy", + server: newTCPServer(t, + false, + tcpMockSequence{accept: true}, + tcpMockSequence{accept: false}, + tcpMockSequence{accept: true}, + ), + config: &dynamic.TCPServerHealthCheck{ + Interval: ptypes.Duration(time.Millisecond * 50), + Timeout: ptypes.Duration(time.Millisecond * 40), + }, + expNumRemovedServers: 1, + expNumUpsertedServers: 2, + targetStatus: truntime.StatusUp, + }, + { + desc: "send/expect healthy server staying healthy", + server: newTCPServer(t, + false, + tcpMockSequence{accept: true, payloadIn: "PING", payloadOut: "PONG"}, + tcpMockSequence{accept: true, payloadIn: "PING", payloadOut: "PONG"}, + ), + config: &dynamic.TCPServerHealthCheck{ + Send: "PING", + Expect: "PONG", + Interval: ptypes.Duration(time.Millisecond * 50), + Timeout: ptypes.Duration(time.Millisecond * 40), + }, + expNumRemovedServers: 0, + expNumUpsertedServers: 2, // 2 successful health checks + targetStatus: truntime.StatusUp, + }, + { + desc: "send/expect server with wrong response", + server: newTCPServer(t, + false, + tcpMockSequence{accept: true, payloadIn: "PING", payloadOut: "PONG"}, + tcpMockSequence{accept: true, payloadIn: "PING", payloadOut: "WRONG"}, + ), + config: &dynamic.TCPServerHealthCheck{ + Send: "PING", + Expect: "PONG", + Interval: ptypes.Duration(time.Millisecond * 50), + Timeout: ptypes.Duration(time.Millisecond * 40), + }, + expNumRemovedServers: 1, + expNumUpsertedServers: 1, + targetStatus: truntime.StatusDown, + }, + { + desc: "TLS healthy server staying healthy", + server: newTCPServer(t, + true, + tcpMockSequence{accept: true, payloadIn: "HELLO", payloadOut: "WORLD"}, + ), + config: &dynamic.TCPServerHealthCheck{ + Send: "HELLO", + Expect: "WORLD", + Interval: ptypes.Duration(time.Millisecond * 500), + Timeout: ptypes.Duration(time.Millisecond * 2000), // Even longer timeout for TLS handshake + }, + expNumRemovedServers: 0, + expNumUpsertedServers: 1, // 1 TLS health check sequence + targetStatus: truntime.StatusUp, + }, + { + desc: "send-only healthcheck (no expect)", + server: newTCPServer(t, + false, + tcpMockSequence{accept: true, payloadIn: "STATUS"}, + tcpMockSequence{accept: true, payloadIn: "STATUS"}, + ), + config: &dynamic.TCPServerHealthCheck{ + Send: "STATUS", + Interval: ptypes.Duration(time.Millisecond * 50), + Timeout: ptypes.Duration(time.Millisecond * 40), + }, + expNumRemovedServers: 0, + expNumUpsertedServers: 2, + targetStatus: truntime.StatusUp, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + ctx, cancel := context.WithCancel(log.Logger.WithContext(t.Context())) + defer cancel() + + test.server.Start(t) + + dialerManager := tcp.NewDialerManager(nil) + dialerManager.Update(map[string]*dynamic.TCPServersTransport{"default@internal": { + TLS: &dynamic.TLSClientConfig{ + InsecureSkipVerify: true, + ServerName: "example.com", + }, + }}) + + dialer, err := dialerManager.Build(&dynamic.TCPServersLoadBalancer{}, test.server.TLS) + require.NoError(t, err) + + targets := []TCPHealthCheckTarget{ + { + Address: test.server.Addr.String(), + TLS: test.server.TLS, + Dialer: dialer, + }, + } + + lb := &testLoadBalancer{} + serviceInfo := &truntime.TCPServiceInfo{} + + service := NewServiceTCPHealthChecker(ctx, test.config, lb, serviceInfo, targets, "serviceName") + + go service.Launch(ctx) + + // How much time to wait for the health check to actually complete. + deadline := time.Now().Add(200 * time.Millisecond) + // TLS handshake can take much longer. + if test.server.TLS { + deadline = time.Now().Add(1000 * time.Millisecond) + } + + // Wait for all health checks to complete deterministically + for i := range test.server.StatusSequence { + test.server.Next() + + initialUpserted := lb.numUpsertedServers + initialRemoved := lb.numRemovedServers + + for time.Now().Before(deadline) { + time.Sleep(5 * time.Millisecond) + if lb.numUpsertedServers > initialUpserted || lb.numRemovedServers > initialRemoved { + // Stop the health checker immediately after the last expected sequence completes + // to prevent extra health checks from firing and modifying the counters. + if i == len(test.server.StatusSequence)-1 { + cancel() + } + break + } + } + } + + assert.Equal(t, test.expNumRemovedServers, lb.numRemovedServers, "removed servers") + assert.Equal(t, test.expNumUpsertedServers, lb.numUpsertedServers, "upserted servers") + assert.Equal(t, map[string]string{test.server.Addr.String(): test.targetStatus}, serviceInfo.GetAllStatus()) + }) + } +} + +func TestServiceTCPHealthChecker_differentIntervals(t *testing.T) { + // Test that unhealthy servers are checked more frequently than healthy servers + // when UnhealthyInterval is set to a lower value than Interval + ctx, cancel := context.WithCancel(t.Context()) + t.Cleanup(cancel) + + // Create a healthy TCP server that always accepts connections + healthyServer := newTCPServer(t, false, + tcpMockSequence{accept: true}, tcpMockSequence{accept: true}, tcpMockSequence{accept: true}, + tcpMockSequence{accept: true}, tcpMockSequence{accept: true}, + ) + healthyServer.Start(t) + + // Create an unhealthy TCP server that always rejects connections + unhealthyServer := newTCPServer(t, false, + tcpMockSequence{accept: false}, tcpMockSequence{accept: false}, tcpMockSequence{accept: false}, + tcpMockSequence{accept: false}, tcpMockSequence{accept: false}, tcpMockSequence{accept: false}, + tcpMockSequence{accept: false}, tcpMockSequence{accept: false}, tcpMockSequence{accept: false}, + tcpMockSequence{accept: false}, + ) + unhealthyServer.Start(t) + + lb := &testLoadBalancer{RWMutex: &sync.RWMutex{}} + + // Set normal interval to 500ms but unhealthy interval to 50ms + // This means unhealthy servers should be checked 10x more frequently + config := &dynamic.TCPServerHealthCheck{ + Interval: ptypes.Duration(500 * time.Millisecond), + UnhealthyInterval: pointer(ptypes.Duration(50 * time.Millisecond)), + Timeout: ptypes.Duration(100 * time.Millisecond), + } + + // Set up dialer manager + dialerManager := tcp.NewDialerManager(nil) + dialerManager.Update(map[string]*dynamic.TCPServersTransport{ + "default@internal": { + DialTimeout: ptypes.Duration(100 * time.Millisecond), + DialKeepAlive: ptypes.Duration(100 * time.Millisecond), + }, + }) + + // Get dialer for targets + dialer, err := dialerManager.Build(&dynamic.TCPServersLoadBalancer{}, false) + require.NoError(t, err) + + targets := []TCPHealthCheckTarget{ + {Address: healthyServer.Addr.String(), TLS: false, Dialer: dialer}, + {Address: unhealthyServer.Addr.String(), TLS: false, Dialer: dialer}, + } + + serviceInfo := &truntime.TCPServiceInfo{} + hc := NewServiceTCPHealthChecker(ctx, config, lb, serviceInfo, targets, "test-service") + + wg := sync.WaitGroup{} + wg.Add(1) + + go func() { + hc.Launch(ctx) + wg.Done() + }() + + // Let it run for 2 seconds to see the different check frequencies + select { + case <-time.After(2 * time.Second): + cancel() + case <-ctx.Done(): + } + + wg.Wait() + + lb.Lock() + defer lb.Unlock() + + // The unhealthy server should be checked more frequently (50ms interval) + // compared to the healthy server (500ms interval), so we should see + // significantly more "removed" events than "upserted" events + assert.Greater(t, lb.numRemovedServers, lb.numUpsertedServers, "unhealthy servers checked more frequently") +} + +type tcpMockSequence struct { + accept bool + payloadIn string + payloadOut string +} + +type sequencedTCPServer struct { + Addr *net.TCPAddr + StatusSequence []tcpMockSequence + TLS bool + release chan struct{} +} + +func newTCPServer(t *testing.T, tlsEnabled bool, statusSequence ...tcpMockSequence) *sequencedTCPServer { + t.Helper() + + addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0") + require.NoError(t, err) + listener, err := net.ListenTCP("tcp", addr) + require.NoError(t, err) + + tcpAddr, ok := listener.Addr().(*net.TCPAddr) + require.True(t, ok) + + listener.Close() + + return &sequencedTCPServer{ + Addr: tcpAddr, + TLS: tlsEnabled, + StatusSequence: statusSequence, + release: make(chan struct{}), + } +} + +func (s *sequencedTCPServer) Next() { + s.release <- struct{}{} +} + +func (s *sequencedTCPServer) Start(t *testing.T) { + t.Helper() + + go func() { + var listener net.Listener + + for _, seq := range s.StatusSequence { + <-s.release + if listener != nil { + listener.Close() + } + + if !seq.accept { + continue + } + + lis, err := net.ListenTCP("tcp", s.Addr) + require.NoError(t, err) + + listener = lis + + if s.TLS { + cert, err := tls.X509KeyPair(localhostCert, localhostKey) + require.NoError(t, err) + + x509Cert, err := x509.ParseCertificate(cert.Certificate[0]) + require.NoError(t, err) + + certpool := x509.NewCertPool() + certpool.AddCert(x509Cert) + + listener = tls.NewListener( + listener, + &tls.Config{ + RootCAs: certpool, + Certificates: []tls.Certificate{cert}, + InsecureSkipVerify: true, + ServerName: "example.com", + MinVersion: tls.VersionTLS12, + MaxVersion: tls.VersionTLS12, + ClientAuth: tls.VerifyClientCertIfGiven, + ClientCAs: certpool, + }, + ) + } + + conn, err := listener.Accept() + require.NoError(t, err) + t.Cleanup(func() { + _ = conn.Close() + }) + + // For TLS connections, perform handshake first + if s.TLS { + if tlsConn, ok := conn.(*tls.Conn); ok { + if err := tlsConn.Handshake(); err != nil { + continue // Skip this sequence on handshake failure + } + } + } + + if seq.payloadIn == "" { + continue + } + + buf := make([]byte, len(seq.payloadIn)) + n, err := conn.Read(buf) + require.NoError(t, err) + + recv := strings.TrimSpace(string(buf[:n])) + + switch recv { + case seq.payloadIn: + if _, err := conn.Write([]byte(seq.payloadOut)); err != nil { + t.Errorf("failed to write payload: %v", err) + } + default: + if _, err := conn.Write([]byte("FAULT\n")); err != nil { + t.Errorf("failed to write payload: %v", err) + } + } + } + + defer close(s.release) + }() +} + +type dialerMock struct { + onDial func(network, addr string) (net.Conn, error) +} + +func (dm *dialerMock) Dial(network, addr string, _ tcp.ClientConn) (net.Conn, error) { + return dm.onDial(network, addr) +} + +func (dm *dialerMock) DialContext(_ context.Context, network, addr string, _ tcp.ClientConn) (net.Conn, error) { + return dm.onDial(network, addr) +} + +func (dm *dialerMock) TerminationDelay() time.Duration { + return 0 +} + +type connMock struct { + writeFunc func([]byte) (int, error) + readFunc func([]byte) (int, error) +} + +func (cm *connMock) Read(b []byte) (n int, err error) { + if cm.readFunc != nil { + return cm.readFunc(b) + } + return 0, nil +} + +func (cm *connMock) Write(b []byte) (n int, err error) { + if cm.writeFunc != nil { + return cm.writeFunc(b) + } + return len(b), nil +} + +func (cm *connMock) Close() error { return nil } + +func (cm *connMock) LocalAddr() net.Addr { return &net.TCPAddr{} } + +func (cm *connMock) RemoteAddr() net.Addr { return &net.TCPAddr{} } + +func (cm *connMock) SetDeadline(_ time.Time) error { return nil } + +func (cm *connMock) SetReadDeadline(_ time.Time) error { return nil } + +func (cm *connMock) SetWriteDeadline(_ time.Time) error { return nil } diff --git a/pkg/healthcheck/healthcheck_test.go b/pkg/healthcheck/healthcheck_test.go index 056a62a49..cf2bdf2a4 100644 --- a/pkg/healthcheck/healthcheck_test.go +++ b/pkg/healthcheck/healthcheck_test.go @@ -66,6 +66,8 @@ func TestNewServiceHealthChecker_durations(t *testing.T) { for _, test := range testCases { t.Run(test.desc, func(t *testing.T) { + t.Parallel() + healthChecker := NewServiceHealthChecker(t.Context(), nil, test.config, nil, nil, http.DefaultTransport, nil, "") assert.Equal(t, test.expInterval, healthChecker.interval) assert.Equal(t, test.expTimeout, healthChecker.timeout) diff --git a/pkg/middlewares/accesslog/field_middleware.go b/pkg/middlewares/accesslog/field_middleware.go index 4d439bc25..d0e56e920 100644 --- a/pkg/middlewares/accesslog/field_middleware.go +++ b/pkg/middlewares/accesslog/field_middleware.go @@ -2,6 +2,7 @@ package accesslog import ( "net/http" + "strings" "time" "github.com/rs/zerolog/log" @@ -76,3 +77,37 @@ func InitServiceFields(rw http.ResponseWriter, req *http.Request, next http.Hand next.ServeHTTP(rw, req) } + +const separator = " -> " + +// ConcatFieldHandler concatenates field values instead of overriding them. +type ConcatFieldHandler struct { + next http.Handler + name string + value string +} + +// NewConcatFieldHandler creates a ConcatField handler that concatenates values. +func NewConcatFieldHandler(next http.Handler, name, value string) http.Handler { + return &ConcatFieldHandler{next: next, name: name, value: value} +} + +func (c *ConcatFieldHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + table := GetLogData(req) + if table == nil { + c.next.ServeHTTP(rw, req) + return + } + + // Check if field already exists and concatenate if so + if existingValue, exists := table.Core[c.name]; exists && existingValue != nil { + if existingStr, ok := existingValue.(string); ok && strings.TrimSpace(existingStr) != "" { + table.Core[c.name] = existingStr + separator + c.value + c.next.ServeHTTP(rw, req) + return + } + } + + table.Core[c.name] = c.value + c.next.ServeHTTP(rw, req) +} diff --git a/pkg/middlewares/accesslog/field_middleware_test.go b/pkg/middlewares/accesslog/field_middleware_test.go new file mode 100644 index 000000000..50a8a3406 --- /dev/null +++ b/pkg/middlewares/accesslog/field_middleware_test.go @@ -0,0 +1,96 @@ +package accesslog + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestConcatFieldHandler_ServeHTTP(t *testing.T) { + testCases := []struct { + desc string + existingValue interface{} + newValue string + expectedResult string + }{ + { + desc: "first router - no existing value", + existingValue: nil, + newValue: "router1", + expectedResult: "router1", + }, + { + desc: "second router - concatenate with existing string", + existingValue: "router1", + newValue: "router2", + expectedResult: "router1 -> router2", + }, + { + desc: "third router - concatenate with existing chain", + existingValue: "router1 -> router2", + newValue: "router3", + expectedResult: "router1 -> router2 -> router3", + }, + { + desc: "empty existing value - treat as first", + existingValue: " ", + newValue: "router1", + expectedResult: "router1", + }, + { + desc: "non-string existing value - replace with new value", + existingValue: 123, + newValue: "router1", + expectedResult: "router1", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + logData := &LogData{ + Core: CoreLogData{}, + } + if test.existingValue != nil { + logData.Core[RouterName] = test.existingValue + } + + req := httptest.NewRequest(http.MethodGet, "/test", nil) + req = req.WithContext(context.WithValue(req.Context(), DataTableKey, logData)) + + handler := NewConcatFieldHandler(nextHandler, RouterName, test.newValue) + + rec := httptest.NewRecorder() + handler.ServeHTTP(rec, req) + + assert.Equal(t, test.expectedResult, logData.Core[RouterName]) + assert.Equal(t, http.StatusOK, rec.Code) + }) + } +} + +func TestConcatFieldHandler_ServeHTTP_NoLogData(t *testing.T) { + nextHandlerCalled := false + nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + nextHandlerCalled = true + w.WriteHeader(http.StatusOK) + }) + + handler := NewConcatFieldHandler(nextHandler, RouterName, "router1") + + // Create request without LogData in context. + req := httptest.NewRequest(http.MethodGet, "/test", nil) + rec := httptest.NewRecorder() + + handler.ServeHTTP(rec, req) + + // Verify next handler was called and no panic occurred. + assert.True(t, nextHandlerCalled) + assert.Equal(t, http.StatusOK, rec.Code) +} diff --git a/pkg/middlewares/accesslog/logger_test.go b/pkg/middlewares/accesslog/logger_test.go index 2da72991e..8062f59eb 100644 --- a/pkg/middlewares/accesslog/logger_test.go +++ b/pkg/middlewares/accesslog/logger_test.go @@ -1180,6 +1180,83 @@ func logWriterTestHandlerFunc(rw http.ResponseWriter, r *http.Request) { rw.WriteHeader(testStatus) } +func TestConcatFieldHandler_LoggerIntegration(t *testing.T) { + logFilePath := filepath.Join(t.TempDir(), "access.log") + config := &otypes.AccessLog{FilePath: logFilePath, Format: CommonFormat} + + logger, err := NewHandler(t.Context(), config) + require.NoError(t, err) + t.Cleanup(func() { + err := logger.Close() + require.NoError(t, err) + }) + + req := &http.Request{ + Header: map[string][]string{ + "User-Agent": {testUserAgent}, + "Referer": {testReferer}, + }, + Proto: testProto, + Host: testHostname, + Method: testMethod, + RemoteAddr: fmt.Sprintf("%s:%d", testHostname, testPort), + URL: &url.URL{ + User: url.UserPassword(testUsername, ""), + Path: testPath, + }, + Body: io.NopCloser(bytes.NewReader([]byte("testdata"))), + } + + chain := alice.New() + chain = chain.Append(capture.Wrap) + + // Injection of the observability variables in the request context. + chain = chain.Append(func(next http.Handler) (http.Handler, error) { + return observability.WithObservabilityHandler(next, observability.Observability{ + AccessLogsEnabled: true, + }), nil + }) + + chain = chain.Append(logger.AliceConstructor()) + + // Simulate multi-layer routing with concatenated router names + var handler http.Handler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + logData := GetLogData(r) + if logData != nil { + logData.Core[ServiceURL] = testServiceName + logData.Core[OriginStatus] = testStatus + logData.Core[OriginContentSize] = testContentSize + logData.Core[RetryAttempts] = testRetryAttempts + logData.Core[StartUTC] = testStart.UTC() + logData.Core[StartLocal] = testStart.Local() + } + rw.WriteHeader(testStatus) + if _, err := rw.Write([]byte(testContent)); err != nil { + http.Error(rw, err.Error(), http.StatusInternalServerError) + } + }) + + // Create chain of ConcatFieldHandlers to simulate multi-layer routing + handler = NewConcatFieldHandler(handler, RouterName, "child-router") + handler = NewConcatFieldHandler(handler, RouterName, "parent-router") + handler = NewConcatFieldHandler(handler, RouterName, "root-router") + + finalHandler, err := chain.Then(handler) + require.NoError(t, err) + + recorder := httptest.NewRecorder() + finalHandler.ServeHTTP(recorder, req) + + logData, err := os.ReadFile(logFilePath) + require.NoError(t, err) + + result, err := ParseAccessLog(string(logData)) + require.NoError(t, err) + + expectedRouterName := "\"root-router -> parent-router -> child-router\"" + assert.Equal(t, expectedRouterName, result[RouterName]) +} + func doLoggingWithAbortedStream(t *testing.T, config *otypes.AccessLog) { t.Helper() diff --git a/pkg/middlewares/auth/forward.go b/pkg/middlewares/auth/forward.go index 4438a486e..dbaddef2c 100644 --- a/pkg/middlewares/auth/forward.go +++ b/pkg/middlewares/auth/forward.go @@ -88,6 +88,8 @@ func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAu if config.MaxBodySize != nil { fa.maxBodySize = *config.MaxBodySize + } else if fa.forwardBody { + logger.Warn().Msgf("ForwardAuth 'maxBodySize' is not configured with 'forwardBody: true', allowing unlimited request body size which can lead to DoS attacks and memory exhaustion. Please set an appropriate limit.") } // Ensure our request client does not follow redirects @@ -193,7 +195,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) { forwardResponse, forwardErr := fa.client.Do(forwardReq) if forwardErr != nil { - logger.Debug().Err(forwardErr).Msgf("Error calling %s", fa.address) + logger.Error().Err(forwardErr).Msgf("Error calling %s", fa.address) observability.SetStatusErrorf(req.Context(), "Error calling %s. Cause: %s", fa.address, forwardErr) statusCode := http.StatusInternalServerError diff --git a/pkg/middlewares/redirect/redirect.go b/pkg/middlewares/redirect/redirect.go index d8499d1aa..503df4419 100644 --- a/pkg/middlewares/redirect/redirect.go +++ b/pkg/middlewares/redirect/redirect.go @@ -18,30 +18,32 @@ const typeName = "Redirect" var uriRegexp = regexp.MustCompile(`^(https?):\/\/(\[[\w:.]+\]|[\w\._-]+)?(:\d+)?(.*)$`) type redirect struct { - next http.Handler - regex *regexp.Regexp - replacement string - permanent bool - errHandler utils.ErrorHandler - name string - rawURL func(*http.Request) string + next http.Handler + regex *regexp.Regexp + replacement string + permanent bool + forcePermanentRedirect bool + errHandler utils.ErrorHandler + name string + rawURL func(*http.Request) string } // New creates a Redirect middleware. -func newRedirect(next http.Handler, regex, replacement string, permanent bool, rawURL func(*http.Request) string, name string) (http.Handler, error) { +func newRedirect(next http.Handler, regex, replacement string, permanent bool, forcePermanentRedirect bool, rawURL func(*http.Request) string, name string) (http.Handler, error) { re, err := regexp.Compile(regex) if err != nil { return nil, err } return &redirect{ - regex: re, - replacement: replacement, - permanent: permanent, - errHandler: utils.DefaultHandler, - next: next, - name: name, - rawURL: rawURL, + regex: re, + replacement: replacement, + permanent: permanent, + forcePermanentRedirect: forcePermanentRedirect, + errHandler: utils.DefaultHandler, + next: next, + name: name, + rawURL: rawURL, }, nil } @@ -69,7 +71,7 @@ func (r *redirect) ServeHTTP(rw http.ResponseWriter, req *http.Request) { } if newURL != oldURL { - handler := &moveHandler{location: parsedURL, permanent: r.permanent} + handler := &moveHandler{location: parsedURL, permanent: r.permanent, forcePermanentRedirect: r.forcePermanentRedirect} handler.ServeHTTP(rw, req) return } @@ -82,8 +84,9 @@ func (r *redirect) ServeHTTP(rw http.ResponseWriter, req *http.Request) { } type moveHandler struct { - location *url.URL - permanent bool + location *url.URL + permanent bool + forcePermanentRedirect bool } func (m *moveHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { @@ -100,6 +103,11 @@ func (m *moveHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) { status = http.StatusPermanentRedirect } } + + if m.forcePermanentRedirect { + status = http.StatusPermanentRedirect + } + rw.WriteHeader(status) _, err := rw.Write([]byte(http.StatusText(status))) if err != nil { diff --git a/pkg/middlewares/redirect/redirect_regex.go b/pkg/middlewares/redirect/redirect_regex.go index f68493205..5846d5c0d 100644 --- a/pkg/middlewares/redirect/redirect_regex.go +++ b/pkg/middlewares/redirect/redirect_regex.go @@ -17,7 +17,7 @@ func NewRedirectRegex(ctx context.Context, next http.Handler, conf dynamic.Redir logger.Debug().Msg("Creating middleware") logger.Debug().Msgf("Setting up redirection from %s to %s", conf.Regex, conf.Replacement) - return newRedirect(next, conf.Regex, conf.Replacement, conf.Permanent, rawURL, name) + return newRedirect(next, conf.Regex, conf.Replacement, conf.Permanent, false, rawURL, name) } func rawURL(req *http.Request) string { diff --git a/pkg/middlewares/redirect/redirect_scheme.go b/pkg/middlewares/redirect/redirect_scheme.go index b0d8146c7..b1b1e3c3c 100644 --- a/pkg/middlewares/redirect/redirect_scheme.go +++ b/pkg/middlewares/redirect/redirect_scheme.go @@ -40,7 +40,7 @@ func NewRedirectScheme(ctx context.Context, next http.Handler, conf dynamic.Redi rs := &redirectScheme{name: name} - handler, err := newRedirect(next, uriPattern, conf.Scheme+"://${2}"+port+"${4}", conf.Permanent, rs.clientRequestURL, name) + handler, err := newRedirect(next, uriPattern, conf.Scheme+"://${2}"+port+"${4}", conf.Permanent, conf.ForcePermanentRedirect, rs.clientRequestURL, name) if err != nil { return nil, err } diff --git a/pkg/middlewares/redirect/redirect_scheme_test.go b/pkg/middlewares/redirect/redirect_scheme_test.go index aa90bddbc..1e45f5bc7 100644 --- a/pkg/middlewares/redirect/redirect_scheme_test.go +++ b/pkg/middlewares/redirect/redirect_scheme_test.go @@ -165,6 +165,27 @@ func TestRedirectSchemeHandler(t *testing.T) { expectedURL: "https://foo:8443", expectedStatus: http.StatusMovedPermanently, }, + { + desc: "HTTP to HTTPS with explicit 308 status code", + config: dynamic.RedirectScheme{ + Scheme: "https", + ForcePermanentRedirect: true, + }, + url: "http://foo", + expectedURL: "https://foo", + expectedStatus: http.StatusPermanentRedirect, + }, + { + desc: "HTTP to HTTPS with explicit 308 status code for GET request", + method: http.MethodGet, + config: dynamic.RedirectScheme{ + Scheme: "https", + ForcePermanentRedirect: true, + }, + url: "http://foo", + expectedURL: "https://foo", + expectedStatus: http.StatusPermanentRedirect, + }, { desc: "to HTTP 80", config: dynamic.RedirectScheme{ diff --git a/pkg/plugins/fixtures/src/testpluginsafe/go.mod b/pkg/plugins/fixtures/src/testpluginsafe/go.mod new file mode 100644 index 000000000..ffc61e1ba --- /dev/null +++ b/pkg/plugins/fixtures/src/testpluginsafe/go.mod @@ -0,0 +1,3 @@ +module testpluginsafe + +go 1.23.0 diff --git a/pkg/plugins/fixtures/src/testpluginsafe/testpluginsafe.go b/pkg/plugins/fixtures/src/testpluginsafe/testpluginsafe.go new file mode 100644 index 000000000..d9ca33cde --- /dev/null +++ b/pkg/plugins/fixtures/src/testpluginsafe/testpluginsafe.go @@ -0,0 +1,21 @@ +package testpluginsafe + +import ( + "context" + "net/http" +) + +type Config struct { + Message string +} + +func CreateConfig() *Config { + return &Config{Message: "safe plugin"} +} + +func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("X-Test-Plugin", "safe") + next.ServeHTTP(rw, req) + }), nil +} diff --git a/pkg/plugins/fixtures/src/testpluginsyscall/go.mod b/pkg/plugins/fixtures/src/testpluginsyscall/go.mod new file mode 100644 index 000000000..871d361b1 --- /dev/null +++ b/pkg/plugins/fixtures/src/testpluginsyscall/go.mod @@ -0,0 +1,3 @@ +module testpluginsyscall + +go 1.23.0 \ No newline at end of file diff --git a/pkg/plugins/fixtures/src/testpluginsyscall/testpluginsyscall.go b/pkg/plugins/fixtures/src/testpluginsyscall/testpluginsyscall.go new file mode 100644 index 000000000..41272fb27 --- /dev/null +++ b/pkg/plugins/fixtures/src/testpluginsyscall/testpluginsyscall.go @@ -0,0 +1,29 @@ +package testpluginsyscall + +import ( + "context" + "net/http" + "syscall" + "unsafe" +) + +type Config struct { + Message string +} + +func CreateConfig() *Config { + return &Config{Message: "syscall plugin"} +} + +func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) { + // Use syscall and unsafe to test they're available + pid := syscall.Getpid() + size := unsafe.Sizeof(int(0)) + + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("X-Test-Plugin", "syscall") + rw.Header().Set("X-Test-PID", string(rune(pid))) + rw.Header().Set("X-Test-Size", string(rune(size))) + next.ServeHTTP(rw, req) + }), nil +} diff --git a/pkg/plugins/fixtures/src/testpluginunsafe/go.mod b/pkg/plugins/fixtures/src/testpluginunsafe/go.mod new file mode 100644 index 000000000..ef0bee089 --- /dev/null +++ b/pkg/plugins/fixtures/src/testpluginunsafe/go.mod @@ -0,0 +1,3 @@ +module testpluginunsafe + +go 1.23.0 \ No newline at end of file diff --git a/pkg/plugins/fixtures/src/testpluginunsafe/testpluginunsafe.go b/pkg/plugins/fixtures/src/testpluginunsafe/testpluginunsafe.go new file mode 100644 index 000000000..1a58901dd --- /dev/null +++ b/pkg/plugins/fixtures/src/testpluginunsafe/testpluginunsafe.go @@ -0,0 +1,26 @@ +package testpluginunsafe + +import ( + "context" + "net/http" + "unsafe" +) + +type Config struct { + Message string +} + +func CreateConfig() *Config { + return &Config{Message: "unsafe only plugin"} +} + +func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) { + // Use ONLY unsafe to test it's available + size := unsafe.Sizeof(int(0)) + + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("X-Test-Plugin", "unsafe-only") + rw.Header().Set("X-Test-Unsafe-Size", string(rune(size))) + next.ServeHTTP(rw, req) + }), nil +} diff --git a/pkg/plugins/middlewareyaegi.go b/pkg/plugins/middlewareyaegi.go index 590938044..41d120158 100644 --- a/pkg/plugins/middlewareyaegi.go +++ b/pkg/plugins/middlewareyaegi.go @@ -16,6 +16,7 @@ import ( "github.com/traefik/traefik/v3/pkg/observability/logs" "github.com/traefik/yaegi/interp" "github.com/traefik/yaegi/stdlib" + "github.com/traefik/yaegi/stdlib/syscall" "github.com/traefik/yaegi/stdlib/unsafe" ) @@ -135,7 +136,7 @@ func newInterpreter(ctx context.Context, goPath string, manifest *Manifest, sett } if manifest.UseUnsafe && !settings.UseUnsafe { - return nil, errors.New("this plugin uses unsafe import. If you want to use it, you need to allow useUnsafe in the settings") + return nil, errors.New("this plugin uses restricted imports. If you want to use it, you need to allow useUnsafe in the settings") } if settings.UseUnsafe && manifest.UseUnsafe { @@ -143,6 +144,11 @@ func newInterpreter(ctx context.Context, goPath string, manifest *Manifest, sett if err != nil { return nil, fmt.Errorf("failed to load unsafe symbols: %w", err) } + + err = i.Use(syscall.Symbols) + if err != nil { + return nil, fmt.Errorf("failed to load syscall symbols: %w", err) + } } err = i.Use(ppSymbols()) diff --git a/pkg/plugins/middlewareyaegi_test.go b/pkg/plugins/middlewareyaegi_test.go new file mode 100644 index 000000000..f9df8232d --- /dev/null +++ b/pkg/plugins/middlewareyaegi_test.go @@ -0,0 +1,148 @@ +package plugins + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/traefik/yaegi/interp" +) + +// TestNewInterpreter_SyscallErrorCase - Tests the security gate logic +func TestNewInterpreter_SyscallErrorCase(t *testing.T) { + manifest := &Manifest{ + Import: "does-not-matter-will-error-before-import", + UseUnsafe: true, // Plugin wants unsafe access + } + settings := Settings{ + UseUnsafe: false, // But admin doesn't allow it + } + + ctx := t.Context() + _, err := newInterpreter(ctx, "/tmp", manifest, settings) + + // This proves our security gate logic works + require.Error(t, err) + assert.Contains(t, err.Error(), "restricted imports", "Our error message should be returned") +} + +// TestNewYaegiMiddlewareBuilder_WithSyscallSupport - Tests the ACTUAL production code! +func TestNewYaegiMiddlewareBuilder_WithSyscallSupport(t *testing.T) { + tests := []struct { + name string + pluginType string + manifestUnsafe bool + settingsUnsafe bool + shouldSucceed bool + expectedError string + }{ + { + name: "Should work with safe plugin when useUnsafe disabled", + pluginType: "safe", + manifestUnsafe: false, + settingsUnsafe: false, + shouldSucceed: true, + }, + { + name: "Should work with unsafe-only plugin when useUnsafe enabled", + pluginType: "unsafe-only", + manifestUnsafe: true, + settingsUnsafe: true, + shouldSucceed: true, + }, + { + name: "Should work with unsafe+syscall plugin when useUnsafe enabled", + pluginType: "unsafe+syscall", + manifestUnsafe: true, + settingsUnsafe: true, + shouldSucceed: true, + }, + { + name: "Should fail when plugin needs unsafe but setting disabled", + pluginType: "unsafe-only", + manifestUnsafe: true, + settingsUnsafe: false, + shouldSucceed: false, + expectedError: "restricted imports", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + ctx := t.Context() + + // Set GOPATH to include our fixtures directory + goPath := "fixtures" + + // Create interpreter using our ACTUAL newInterpreter function + // This will automatically import the real test plugin! + interpreter, err := createInterpreterForTesting(ctx, goPath, tc.pluginType, tc.manifestUnsafe, tc.settingsUnsafe) + + if tc.shouldSucceed { + require.NoError(t, err) + require.NotNil(t, interpreter) + + // Test actual middleware building using newYaegiMiddlewareBuilder + // The plugin is already loaded by newInterpreter! + basePkg := getPluginPackage(tc.pluginType) + + builder, err := newYaegiMiddlewareBuilder(interpreter, basePkg, basePkg) + require.NoError(t, err) + require.NotNil(t, builder) + + // Verify that unsafe/syscall functions actually work if the plugin uses them + if tc.pluginType != "safe" { + verifyMiddlewareWorks(t, builder) + } + } else { + assert.Error(t, err) + assert.Contains(t, err.Error(), tc.expectedError) + } + }) + } +} + +// Helper that uses the ACTUAL newInterpreter function with real test plugins +func createInterpreterForTesting(ctx context.Context, goPath, pluginType string, manifestUnsafe, settingsUnsafe bool) (*interp.Interpreter, error) { + pluginImport := getPluginPackage(pluginType) + + manifest := &Manifest{ + Import: pluginImport, + UseUnsafe: manifestUnsafe, + } + settings := Settings{ + UseUnsafe: settingsUnsafe, + } + + // Call the ACTUAL production newInterpreter function - no workarounds needed! + return newInterpreter(ctx, goPath, manifest, settings) +} + +// Helper to get the correct plugin package name based on type +func getPluginPackage(pluginType string) string { + switch pluginType { + case "safe": + return "testpluginsafe" + case "unsafe-only": + return "testpluginunsafe" + case "unsafe+syscall": + return "testpluginsyscall" + default: + return "testpluginsafe" + } +} + +// Helper to verify that unsafe/syscall functions actually work by invoking the middleware +func verifyMiddlewareWorks(t *testing.T, builder *yaegiMiddlewareBuilder) { + t.Helper() + // Create a middleware instance - this will call the plugin's New() function + // which uses unsafe/syscall, proving they work + middleware, err := builder.newMiddleware(map[string]interface{}{ + "message": "test", + }, "test-middleware") + require.NoError(t, err, "Should be able to create middleware that uses unsafe/syscall") + require.NotNil(t, middleware, "Middleware should not be nil") + + // The fact that we got here without crashing proves unsafe/syscall work! +} diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go index f7d543154..8157a04c7 100644 --- a/pkg/plugins/plugins.go +++ b/pkg/plugins/plugins.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/go-multierror" "github.com/rs/zerolog/log" + "golang.org/x/mod/module" ) const localGoPath = "./plugins-local/" @@ -53,24 +54,18 @@ func checkRemotePluginsConfiguration(plugins map[string]Descriptor) error { var errs []string for pAlias, descriptor := range plugins { - if descriptor.ModuleName == "" { - errs = append(errs, fmt.Sprintf("%s: plugin name is missing", pAlias)) + if err := module.CheckPath(descriptor.ModuleName); err != nil { + errs = append(errs, fmt.Sprintf("%s: malformed plugin module name is missing: %s", pAlias, err)) } if descriptor.Version == "" { errs = append(errs, fmt.Sprintf("%s: plugin version is missing", pAlias)) } - if strings.HasPrefix(descriptor.ModuleName, "/") || strings.HasSuffix(descriptor.ModuleName, "/") { - errs = append(errs, fmt.Sprintf("%s: plugin name should not start or end with a /", pAlias)) - continue - } - if _, ok := uniq[descriptor.ModuleName]; ok { errs = append(errs, fmt.Sprintf("only one version of a plugin is allowed, there is a duplicate of %s", descriptor.ModuleName)) continue } - uniq[descriptor.ModuleName] = struct{}{} } diff --git a/pkg/plugins/plugins_test.go b/pkg/plugins/plugins_test.go new file mode 100644 index 000000000..0fff67915 --- /dev/null +++ b/pkg/plugins/plugins_test.go @@ -0,0 +1,85 @@ +package plugins + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_checkRemotePluginsConfiguration(t *testing.T) { + testCases := []struct { + name string + plugins map[string]Descriptor + wantErr bool + }{ + { + name: "nil plugins configuration returns no error", + plugins: nil, + wantErr: false, + }, + { + name: "malformed module name returns error", + plugins: map[string]Descriptor{ + "plugin1": {ModuleName: "invalid/module/name", Version: "v1.0.0"}, + }, + wantErr: true, + }, + { + name: "malformed module name with path traversal returns error", + plugins: map[string]Descriptor{ + "plugin1": {ModuleName: "github.com/module/../name", Version: "v1.0.0"}, + }, + wantErr: true, + }, + { + name: "malformed module name with encoded path traversal returns error", + plugins: map[string]Descriptor{ + "plugin1": {ModuleName: "github.com/module%2F%2E%2E%2Fname", Version: "v1.0.0"}, + }, + wantErr: true, + }, + { + name: "malformed module name returns error", + plugins: map[string]Descriptor{ + "plugin1": {ModuleName: "invalid/module/name", Version: "v1.0.0"}, + }, + wantErr: true, + }, + { + name: "missing plugin version returns error", + plugins: map[string]Descriptor{ + "plugin1": {ModuleName: "github.com/module/name", Version: ""}, + }, + wantErr: true, + }, + { + name: "duplicate plugin module name returns error", + plugins: map[string]Descriptor{ + "plugin1": {ModuleName: "github.com/module/name", Version: "v1.0.0"}, + "plugin2": {ModuleName: "github.com/module/name", Version: "v1.1.0"}, + }, + wantErr: true, + }, + { + name: "valid plugins configuration returns no error", + plugins: map[string]Descriptor{ + "plugin1": {ModuleName: "github.com/module/name1", Version: "v1.0.0"}, + "plugin2": {ModuleName: "github.com/module/name2", Version: "v1.1.0"}, + }, + wantErr: false, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + err := checkRemotePluginsConfiguration(test.plugins) + if test.wantErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/pkg/plugins/types.go b/pkg/plugins/types.go index 75bb589b3..aa05cc320 100644 --- a/pkg/plugins/types.go +++ b/pkg/plugins/types.go @@ -13,7 +13,7 @@ const ( type Settings struct { Envs []string `description:"Environment variables to forward to the wasm guest." json:"envs,omitempty" toml:"envs,omitempty" yaml:"envs,omitempty"` Mounts []string `description:"Directory to mount to the wasm guest." json:"mounts,omitempty" toml:"mounts,omitempty" yaml:"mounts,omitempty"` - UseUnsafe bool `description:"Allow the plugin to use unsafe package." json:"useUnsafe,omitempty" toml:"useUnsafe,omitempty" yaml:"useUnsafe,omitempty"` + UseUnsafe bool `description:"Allow the plugin to use unsafe and syscall packages." json:"useUnsafe,omitempty" toml:"useUnsafe,omitempty" yaml:"useUnsafe,omitempty"` } // Descriptor The static part of a plugin configuration. diff --git a/pkg/provider/acme/provider.go b/pkg/provider/acme/provider.go index c818ac7d2..f8ab4ca1f 100644 --- a/pkg/provider/acme/provider.go +++ b/pkg/provider/acme/provider.go @@ -22,6 +22,7 @@ import ( "github.com/go-acme/lego/v4/challenge" "github.com/go-acme/lego/v4/challenge/dns01" "github.com/go-acme/lego/v4/challenge/http01" + "github.com/go-acme/lego/v4/challenge/tlsalpn01" "github.com/go-acme/lego/v4/lego" "github.com/go-acme/lego/v4/providers/dns" "github.com/go-acme/lego/v4/registration" @@ -46,6 +47,7 @@ type Configuration struct { PreferredChain string `description:"Preferred chain to use." json:"preferredChain,omitempty" toml:"preferredChain,omitempty" yaml:"preferredChain,omitempty" export:"true"` Profile string `description:"Certificate profile to use." json:"profile,omitempty" toml:"profile,omitempty" yaml:"profile,omitempty" export:"true"` EmailAddresses []string `description:"CSR email addresses to use." json:"emailAddresses,omitempty" toml:"emailAddresses,omitempty" yaml:"emailAddresses,omitempty"` + DisableCommonName bool `description:"Disable the common name in the CSR." json:"disableCommonName,omitempty" toml:"disableCommonName,omitempty" yaml:"disableCommonName,omitempty" export:"true"` Storage string `description:"Storage to use." json:"storage,omitempty" toml:"storage,omitempty" yaml:"storage,omitempty" export:"true"` KeyType string `description:"KeyType used for generating certificate private key. Allow value 'EC256', 'EC384', 'RSA2048', 'RSA4096', 'RSA8192'." json:"keyType,omitempty" toml:"keyType,omitempty" yaml:"keyType,omitempty" export:"true"` EAB *EAB `description:"External Account Binding to use." json:"eab,omitempty" toml:"eab,omitempty" yaml:"eab,omitempty"` @@ -118,7 +120,9 @@ type HTTPChallenge struct { } // TLSChallenge contains TLS challenge configuration. -type TLSChallenge struct{} +type TLSChallenge struct { + Delay ptypes.Duration `description:"Delay between the creation of the challenge and the validation." json:"delay,omitempty" toml:"delay,omitempty" yaml:"delay,omitempty" export:"true"` +} // Provider holds configurations of the provider. type Provider struct { @@ -293,6 +297,7 @@ func (p *Provider) getClient() (*lego.Client, error) { config.CADirURL = caServer config.Certificate.KeyType = GetKeyType(ctx, p.KeyType) config.UserAgent = fmt.Sprintf("containous-traefik/%s", version.Version) + config.Certificate.DisableCommonName = p.DisableCommonName config.HTTPClient, err = p.createHTTPClient() if err != nil { @@ -372,7 +377,7 @@ func (p *Provider) getClient() (*lego.Client, error) { if p.TLSChallenge != nil { logger.Debug().Msg("Using TLS Challenge provider.") - err = client.Challenge.SetTLSALPN01Provider(p.TLSChallengeProvider) + err = client.Challenge.SetTLSALPN01Provider(p.TLSChallengeProvider, tlsalpn01.SetDelay(time.Duration(p.TLSChallenge.Delay))) if err != nil { return nil, err } @@ -916,11 +921,11 @@ func (p *Provider) renewCertificates(ctx context.Context, renewPeriod time.Durat for _, cert := range certificates { client, err := p.getClient() if err != nil { - logger.Info().Err(err).Msgf("Error renewing certificate from LE : %+v", cert.Domain) + logger.Info().Err(err).Msgf("Error renewing ACME certificate: %+v", cert.Domain) continue } - logger.Info().Msgf("Renewing certificate from LE : %+v", cert.Domain) + logger.Info().Msgf("Renewing ACME certificate: %+v", cert.Domain) res := certificate.Resource{ Domain: cert.Domain.Main, @@ -930,12 +935,14 @@ func (p *Provider) renewCertificates(ctx context.Context, renewPeriod time.Durat opts := &certificate.RenewOptions{ Bundle: true, + EmailAddresses: p.EmailAddresses, + Profile: p.Profile, PreferredChain: p.PreferredChain, } renewedCert, err := client.Certificate.RenewWithOptions(res, opts) if err != nil { - logger.Error().Err(err).Msgf("Error renewing certificate from LE: %v", cert.Domain) + logger.Error().Err(err).Msgf("Error renewing ACME certificate: %v", cert.Domain) continue } diff --git a/pkg/provider/aggregator/aggregator.go b/pkg/provider/aggregator/aggregator.go index fb2b1a6dc..95bb0b220 100644 --- a/pkg/provider/aggregator/aggregator.go +++ b/pkg/provider/aggregator/aggregator.go @@ -2,6 +2,7 @@ package aggregator import ( "context" + "fmt" "time" "github.com/rs/zerolog/log" @@ -100,6 +101,10 @@ func NewProviderAggregator(conf static.Providers) *ProviderAggregator { p.quietAddProvider(conf.KubernetesCRD) } + if conf.Knative != nil { + p.quietAddProvider(conf.Knative) + } + if conf.KubernetesGateway != nil { p.quietAddProvider(conf.KubernetesGateway) } @@ -203,8 +208,16 @@ func (p *ProviderAggregator) launchProvider(configurationChan chan<- dynamic.Mes log.Debug().Err(err).Msgf("Cannot marshal the provider configuration %T", prd) } - log.Info().Msgf("Starting provider %T", prd) - log.Debug().RawJSON("config", []byte(jsonConf)).Msgf("%T provider configuration", prd) + // Check if provider has namespace information. + var namespaceInfo string + if namespaceProvider, ok := prd.(provider.NamespacedProvider); ok { + if namespace := namespaceProvider.Namespace(); namespace != "" { + namespaceInfo = fmt.Sprintf(" (namespace: %s)", namespace) + } + } + + log.Info().Msgf("Starting provider %T%s", prd, namespaceInfo) + log.Debug().RawJSON("config", []byte(jsonConf)).Msgf("%T provider configuration%s", prd, namespaceInfo) if err := maybeThrottledProvide(prd, p.providersThrottleDuration)(configurationChan, pool); err != nil { log.Error().Err(err).Msgf("Cannot start the provider %T", prd) diff --git a/pkg/provider/aggregator/aggregator_test.go b/pkg/provider/aggregator/aggregator_test.go index 895dc6539..8bb609eb4 100644 --- a/pkg/provider/aggregator/aggregator_test.go +++ b/pkg/provider/aggregator/aggregator_test.go @@ -1,9 +1,13 @@ package aggregator import ( + "bytes" "testing" "time" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/provider" @@ -40,6 +44,34 @@ func TestProviderAggregator_Provide(t *testing.T) { require.NoError(t, <-errCh) } +func TestLaunchNamespacedProvider(t *testing.T) { + // Capture log output + var buf bytes.Buffer + + originalLogger := log.Logger + log.Logger = zerolog.New(&buf).Level(zerolog.InfoLevel) + + providerWithNamespace := &mockNamespacedProvider{namespace: "test-namespace"} + + aggregator := ProviderAggregator{ + internalProvider: providerWithNamespace, + } + + cfgCh := make(chan dynamic.Message) + pool := safe.NewPool(t.Context()) + + t.Cleanup(func() { + pool.Stop() + log.Logger = originalLogger + }) + + err := aggregator.Provide(cfgCh, pool) + require.NoError(t, err) + + output := buf.String() + assert.Contains(t, output, "Starting provider *aggregator.mockNamespacedProvider (namespace: test-namespace)") +} + // requireReceivedMessageFromProviders makes sure the given providers have emitted a message on the given message channel. // Providers order is not enforced. func requireReceivedMessageFromProviders(t *testing.T, cfgCh <-chan dynamic.Message, names []string) { @@ -76,3 +108,20 @@ func (p *providerMock) Provide(configurationChan chan<- dynamic.Message, pool *s return nil } + +// mockNamespacedProvider is a mock implementation of NamespacedProvider for testing. +type mockNamespacedProvider struct { + namespace string +} + +func (m *mockNamespacedProvider) Namespace() string { + return m.namespace +} + +func (m *mockNamespacedProvider) Provide(_ chan<- dynamic.Message, _ *safe.Pool) error { + return nil +} + +func (m *mockNamespacedProvider) Init() error { + return nil +} diff --git a/pkg/provider/consulcatalog/consul_catalog.go b/pkg/provider/consulcatalog/consul_catalog.go index abdc8648e..ec3fb64da 100644 --- a/pkg/provider/consulcatalog/consul_catalog.go +++ b/pkg/provider/consulcatalog/consul_catalog.go @@ -647,3 +647,8 @@ func repeatSend(ctx context.Context, interval time.Duration, c chan<- struct{}) } } } + +// Namespace returns the namespace of the ConsulCatalog provider. +func (p *Provider) Namespace() string { + return p.namespace +} diff --git a/pkg/provider/docker/builder_test.go b/pkg/provider/docker/builder_test.go index c0b4f1d7f..4d001c8ce 100644 --- a/pkg/provider/docker/builder_test.go +++ b/pkg/provider/docker/builder_test.go @@ -12,6 +12,7 @@ func containerJSON(ops ...func(*containertypes.InspectResponse)) containertypes. ContainerJSONBase: &containertypes.ContainerJSONBase{ Name: "fake", HostConfig: &containertypes.HostConfig{}, + State: &containertypes.State{}, }, Config: &containertypes.Config{}, NetworkSettings: &containertypes.NetworkSettings{ diff --git a/pkg/provider/docker/config.go b/pkg/provider/docker/config.go index 0b322715a..26238e552 100644 --- a/pkg/provider/docker/config.go +++ b/pkg/provider/docker/config.go @@ -138,6 +138,11 @@ func (p *DynConfBuilder) buildTCPServiceConfiguration(ctx context.Context, conta } } + // Keep an empty server load-balancer for non-running containers. + if container.Status != "" && container.Status != containertypes.StateRunning { + return nil + } + // Keep an empty server load-balancer for unhealthy containers. if container.Health != "" && container.Health != containertypes.Healthy { return nil } @@ -162,6 +167,11 @@ func (p *DynConfBuilder) buildUDPServiceConfiguration(ctx context.Context, conta } } + // Keep an empty server load-balancer for non-running containers. + if container.Status != "" && container.Status != containertypes.StateRunning { + return nil + } + // Keep an empty server load-balancer for unhealthy containers. if container.Health != "" && container.Health != containertypes.Healthy { return nil } @@ -188,6 +198,11 @@ func (p *DynConfBuilder) buildServiceConfiguration(ctx context.Context, containe } } + // Keep an empty server load-balancer for non-running containers. + if container.Status != "" && container.Status != containertypes.StateRunning { + return nil + } + // Keep an empty server load-balancer for unhealthy containers. if container.Health != "" && container.Health != containertypes.Healthy { return nil } @@ -220,6 +235,19 @@ func (p *DynConfBuilder) keepContainer(ctx context.Context, container dockerData return false } + // AllowNonRunning has precedence over AllowEmptyServices. + // If AllowNonRunning is true, we don't care about the container health/status, + // and we need to quit before checking it. + // Only configurable with the Docker provider. + if container.ExtraConf.AllowNonRunning { + return true + } + + if container.Status != "" && container.Status != containertypes.StateRunning { + logger.Debug().Msg("Filtering non running container") + return false + } + if !p.AllowEmptyServices && container.Health != "" && container.Health != containertypes.Healthy { logger.Debug().Msg("Filtering unhealthy or starting container") return false diff --git a/pkg/provider/docker/config_test.go b/pkg/provider/docker/config_test.go index e54b980c3..33b8e390a 100644 --- a/pkg/provider/docker/config_test.go +++ b/pkg/provider/docker/config_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/provider" "github.com/traefik/traefik/v3/pkg/tls" "github.com/traefik/traefik/v3/pkg/types" ) @@ -3935,6 +3936,464 @@ func TestDynConfBuilder_build(t *testing.T) { } } +func TestDynConfBuilder_build_allowNonRunning(t *testing.T) { + testCases := []struct { + desc string + containers []dockerData + expected *dynamic.Configuration + }{ + { + desc: "exited container with allowNonRunning=true should create router and service without servers", + containers: []dockerData{ + { + ServiceName: "Test", + Name: "Test", + Status: "exited", + Health: "", + ExtraConf: configuration{ + Enable: true, + AllowNonRunning: true, + }, + NetworkSettings: networkSettings{ + NetworkMode: "bridge", + Ports: nat.PortMap{ + "80/tcp": []nat.PortBinding{}, + }, + Networks: map[string]*networkData{ + "bridge": { + Name: "bridge", + Addr: "127.0.0.1", + }, + }, + }, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`Test`)", + DefaultRule: true, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + PassHostHeader: pointer(true), + Strategy: "wrr", + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: ptypes.Duration(100 * time.Millisecond), + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, + }, + }, + { + desc: "exited container with allowNonRunning=false should not create anything", + containers: []dockerData{ + { + ServiceName: "Test", + Name: "Test", + Status: "exited", + Health: "", + ExtraConf: configuration{ + Enable: true, + AllowNonRunning: false, + }, + NetworkSettings: networkSettings{ + NetworkMode: "bridge", + Ports: nat.PortMap{ + "80/tcp": []nat.PortBinding{}, + }, + Networks: map[string]*networkData{ + "bridge": { + Name: "bridge", + Addr: "127.0.0.1", + }, + }, + }, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, + }, + }, + { + desc: "running container with allowNonRunning=true should work normally with servers", + containers: []dockerData{ + { + ServiceName: "Test", + Name: "Test", + Status: "running", + Health: "", + ExtraConf: configuration{ + Enable: true, + AllowNonRunning: true, + }, + NetworkSettings: networkSettings{ + NetworkMode: "bridge", + Ports: nat.PortMap{ + "80/tcp": []nat.PortBinding{}, + }, + Networks: map[string]*networkData{ + "bridge": { + Name: "bridge", + Addr: "127.0.0.1", + }, + }, + }, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`Test`)", + DefaultRule: true, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://127.0.0.1:80", + }, + }, + PassHostHeader: pointer(true), + Strategy: "wrr", + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: ptypes.Duration(100 * time.Millisecond), + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, + }, + }, + { + desc: "created container with allowNonRunning=true should create router and service without servers)", + containers: []dockerData{ + { + ServiceName: "Test", + Name: "Test", + Status: "created", + Health: "", + ExtraConf: configuration{ + Enable: true, + AllowNonRunning: true, + }, + NetworkSettings: networkSettings{ + NetworkMode: "bridge", + Ports: nat.PortMap{ + "80/tcp": []nat.PortBinding{}, + }, + Networks: map[string]*networkData{ + "bridge": { + Name: "bridge", + Addr: "127.0.0.1", + }, + }, + }, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`Test`)", + DefaultRule: true, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + PassHostHeader: pointer(true), + Strategy: "wrr", + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: ptypes.Duration(100 * time.Millisecond), + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, + }, + }, + { + desc: "dead container with allowNonRunning=true should create router and service without servers)", + containers: []dockerData{ + { + ServiceName: "Test", + Name: "Test", + Status: "dead", + Health: "", + ExtraConf: configuration{ + Enable: true, + AllowNonRunning: true, + }, + NetworkSettings: networkSettings{ + NetworkMode: "bridge", + Ports: nat.PortMap{ + "80/tcp": []nat.PortBinding{}, + }, + Networks: map[string]*networkData{ + "bridge": { + Name: "bridge", + Addr: "127.0.0.1", + }, + }, + }, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "Test": { + Service: "Test", + Rule: "Host(`Test`)", + DefaultRule: true, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "Test": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + PassHostHeader: pointer(true), + Strategy: "wrr", + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: ptypes.Duration(100 * time.Millisecond), + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, + }, + }, + { + desc: "exited container with TCP configuration and allowNonRunning=true should create TCP service without servers", + containers: []dockerData{ + { + ServiceName: "Test", + Name: "Test", + Status: "exited", + Health: "", + Labels: map[string]string{ + "traefik.tcp.routers.Test.rule": "HostSNI(`test.localhost`)", + }, + ExtraConf: configuration{ + Enable: true, + AllowNonRunning: true, + }, + NetworkSettings: networkSettings{ + NetworkMode: "bridge", + Ports: nat.PortMap{ + "80/tcp": []nat.PortBinding{}, + }, + Networks: map[string]*networkData{ + "bridge": { + Name: "bridge", + Addr: "127.0.0.1", + }, + }, + }, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{ + "Test": { + Service: "Test", + Rule: "HostSNI(`test.localhost`)", + }, + }, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{ + "Test": { + LoadBalancer: &dynamic.TCPServersLoadBalancer{}, + }, + }, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, + }, + }, + { + desc: "exited container with UDP configuration and allowNonRunning=true should create UDP service without servers", + containers: []dockerData{ + { + ServiceName: "Test", + Name: "Test", + Status: "exited", + Health: "", + Labels: map[string]string{ + "traefik.udp.routers.Test.entrypoints": "udp", + }, + ExtraConf: configuration{ + Enable: true, + AllowNonRunning: true, + }, + NetworkSettings: networkSettings{ + NetworkMode: "bridge", + Ports: nat.PortMap{ + "80/udp": []nat.PortBinding{}, + }, + Networks: map[string]*networkData{ + "bridge": { + Name: "bridge", + Addr: "127.0.0.1", + }, + }, + }, + }, + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{ + "Test": { + Service: "Test", + EntryPoints: []string{"udp"}, + }, + }, + Services: map[string]*dynamic.UDPService{ + "Test": { + LoadBalancer: &dynamic.UDPServersLoadBalancer{}, + }, + }, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{ + Stores: map[string]tls.Store{}, + }, + }, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + defaultRuleTpl, err := provider.MakeDefaultRuleTemplate(DefaultTemplateRule, nil) + require.NoError(t, err) + + p := Shared{ + ExposedByDefault: true, + DefaultRule: DefaultTemplateRule, + defaultRuleTpl: defaultRuleTpl, + } + + builder := NewDynConfBuilder(p, nil, false) + configuration := builder.build(t.Context(), test.containers) + + assert.Equal(t, test.expected, configuration) + }) + } +} + func TestDynConfBuilder_getIPPort_docker(t *testing.T) { type expected struct { ip string diff --git a/pkg/provider/docker/data.go b/pkg/provider/docker/data.go index 4c42d396e..22dde04e7 100644 --- a/pkg/provider/docker/data.go +++ b/pkg/provider/docker/data.go @@ -10,6 +10,7 @@ type dockerData struct { ID string ServiceName string Name string + Status string Labels map[string]string // List of labels set to container or service NetworkSettings networkSettings Health string diff --git a/pkg/provider/docker/pdocker.go b/pkg/provider/docker/pdocker.go index 5e88be51b..dbd609f9f 100644 --- a/pkg/provider/docker/pdocker.go +++ b/pkg/provider/docker/pdocker.go @@ -21,9 +21,6 @@ import ( "github.com/traefik/traefik/v3/pkg/safe" ) -// DockerAPIVersion is a constant holding the version of the Provider API traefik will use. -const DockerAPIVersion = "1.24" - const dockerName = "docker" var _ provider.Provider = (*Provider)(nil) @@ -61,7 +58,6 @@ func (p *Provider) Init() error { } func (p *Provider) createClient(ctx context.Context) (*client.Client, error) { - p.ClientConfig.apiVersion = DockerAPIVersion return createClient(ctx, p.ClientConfig) } @@ -172,7 +168,9 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe. } func (p *Provider) listContainers(ctx context.Context, dockerClient client.ContainerAPIClient) ([]dockerData, error) { - containerList, err := dockerClient.ContainerList(ctx, container.ListOptions{}) + containerList, err := dockerClient.ContainerList(ctx, container.ListOptions{ + All: true, + }) if err != nil { return nil, err } diff --git a/pkg/provider/docker/pswarm.go b/pkg/provider/docker/pswarm.go index bcb7ded48..f596b9e67 100644 --- a/pkg/provider/docker/pswarm.go +++ b/pkg/provider/docker/pswarm.go @@ -22,9 +22,6 @@ import ( "github.com/traefik/traefik/v3/pkg/safe" ) -// SwarmAPIVersion is a constant holding the version of the Provider API traefik will use. -const SwarmAPIVersion = "1.24" - const swarmName = "swarm" var _ provider.Provider = (*SwarmProvider)(nil) @@ -58,7 +55,6 @@ func (p *SwarmProvider) Init() error { } func (p *SwarmProvider) createClient(ctx context.Context) (*client.Client, error) { - p.ClientConfig.apiVersion = SwarmAPIVersion return createClient(ctx, p.ClientConfig) } diff --git a/pkg/provider/docker/shared.go b/pkg/provider/docker/shared.go index 931c61905..bd3c06269 100644 --- a/pkg/provider/docker/shared.go +++ b/pkg/provider/docker/shared.go @@ -53,9 +53,9 @@ func inspectContainers(ctx context.Context, dockerClient client.ContainerAPIClie return dockerData{} } - // This condition is here to avoid to have empty IP https://github.com/traefik/traefik/issues/2459 - // We register only container which are running - if containerInspected.ContainerJSONBase != nil && containerInspected.ContainerJSONBase.State != nil && containerInspected.ContainerJSONBase.State.Running { + // Always parse all containers (running and stopped) + // The allowNonRunning filtering will be applied later in service configuration + if containerInspected.ContainerJSONBase != nil && containerInspected.ContainerJSONBase.State != nil { return parseContainer(containerInspected) } @@ -71,6 +71,7 @@ func parseContainer(container containertypes.InspectResponse) dockerData { dData.ID = container.ContainerJSONBase.ID dData.Name = container.ContainerJSONBase.Name dData.ServiceName = dData.Name // Default ServiceName to be the container's Name. + dData.Status = container.ContainerJSONBase.State.Status if container.ContainerJSONBase.HostConfig != nil { dData.NetworkSettings.NetworkMode = container.ContainerJSONBase.HostConfig.NetworkMode @@ -109,8 +110,6 @@ func parseContainer(container containertypes.InspectResponse) dockerData { } type ClientConfig struct { - apiVersion string - Username string `description:"Username for Basic HTTP authentication." json:"username,omitempty" toml:"username,omitempty" yaml:"username,omitempty"` Password string `description:"Password for Basic HTTP authentication." json:"password,omitempty" toml:"password,omitempty" yaml:"password,omitempty"` Endpoint string `description:"Docker server endpoint. Can be a TCP or a Unix socket endpoint." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` @@ -132,8 +131,9 @@ func createClient(ctx context.Context, cfg ClientConfig) (*client.Client, error) } opts = append(opts, - client.WithHTTPHeaders(httpHeaders), - client.WithVersion(cfg.apiVersion)) + client.FromEnv, + client.WithAPIVersionNegotiation(), + client.WithHTTPHeaders(httpHeaders)) return client.NewClientWithOpts(opts...) } diff --git a/pkg/provider/docker/shared_labels.go b/pkg/provider/docker/shared_labels.go index 6eab7143d..7d3bed8c1 100644 --- a/pkg/provider/docker/shared_labels.go +++ b/pkg/provider/docker/shared_labels.go @@ -16,17 +16,24 @@ const ( // configuration contains information from the labels that are globals (not related to the dynamic configuration) // or specific to the provider. type configuration struct { - Enable bool - Network string - LBSwarm bool + Enable bool + Network string + LBSwarm bool + AllowNonRunning bool } type labelConfiguration struct { Enable bool - Docker *specificConfiguration + Docker *dockerSpecificConfiguration Swarm *specificConfiguration } +type dockerSpecificConfiguration struct { + Network *string + LBSwarm bool + AllowNonRunning bool +} + type specificConfiguration struct { Network *string LBSwarm bool @@ -43,9 +50,15 @@ func (p *Shared) extractDockerLabels(container dockerData) (configuration, error network = *conf.Docker.Network } + var allowNonRunning bool + if conf.Docker != nil { + allowNonRunning = conf.Docker.AllowNonRunning + } + return configuration{ - Enable: conf.Enable, - Network: network, + Enable: conf.Enable, + Network: network, + AllowNonRunning: allowNonRunning, }, nil } diff --git a/pkg/provider/ecs/ecs.go b/pkg/provider/ecs/ecs.go index 084b9b9ca..1204387d7 100644 --- a/pkg/provider/ecs/ecs.go +++ b/pkg/provider/ecs/ecs.go @@ -317,8 +317,14 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI protocol: mapping.Protocol, }) } + + privateIP := aws.ToString(container.NetworkInterfaces[0].PrivateIpv4Address) + if privateIP == "" { + privateIP = aws.ToString(container.NetworkInterfaces[0].Ipv6Address) + } + mach = &machine{ - privateIP: aws.ToString(container.NetworkInterfaces[0].PrivateIpv4Address), + privateIP: privateIP, ports: ports, state: ec2types.InstanceStateName(strings.ToLower(aws.ToString(task.LastStatus))), healthStatus: task.HealthStatus, diff --git a/pkg/provider/kubernetes/crd/client_test.go b/pkg/provider/kubernetes/crd/client_test.go index 5d71b6a78..5d1c9aba7 100644 --- a/pkg/provider/kubernetes/crd/client_test.go +++ b/pkg/provider/kubernetes/crd/client_test.go @@ -29,8 +29,8 @@ func TestClientIgnoresHelmOwnedSecrets(t *testing.T) { }, } - kubeClient := kubefake.NewSimpleClientset(helmSecret, secret) - crdClient := traefikcrdfake.NewSimpleClientset() + kubeClient := kubefake.NewClientset(helmSecret, secret) + crdClient := traefikcrdfake.NewClientset() client := newClientImpl(kubeClient, crdClient) diff --git a/pkg/provider/kubernetes/crd/fixtures/parent_refs_cross_namespace_allowed.yml b/pkg/provider/kubernetes/crd/fixtures/parent_refs_cross_namespace_allowed.yml new file mode 100644 index 000000000..9d25cf43c --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/parent_refs_cross_namespace_allowed.yml @@ -0,0 +1,28 @@ +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: parent-cross + namespace: ns-a +spec: + entryPoints: + - web + routes: + - match: Host(`cross.example.com`) + kind: Rule +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: child-cross-allowed + namespace: ns-b +spec: + parentRefs: + - name: parent-cross + namespace: ns-a + routes: + - match: Path(`/cross`) + kind: Rule + services: + - name: cross-service + port: 9000 \ No newline at end of file diff --git a/pkg/provider/kubernetes/crd/fixtures/parent_refs_cross_namespace_denied.yml b/pkg/provider/kubernetes/crd/fixtures/parent_refs_cross_namespace_denied.yml new file mode 100644 index 000000000..0ca584e74 --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/parent_refs_cross_namespace_denied.yml @@ -0,0 +1,28 @@ +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: parent-cross + namespace: ns-a +spec: + entryPoints: + - web + routes: + - match: Host(`cross.example.com`) + kind: Rule +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: child-cross-denied + namespace: ns-b +spec: + parentRefs: + - name: parent-cross + namespace: ns-a + routes: + - match: Path(`/denied`) + kind: Rule + services: + - name: cross-service + port: 9000 \ No newline at end of file diff --git a/pkg/provider/kubernetes/crd/fixtures/parent_refs_default_namespace.yml b/pkg/provider/kubernetes/crd/fixtures/parent_refs_default_namespace.yml new file mode 100644 index 000000000..aa82b47ca --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/parent_refs_default_namespace.yml @@ -0,0 +1,27 @@ +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: parent-default + namespace: default +spec: + entryPoints: + - web + routes: + - match: Host(`default.example.com`) + kind: Rule +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: child-same + namespace: default +spec: + parentRefs: + - name: parent-default + routes: + - match: Path(`/same`) + kind: Rule + services: + - name: same-service + port: 9000 \ No newline at end of file diff --git a/pkg/provider/kubernetes/crd/fixtures/parent_refs_missing_parent.yml b/pkg/provider/kubernetes/crd/fixtures/parent_refs_missing_parent.yml new file mode 100644 index 000000000..c35ac69e8 --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/parent_refs_missing_parent.yml @@ -0,0 +1,16 @@ +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: child-missing-parent + namespace: default +spec: + parentRefs: + - name: non-existent-parent + namespace: default + routes: + - match: Path(`/missing`) + kind: Rule + services: + - name: child-service + port: 9000 \ No newline at end of file diff --git a/pkg/provider/kubernetes/crd/fixtures/parent_refs_multiple_parents.yml b/pkg/provider/kubernetes/crd/fixtures/parent_refs_multiple_parents.yml new file mode 100644 index 000000000..e4f099b0d --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/parent_refs_multiple_parents.yml @@ -0,0 +1,42 @@ +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: parent-a + namespace: default +spec: + entryPoints: + - web + routes: + - match: Host(`a.example.com`) + kind: Rule +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: parent-b + namespace: default +spec: + entryPoints: + - web + routes: + - match: Host(`b.example.com`) + kind: Rule +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: child-multi-parents + namespace: default +spec: + parentRefs: + - name: parent-a + namespace: default + - name: parent-b + namespace: default + routes: + - match: Path(`/shared`) + kind: Rule + services: + - name: shared-service + port: 9000 \ No newline at end of file diff --git a/pkg/provider/kubernetes/crd/fixtures/parent_refs_services.yml b/pkg/provider/kubernetes/crd/fixtures/parent_refs_services.yml new file mode 100644 index 000000000..0523ea401 --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/parent_refs_services.yml @@ -0,0 +1,139 @@ +apiVersion: v1 +kind: Service +metadata: + name: child-service + namespace: default +spec: + ports: + - name: web + port: 9000 +--- +kind: EndpointSlice +apiVersion: discovery.k8s.io/v1 +metadata: + name: child-service-abc + namespace: default + labels: + kubernetes.io/service-name: child-service +addressType: IPv4 +ports: + - name: web + port: 9000 +endpoints: + - addresses: + - 10.10.2.1 + - 10.10.2.2 + conditions: + ready: true +--- +apiVersion: v1 +kind: Service +metadata: + name: users-service + namespace: default +spec: + ports: + - name: web + port: 9000 +--- +kind: EndpointSlice +apiVersion: discovery.k8s.io/v1 +metadata: + name: users-service-abc + namespace: default + labels: + kubernetes.io/service-name: users-service +addressType: IPv4 +ports: + - name: web + port: 9000 +endpoints: + - addresses: + - 10.10.5.1 + - 10.10.5.2 + conditions: + ready: true +--- +apiVersion: v1 +kind: Service +metadata: + name: shared-service + namespace: default +spec: + ports: + - name: web + port: 9000 +--- +kind: EndpointSlice +apiVersion: discovery.k8s.io/v1 +metadata: + name: shared-service-abc + namespace: default + labels: + kubernetes.io/service-name: shared-service +addressType: IPv4 +ports: + - name: web + port: 9000 +endpoints: + - addresses: + - 10.10.8.1 + - 10.10.8.2 + conditions: + ready: true +--- +apiVersion: v1 +kind: Service +metadata: + name: cross-service + namespace: ns-b +spec: + ports: + - name: web + port: 9000 +--- +kind: EndpointSlice +apiVersion: discovery.k8s.io/v1 +metadata: + name: cross-service-abc + namespace: ns-b + labels: + kubernetes.io/service-name: cross-service +addressType: IPv4 +ports: + - name: web + port: 9000 +endpoints: + - addresses: + - 10.10.11.1 + - 10.10.11.2 + conditions: + ready: true +--- +apiVersion: v1 +kind: Service +metadata: + name: same-service + namespace: default +spec: + ports: + - name: web + port: 9000 +--- +kind: EndpointSlice +apiVersion: discovery.k8s.io/v1 +metadata: + name: same-service-abc + namespace: default + labels: + kubernetes.io/service-name: same-service +addressType: IPv4 +ports: + - name: web + port: 9000 +endpoints: + - addresses: + - 10.10.14.1 + - 10.10.14.2 + conditions: + ready: true \ No newline at end of file diff --git a/pkg/provider/kubernetes/crd/fixtures/parent_refs_single_parent_multiple_routes.yml b/pkg/provider/kubernetes/crd/fixtures/parent_refs_single_parent_multiple_routes.yml new file mode 100644 index 000000000..4d91e32a6 --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/parent_refs_single_parent_multiple_routes.yml @@ -0,0 +1,30 @@ +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: parent-multi + namespace: default +spec: + entryPoints: + - web + routes: + - match: Host(`api.example.com`) && PathPrefix(`/v1`) + kind: Rule + - match: Host(`api.example.com`) && PathPrefix(`/v2`) + kind: Rule +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: child-multi-routes + namespace: default +spec: + parentRefs: + - name: parent-multi + namespace: default + routes: + - match: Path(`/users`) + kind: Rule + services: + - name: users-service + port: 9000 \ No newline at end of file diff --git a/pkg/provider/kubernetes/crd/fixtures/parent_refs_single_parent_single_route.yml b/pkg/provider/kubernetes/crd/fixtures/parent_refs_single_parent_single_route.yml new file mode 100644 index 000000000..073776252 --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/parent_refs_single_parent_single_route.yml @@ -0,0 +1,28 @@ +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: parent-single + namespace: default +spec: + entryPoints: + - web + routes: + - match: Host(`parent.example.com`) + kind: Rule +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: child-single + namespace: default +spec: + parentRefs: + - name: parent-single + namespace: default + routes: + - match: Path(`/api`) + kind: Rule + services: + - name: child-service + port: 9000 \ No newline at end of file diff --git a/pkg/provider/kubernetes/crd/fixtures/with_highest_random_weight.yml b/pkg/provider/kubernetes/crd/fixtures/with_highest_random_weight.yml new file mode 100644 index 000000000..f6684c03e --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/with_highest_random_weight.yml @@ -0,0 +1,107 @@ +--- +kind: EndpointSlice +apiVersion: discovery.k8s.io/v1 +metadata: + name: whoami1-abc + namespace: default + labels: + kubernetes.io/service-name: whoami1 + +addressType: IPv4 +ports: + - name: web + port: 8080 +endpoints: + - addresses: + - 10.10.0.1 + - 10.10.0.2 + conditions: + ready: true + +--- +apiVersion: v1 +kind: Service +metadata: + name: whoami1 + namespace: default + +spec: + ports: + - name: web + port: 8080 + selector: + app: traefiklabs + task: whoami1 + +--- +kind: EndpointSlice +apiVersion: discovery.k8s.io/v1 +metadata: + name: whoami2-abc + namespace: default + labels: + kubernetes.io/service-name: whoami2 + +addressType: IPv4 +ports: + - name: web + port: 8080 +endpoints: + - addresses: + - 10.10.0.3 + - 10.10.0.4 + conditions: + ready: true + +--- +apiVersion: v1 +kind: Service +metadata: + name: whoami2 + namespace: default + +spec: + ports: + - name: web + port: 8080 + selector: + app: traefiklabs + task: whoami2 + +--- +apiVersion: traefik.io/v1alpha1 +kind: TraefikService +metadata: + name: hrw1 + namespace: default + +spec: + highestRandomWeight: + services: + - name: whoami1 + kind: Service + port: 8080 + weight: 10 + - name: whoami2 + kind: Service + port: 8080 + weight: 20 + +--- +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: test.route + namespace: default + +spec: + entryPoints: + - web + + routes: + - match: Host(`foo.com`) && PathPrefix(`/foo`) + kind: Rule + priority: 12 + services: + - name: hrw1 + kind: TraefikService \ No newline at end of file diff --git a/pkg/provider/kubernetes/crd/fixtures/with_leasttime_strategy.yml b/pkg/provider/kubernetes/crd/fixtures/with_leasttime_strategy.yml new file mode 100644 index 000000000..4f47fd4bb --- /dev/null +++ b/pkg/provider/kubernetes/crd/fixtures/with_leasttime_strategy.yml @@ -0,0 +1,16 @@ +apiVersion: traefik.io/v1alpha1 +kind: IngressRoute +metadata: + name: test.route + namespace: default +spec: + entryPoints: + - web + routes: + - match: Host(`foo.com`) && PathPrefix(`/leasttime`) + kind: Rule + priority: 12 + services: + - name: whoami2 + port: 8080 + strategy: leasttime diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/internal/internal.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/internal/internal.go new file mode 100644 index 000000000..945a4ae8e --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/internal/internal.go @@ -0,0 +1,70 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package internal + +import ( + fmt "fmt" + sync "sync" + + typed "sigs.k8s.io/structured-merge-diff/v6/typed" +) + +func Parser() *typed.Parser { + parserOnce.Do(func() { + var err error + parser, err = typed.NewParser(schemaYAML) + if err != nil { + panic(fmt.Sprintf("Failed to parse schema: %v", err)) + } + }) + return parser +} + +var parserOnce sync.Once +var parser *typed.Parser +var schemaYAML = typed.YAMLObject(`types: +- name: __untyped_atomic_ + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic +- name: __untyped_deduced_ + scalar: untyped + list: + elementType: + namedType: __untyped_atomic_ + elementRelationship: atomic + map: + elementType: + namedType: __untyped_deduced_ + elementRelationship: separable +`) diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/basicauth.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/basicauth.go new file mode 100644 index 000000000..d903414dc --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/basicauth.go @@ -0,0 +1,74 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// BasicAuthApplyConfiguration represents a declarative configuration of the BasicAuth type for use +// with apply. +type BasicAuthApplyConfiguration struct { + Secret *string `json:"secret,omitempty"` + Realm *string `json:"realm,omitempty"` + RemoveHeader *bool `json:"removeHeader,omitempty"` + HeaderField *string `json:"headerField,omitempty"` +} + +// BasicAuthApplyConfiguration constructs a declarative configuration of the BasicAuth type for use with +// apply. +func BasicAuth() *BasicAuthApplyConfiguration { + return &BasicAuthApplyConfiguration{} +} + +// WithSecret sets the Secret field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Secret field is set to the value of the last call. +func (b *BasicAuthApplyConfiguration) WithSecret(value string) *BasicAuthApplyConfiguration { + b.Secret = &value + return b +} + +// WithRealm sets the Realm field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Realm field is set to the value of the last call. +func (b *BasicAuthApplyConfiguration) WithRealm(value string) *BasicAuthApplyConfiguration { + b.Realm = &value + return b +} + +// WithRemoveHeader sets the RemoveHeader field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the RemoveHeader field is set to the value of the last call. +func (b *BasicAuthApplyConfiguration) WithRemoveHeader(value bool) *BasicAuthApplyConfiguration { + b.RemoveHeader = &value + return b +} + +// WithHeaderField sets the HeaderField field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the HeaderField field is set to the value of the last call. +func (b *BasicAuthApplyConfiguration) WithHeaderField(value string) *BasicAuthApplyConfiguration { + b.HeaderField = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/certificate.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/certificate.go new file mode 100644 index 000000000..33f85d146 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/certificate.go @@ -0,0 +1,47 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// CertificateApplyConfiguration represents a declarative configuration of the Certificate type for use +// with apply. +type CertificateApplyConfiguration struct { + SecretName *string `json:"secretName,omitempty"` +} + +// CertificateApplyConfiguration constructs a declarative configuration of the Certificate type for use with +// apply. +func Certificate() *CertificateApplyConfiguration { + return &CertificateApplyConfiguration{} +} + +// WithSecretName sets the SecretName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SecretName field is set to the value of the last call. +func (b *CertificateApplyConfiguration) WithSecretName(value string) *CertificateApplyConfiguration { + b.SecretName = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/chain.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/chain.go new file mode 100644 index 000000000..221da3315 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/chain.go @@ -0,0 +1,52 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// ChainApplyConfiguration represents a declarative configuration of the Chain type for use +// with apply. +type ChainApplyConfiguration struct { + Middlewares []MiddlewareRefApplyConfiguration `json:"middlewares,omitempty"` +} + +// ChainApplyConfiguration constructs a declarative configuration of the Chain type for use with +// apply. +func Chain() *ChainApplyConfiguration { + return &ChainApplyConfiguration{} +} + +// WithMiddlewares adds the given value to the Middlewares field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Middlewares field. +func (b *ChainApplyConfiguration) WithMiddlewares(values ...*MiddlewareRefApplyConfiguration) *ChainApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithMiddlewares") + } + b.Middlewares = append(b.Middlewares, *values[i]) + } + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/circuitbreaker.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/circuitbreaker.go new file mode 100644 index 000000000..551041f0f --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/circuitbreaker.go @@ -0,0 +1,87 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + intstr "k8s.io/apimachinery/pkg/util/intstr" +) + +// CircuitBreakerApplyConfiguration represents a declarative configuration of the CircuitBreaker type for use +// with apply. +type CircuitBreakerApplyConfiguration struct { + Expression *string `json:"expression,omitempty"` + CheckPeriod *intstr.IntOrString `json:"checkPeriod,omitempty"` + FallbackDuration *intstr.IntOrString `json:"fallbackDuration,omitempty"` + RecoveryDuration *intstr.IntOrString `json:"recoveryDuration,omitempty"` + ResponseCode *int `json:"responseCode,omitempty"` +} + +// CircuitBreakerApplyConfiguration constructs a declarative configuration of the CircuitBreaker type for use with +// apply. +func CircuitBreaker() *CircuitBreakerApplyConfiguration { + return &CircuitBreakerApplyConfiguration{} +} + +// WithExpression sets the Expression field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Expression field is set to the value of the last call. +func (b *CircuitBreakerApplyConfiguration) WithExpression(value string) *CircuitBreakerApplyConfiguration { + b.Expression = &value + return b +} + +// WithCheckPeriod sets the CheckPeriod field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CheckPeriod field is set to the value of the last call. +func (b *CircuitBreakerApplyConfiguration) WithCheckPeriod(value intstr.IntOrString) *CircuitBreakerApplyConfiguration { + b.CheckPeriod = &value + return b +} + +// WithFallbackDuration sets the FallbackDuration field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the FallbackDuration field is set to the value of the last call. +func (b *CircuitBreakerApplyConfiguration) WithFallbackDuration(value intstr.IntOrString) *CircuitBreakerApplyConfiguration { + b.FallbackDuration = &value + return b +} + +// WithRecoveryDuration sets the RecoveryDuration field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the RecoveryDuration field is set to the value of the last call. +func (b *CircuitBreakerApplyConfiguration) WithRecoveryDuration(value intstr.IntOrString) *CircuitBreakerApplyConfiguration { + b.RecoveryDuration = &value + return b +} + +// WithResponseCode sets the ResponseCode field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResponseCode field is set to the value of the last call. +func (b *CircuitBreakerApplyConfiguration) WithResponseCode(value int) *CircuitBreakerApplyConfiguration { + b.ResponseCode = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/clientauth.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/clientauth.go new file mode 100644 index 000000000..6227ffdc7 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/clientauth.go @@ -0,0 +1,58 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// ClientAuthApplyConfiguration represents a declarative configuration of the ClientAuth type for use +// with apply. +type ClientAuthApplyConfiguration struct { + SecretNames []string `json:"secretNames,omitempty"` + ClientAuthType *string `json:"clientAuthType,omitempty"` +} + +// ClientAuthApplyConfiguration constructs a declarative configuration of the ClientAuth type for use with +// apply. +func ClientAuth() *ClientAuthApplyConfiguration { + return &ClientAuthApplyConfiguration{} +} + +// WithSecretNames adds the given value to the SecretNames field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the SecretNames field. +func (b *ClientAuthApplyConfiguration) WithSecretNames(values ...string) *ClientAuthApplyConfiguration { + for i := range values { + b.SecretNames = append(b.SecretNames, values[i]) + } + return b +} + +// WithClientAuthType sets the ClientAuthType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ClientAuthType field is set to the value of the last call. +func (b *ClientAuthApplyConfiguration) WithClientAuthType(value string) *ClientAuthApplyConfiguration { + b.ClientAuthType = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/clienttls.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/clienttls.go new file mode 100644 index 000000000..dd5f3c2bc --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/clienttls.go @@ -0,0 +1,65 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// ClientTLSApplyConfiguration represents a declarative configuration of the ClientTLS type for use +// with apply. +type ClientTLSApplyConfiguration struct { + CASecret *string `json:"caSecret,omitempty"` + CertSecret *string `json:"certSecret,omitempty"` + InsecureSkipVerify *bool `json:"insecureSkipVerify,omitempty"` +} + +// ClientTLSApplyConfiguration constructs a declarative configuration of the ClientTLS type for use with +// apply. +func ClientTLS() *ClientTLSApplyConfiguration { + return &ClientTLSApplyConfiguration{} +} + +// WithCASecret sets the CASecret field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CASecret field is set to the value of the last call. +func (b *ClientTLSApplyConfiguration) WithCASecret(value string) *ClientTLSApplyConfiguration { + b.CASecret = &value + return b +} + +// WithCertSecret sets the CertSecret field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CertSecret field is set to the value of the last call. +func (b *ClientTLSApplyConfiguration) WithCertSecret(value string) *ClientTLSApplyConfiguration { + b.CertSecret = &value + return b +} + +// WithInsecureSkipVerify sets the InsecureSkipVerify field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the InsecureSkipVerify field is set to the value of the last call. +func (b *ClientTLSApplyConfiguration) WithInsecureSkipVerify(value bool) *ClientTLSApplyConfiguration { + b.InsecureSkipVerify = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/clienttlswithcaoptional.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/clienttlswithcaoptional.go new file mode 100644 index 000000000..d5e4e90ef --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/clienttlswithcaoptional.go @@ -0,0 +1,72 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// ClientTLSWithCAOptionalApplyConfiguration represents a declarative configuration of the ClientTLSWithCAOptional type for use +// with apply. +type ClientTLSWithCAOptionalApplyConfiguration struct { + ClientTLSApplyConfiguration `json:",inline"` + CAOptional *bool `json:"caOptional,omitempty"` +} + +// ClientTLSWithCAOptionalApplyConfiguration constructs a declarative configuration of the ClientTLSWithCAOptional type for use with +// apply. +func ClientTLSWithCAOptional() *ClientTLSWithCAOptionalApplyConfiguration { + return &ClientTLSWithCAOptionalApplyConfiguration{} +} + +// WithCASecret sets the CASecret field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CASecret field is set to the value of the last call. +func (b *ClientTLSWithCAOptionalApplyConfiguration) WithCASecret(value string) *ClientTLSWithCAOptionalApplyConfiguration { + b.ClientTLSApplyConfiguration.CASecret = &value + return b +} + +// WithCertSecret sets the CertSecret field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CertSecret field is set to the value of the last call. +func (b *ClientTLSWithCAOptionalApplyConfiguration) WithCertSecret(value string) *ClientTLSWithCAOptionalApplyConfiguration { + b.ClientTLSApplyConfiguration.CertSecret = &value + return b +} + +// WithInsecureSkipVerify sets the InsecureSkipVerify field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the InsecureSkipVerify field is set to the value of the last call. +func (b *ClientTLSWithCAOptionalApplyConfiguration) WithInsecureSkipVerify(value bool) *ClientTLSWithCAOptionalApplyConfiguration { + b.ClientTLSApplyConfiguration.InsecureSkipVerify = &value + return b +} + +// WithCAOptional sets the CAOptional field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CAOptional field is set to the value of the last call. +func (b *ClientTLSWithCAOptionalApplyConfiguration) WithCAOptional(value bool) *ClientTLSWithCAOptionalApplyConfiguration { + b.CAOptional = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/compress.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/compress.go new file mode 100644 index 000000000..5e5eea00c --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/compress.go @@ -0,0 +1,89 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// CompressApplyConfiguration represents a declarative configuration of the Compress type for use +// with apply. +type CompressApplyConfiguration struct { + ExcludedContentTypes []string `json:"excludedContentTypes,omitempty"` + IncludedContentTypes []string `json:"includedContentTypes,omitempty"` + MinResponseBodyBytes *int `json:"minResponseBodyBytes,omitempty"` + Encodings []string `json:"encodings,omitempty"` + DefaultEncoding *string `json:"defaultEncoding,omitempty"` +} + +// CompressApplyConfiguration constructs a declarative configuration of the Compress type for use with +// apply. +func Compress() *CompressApplyConfiguration { + return &CompressApplyConfiguration{} +} + +// WithExcludedContentTypes adds the given value to the ExcludedContentTypes field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the ExcludedContentTypes field. +func (b *CompressApplyConfiguration) WithExcludedContentTypes(values ...string) *CompressApplyConfiguration { + for i := range values { + b.ExcludedContentTypes = append(b.ExcludedContentTypes, values[i]) + } + return b +} + +// WithIncludedContentTypes adds the given value to the IncludedContentTypes field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the IncludedContentTypes field. +func (b *CompressApplyConfiguration) WithIncludedContentTypes(values ...string) *CompressApplyConfiguration { + for i := range values { + b.IncludedContentTypes = append(b.IncludedContentTypes, values[i]) + } + return b +} + +// WithMinResponseBodyBytes sets the MinResponseBodyBytes field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the MinResponseBodyBytes field is set to the value of the last call. +func (b *CompressApplyConfiguration) WithMinResponseBodyBytes(value int) *CompressApplyConfiguration { + b.MinResponseBodyBytes = &value + return b +} + +// WithEncodings adds the given value to the Encodings field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Encodings field. +func (b *CompressApplyConfiguration) WithEncodings(values ...string) *CompressApplyConfiguration { + for i := range values { + b.Encodings = append(b.Encodings, values[i]) + } + return b +} + +// WithDefaultEncoding sets the DefaultEncoding field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DefaultEncoding field is set to the value of the last call. +func (b *CompressApplyConfiguration) WithDefaultEncoding(value string) *CompressApplyConfiguration { + b.DefaultEncoding = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/digestauth.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/digestauth.go new file mode 100644 index 000000000..70bf9b3b9 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/digestauth.go @@ -0,0 +1,74 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// DigestAuthApplyConfiguration represents a declarative configuration of the DigestAuth type for use +// with apply. +type DigestAuthApplyConfiguration struct { + Secret *string `json:"secret,omitempty"` + RemoveHeader *bool `json:"removeHeader,omitempty"` + Realm *string `json:"realm,omitempty"` + HeaderField *string `json:"headerField,omitempty"` +} + +// DigestAuthApplyConfiguration constructs a declarative configuration of the DigestAuth type for use with +// apply. +func DigestAuth() *DigestAuthApplyConfiguration { + return &DigestAuthApplyConfiguration{} +} + +// WithSecret sets the Secret field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Secret field is set to the value of the last call. +func (b *DigestAuthApplyConfiguration) WithSecret(value string) *DigestAuthApplyConfiguration { + b.Secret = &value + return b +} + +// WithRemoveHeader sets the RemoveHeader field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the RemoveHeader field is set to the value of the last call. +func (b *DigestAuthApplyConfiguration) WithRemoveHeader(value bool) *DigestAuthApplyConfiguration { + b.RemoveHeader = &value + return b +} + +// WithRealm sets the Realm field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Realm field is set to the value of the last call. +func (b *DigestAuthApplyConfiguration) WithRealm(value string) *DigestAuthApplyConfiguration { + b.Realm = &value + return b +} + +// WithHeaderField sets the HeaderField field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the HeaderField field is set to the value of the last call. +func (b *DigestAuthApplyConfiguration) WithHeaderField(value string) *DigestAuthApplyConfiguration { + b.HeaderField = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/errorpage.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/errorpage.go new file mode 100644 index 000000000..3273eb655 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/errorpage.go @@ -0,0 +1,82 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// ErrorPageApplyConfiguration represents a declarative configuration of the ErrorPage type for use +// with apply. +type ErrorPageApplyConfiguration struct { + Status []string `json:"status,omitempty"` + StatusRewrites map[string]int `json:"statusRewrites,omitempty"` + Service *ServiceApplyConfiguration `json:"service,omitempty"` + Query *string `json:"query,omitempty"` +} + +// ErrorPageApplyConfiguration constructs a declarative configuration of the ErrorPage type for use with +// apply. +func ErrorPage() *ErrorPageApplyConfiguration { + return &ErrorPageApplyConfiguration{} +} + +// WithStatus adds the given value to the Status field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Status field. +func (b *ErrorPageApplyConfiguration) WithStatus(values ...string) *ErrorPageApplyConfiguration { + for i := range values { + b.Status = append(b.Status, values[i]) + } + return b +} + +// WithStatusRewrites puts the entries into the StatusRewrites field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the StatusRewrites field, +// overwriting an existing map entries in StatusRewrites field with the same key. +func (b *ErrorPageApplyConfiguration) WithStatusRewrites(entries map[string]int) *ErrorPageApplyConfiguration { + if b.StatusRewrites == nil && len(entries) > 0 { + b.StatusRewrites = make(map[string]int, len(entries)) + } + for k, v := range entries { + b.StatusRewrites[k] = v + } + return b +} + +// WithService sets the Service field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Service field is set to the value of the last call. +func (b *ErrorPageApplyConfiguration) WithService(value *ServiceApplyConfiguration) *ErrorPageApplyConfiguration { + b.Service = value + return b +} + +// WithQuery sets the Query field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Query field is set to the value of the last call. +func (b *ErrorPageApplyConfiguration) WithQuery(value string) *ErrorPageApplyConfiguration { + b.Query = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/forwardauth.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/forwardauth.go new file mode 100644 index 000000000..ab9d603b3 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/forwardauth.go @@ -0,0 +1,152 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// ForwardAuthApplyConfiguration represents a declarative configuration of the ForwardAuth type for use +// with apply. +type ForwardAuthApplyConfiguration struct { + Address *string `json:"address,omitempty"` + TrustForwardHeader *bool `json:"trustForwardHeader,omitempty"` + AuthResponseHeaders []string `json:"authResponseHeaders,omitempty"` + AuthResponseHeadersRegex *string `json:"authResponseHeadersRegex,omitempty"` + AuthRequestHeaders []string `json:"authRequestHeaders,omitempty"` + TLS *ClientTLSWithCAOptionalApplyConfiguration `json:"tls,omitempty"` + AddAuthCookiesToResponse []string `json:"addAuthCookiesToResponse,omitempty"` + HeaderField *string `json:"headerField,omitempty"` + ForwardBody *bool `json:"forwardBody,omitempty"` + MaxBodySize *int64 `json:"maxBodySize,omitempty"` + PreserveLocationHeader *bool `json:"preserveLocationHeader,omitempty"` + PreserveRequestMethod *bool `json:"preserveRequestMethod,omitempty"` +} + +// ForwardAuthApplyConfiguration constructs a declarative configuration of the ForwardAuth type for use with +// apply. +func ForwardAuth() *ForwardAuthApplyConfiguration { + return &ForwardAuthApplyConfiguration{} +} + +// WithAddress sets the Address field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Address field is set to the value of the last call. +func (b *ForwardAuthApplyConfiguration) WithAddress(value string) *ForwardAuthApplyConfiguration { + b.Address = &value + return b +} + +// WithTrustForwardHeader sets the TrustForwardHeader field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the TrustForwardHeader field is set to the value of the last call. +func (b *ForwardAuthApplyConfiguration) WithTrustForwardHeader(value bool) *ForwardAuthApplyConfiguration { + b.TrustForwardHeader = &value + return b +} + +// WithAuthResponseHeaders adds the given value to the AuthResponseHeaders field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the AuthResponseHeaders field. +func (b *ForwardAuthApplyConfiguration) WithAuthResponseHeaders(values ...string) *ForwardAuthApplyConfiguration { + for i := range values { + b.AuthResponseHeaders = append(b.AuthResponseHeaders, values[i]) + } + return b +} + +// WithAuthResponseHeadersRegex sets the AuthResponseHeadersRegex field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the AuthResponseHeadersRegex field is set to the value of the last call. +func (b *ForwardAuthApplyConfiguration) WithAuthResponseHeadersRegex(value string) *ForwardAuthApplyConfiguration { + b.AuthResponseHeadersRegex = &value + return b +} + +// WithAuthRequestHeaders adds the given value to the AuthRequestHeaders field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the AuthRequestHeaders field. +func (b *ForwardAuthApplyConfiguration) WithAuthRequestHeaders(values ...string) *ForwardAuthApplyConfiguration { + for i := range values { + b.AuthRequestHeaders = append(b.AuthRequestHeaders, values[i]) + } + return b +} + +// WithTLS sets the TLS field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the TLS field is set to the value of the last call. +func (b *ForwardAuthApplyConfiguration) WithTLS(value *ClientTLSWithCAOptionalApplyConfiguration) *ForwardAuthApplyConfiguration { + b.TLS = value + return b +} + +// WithAddAuthCookiesToResponse adds the given value to the AddAuthCookiesToResponse field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the AddAuthCookiesToResponse field. +func (b *ForwardAuthApplyConfiguration) WithAddAuthCookiesToResponse(values ...string) *ForwardAuthApplyConfiguration { + for i := range values { + b.AddAuthCookiesToResponse = append(b.AddAuthCookiesToResponse, values[i]) + } + return b +} + +// WithHeaderField sets the HeaderField field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the HeaderField field is set to the value of the last call. +func (b *ForwardAuthApplyConfiguration) WithHeaderField(value string) *ForwardAuthApplyConfiguration { + b.HeaderField = &value + return b +} + +// WithForwardBody sets the ForwardBody field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ForwardBody field is set to the value of the last call. +func (b *ForwardAuthApplyConfiguration) WithForwardBody(value bool) *ForwardAuthApplyConfiguration { + b.ForwardBody = &value + return b +} + +// WithMaxBodySize sets the MaxBodySize field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the MaxBodySize field is set to the value of the last call. +func (b *ForwardAuthApplyConfiguration) WithMaxBodySize(value int64) *ForwardAuthApplyConfiguration { + b.MaxBodySize = &value + return b +} + +// WithPreserveLocationHeader sets the PreserveLocationHeader field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PreserveLocationHeader field is set to the value of the last call. +func (b *ForwardAuthApplyConfiguration) WithPreserveLocationHeader(value bool) *ForwardAuthApplyConfiguration { + b.PreserveLocationHeader = &value + return b +} + +// WithPreserveRequestMethod sets the PreserveRequestMethod field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PreserveRequestMethod field is set to the value of the last call. +func (b *ForwardAuthApplyConfiguration) WithPreserveRequestMethod(value bool) *ForwardAuthApplyConfiguration { + b.PreserveRequestMethod = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/forwardingtimeouts.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/forwardingtimeouts.go new file mode 100644 index 000000000..b24bb3501 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/forwardingtimeouts.go @@ -0,0 +1,87 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + intstr "k8s.io/apimachinery/pkg/util/intstr" +) + +// ForwardingTimeoutsApplyConfiguration represents a declarative configuration of the ForwardingTimeouts type for use +// with apply. +type ForwardingTimeoutsApplyConfiguration struct { + DialTimeout *intstr.IntOrString `json:"dialTimeout,omitempty"` + ResponseHeaderTimeout *intstr.IntOrString `json:"responseHeaderTimeout,omitempty"` + IdleConnTimeout *intstr.IntOrString `json:"idleConnTimeout,omitempty"` + ReadIdleTimeout *intstr.IntOrString `json:"readIdleTimeout,omitempty"` + PingTimeout *intstr.IntOrString `json:"pingTimeout,omitempty"` +} + +// ForwardingTimeoutsApplyConfiguration constructs a declarative configuration of the ForwardingTimeouts type for use with +// apply. +func ForwardingTimeouts() *ForwardingTimeoutsApplyConfiguration { + return &ForwardingTimeoutsApplyConfiguration{} +} + +// WithDialTimeout sets the DialTimeout field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DialTimeout field is set to the value of the last call. +func (b *ForwardingTimeoutsApplyConfiguration) WithDialTimeout(value intstr.IntOrString) *ForwardingTimeoutsApplyConfiguration { + b.DialTimeout = &value + return b +} + +// WithResponseHeaderTimeout sets the ResponseHeaderTimeout field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResponseHeaderTimeout field is set to the value of the last call. +func (b *ForwardingTimeoutsApplyConfiguration) WithResponseHeaderTimeout(value intstr.IntOrString) *ForwardingTimeoutsApplyConfiguration { + b.ResponseHeaderTimeout = &value + return b +} + +// WithIdleConnTimeout sets the IdleConnTimeout field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the IdleConnTimeout field is set to the value of the last call. +func (b *ForwardingTimeoutsApplyConfiguration) WithIdleConnTimeout(value intstr.IntOrString) *ForwardingTimeoutsApplyConfiguration { + b.IdleConnTimeout = &value + return b +} + +// WithReadIdleTimeout sets the ReadIdleTimeout field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ReadIdleTimeout field is set to the value of the last call. +func (b *ForwardingTimeoutsApplyConfiguration) WithReadIdleTimeout(value intstr.IntOrString) *ForwardingTimeoutsApplyConfiguration { + b.ReadIdleTimeout = &value + return b +} + +// WithPingTimeout sets the PingTimeout field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PingTimeout field is set to the value of the last call. +func (b *ForwardingTimeoutsApplyConfiguration) WithPingTimeout(value intstr.IntOrString) *ForwardingTimeoutsApplyConfiguration { + b.PingTimeout = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/highestrandomweight.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/highestrandomweight.go new file mode 100644 index 000000000..f96acf722 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/highestrandomweight.go @@ -0,0 +1,52 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// HighestRandomWeightApplyConfiguration represents a declarative configuration of the HighestRandomWeight type for use +// with apply. +type HighestRandomWeightApplyConfiguration struct { + Services []ServiceApplyConfiguration `json:"services,omitempty"` +} + +// HighestRandomWeightApplyConfiguration constructs a declarative configuration of the HighestRandomWeight type for use with +// apply. +func HighestRandomWeight() *HighestRandomWeightApplyConfiguration { + return &HighestRandomWeightApplyConfiguration{} +} + +// WithServices adds the given value to the Services field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Services field. +func (b *HighestRandomWeightApplyConfiguration) WithServices(values ...*ServiceApplyConfiguration) *HighestRandomWeightApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithServices") + } + b.Services = append(b.Services, *values[i]) + } + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressroute.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressroute.go new file mode 100644 index 000000000..500cb6192 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressroute.go @@ -0,0 +1,241 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// IngressRouteApplyConfiguration represents a declarative configuration of the IngressRoute type for use +// with apply. +type IngressRouteApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *IngressRouteSpecApplyConfiguration `json:"spec,omitempty"` +} + +// IngressRoute constructs a declarative configuration of the IngressRoute type for use with +// apply. +func IngressRoute(name, namespace string) *IngressRouteApplyConfiguration { + b := &IngressRouteApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("IngressRoute") + b.WithAPIVersion("traefik.io/v1alpha1") + return b +} +func (b IngressRouteApplyConfiguration) IsApplyConfiguration() {} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *IngressRouteApplyConfiguration) WithKind(value string) *IngressRouteApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *IngressRouteApplyConfiguration) WithAPIVersion(value string) *IngressRouteApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *IngressRouteApplyConfiguration) WithName(value string) *IngressRouteApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *IngressRouteApplyConfiguration) WithGenerateName(value string) *IngressRouteApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *IngressRouteApplyConfiguration) WithNamespace(value string) *IngressRouteApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *IngressRouteApplyConfiguration) WithUID(value types.UID) *IngressRouteApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *IngressRouteApplyConfiguration) WithResourceVersion(value string) *IngressRouteApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *IngressRouteApplyConfiguration) WithGeneration(value int64) *IngressRouteApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *IngressRouteApplyConfiguration) WithCreationTimestamp(value metav1.Time) *IngressRouteApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *IngressRouteApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *IngressRouteApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *IngressRouteApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *IngressRouteApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *IngressRouteApplyConfiguration) WithLabels(entries map[string]string) *IngressRouteApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *IngressRouteApplyConfiguration) WithAnnotations(entries map[string]string) *IngressRouteApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *IngressRouteApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *IngressRouteApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *IngressRouteApplyConfiguration) WithFinalizers(values ...string) *IngressRouteApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *IngressRouteApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} + } +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spec field is set to the value of the last call. +func (b *IngressRouteApplyConfiguration) WithSpec(value *IngressRouteSpecApplyConfiguration) *IngressRouteApplyConfiguration { + b.Spec = value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *IngressRouteApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *IngressRouteApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *IngressRouteApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *IngressRouteApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressrouteref.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressrouteref.go new file mode 100644 index 000000000..aba2dac46 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressrouteref.go @@ -0,0 +1,56 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// IngressRouteRefApplyConfiguration represents a declarative configuration of the IngressRouteRef type for use +// with apply. +type IngressRouteRefApplyConfiguration struct { + Name *string `json:"name,omitempty"` + Namespace *string `json:"namespace,omitempty"` +} + +// IngressRouteRefApplyConfiguration constructs a declarative configuration of the IngressRouteRef type for use with +// apply. +func IngressRouteRef() *IngressRouteRefApplyConfiguration { + return &IngressRouteRefApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *IngressRouteRefApplyConfiguration) WithName(value string) *IngressRouteRefApplyConfiguration { + b.Name = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *IngressRouteRefApplyConfiguration) WithNamespace(value string) *IngressRouteRefApplyConfiguration { + b.Namespace = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressroutespec.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressroutespec.go new file mode 100644 index 000000000..04b0b3e76 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressroutespec.go @@ -0,0 +1,86 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// IngressRouteSpecApplyConfiguration represents a declarative configuration of the IngressRouteSpec type for use +// with apply. +type IngressRouteSpecApplyConfiguration struct { + Routes []RouteApplyConfiguration `json:"routes,omitempty"` + EntryPoints []string `json:"entryPoints,omitempty"` + TLS *TLSApplyConfiguration `json:"tls,omitempty"` + ParentRefs []IngressRouteRefApplyConfiguration `json:"parentRefs,omitempty"` +} + +// IngressRouteSpecApplyConfiguration constructs a declarative configuration of the IngressRouteSpec type for use with +// apply. +func IngressRouteSpec() *IngressRouteSpecApplyConfiguration { + return &IngressRouteSpecApplyConfiguration{} +} + +// WithRoutes adds the given value to the Routes field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Routes field. +func (b *IngressRouteSpecApplyConfiguration) WithRoutes(values ...*RouteApplyConfiguration) *IngressRouteSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithRoutes") + } + b.Routes = append(b.Routes, *values[i]) + } + return b +} + +// WithEntryPoints adds the given value to the EntryPoints field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the EntryPoints field. +func (b *IngressRouteSpecApplyConfiguration) WithEntryPoints(values ...string) *IngressRouteSpecApplyConfiguration { + for i := range values { + b.EntryPoints = append(b.EntryPoints, values[i]) + } + return b +} + +// WithTLS sets the TLS field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the TLS field is set to the value of the last call. +func (b *IngressRouteSpecApplyConfiguration) WithTLS(value *TLSApplyConfiguration) *IngressRouteSpecApplyConfiguration { + b.TLS = value + return b +} + +// WithParentRefs adds the given value to the ParentRefs field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the ParentRefs field. +func (b *IngressRouteSpecApplyConfiguration) WithParentRefs(values ...*IngressRouteRefApplyConfiguration) *IngressRouteSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithParentRefs") + } + b.ParentRefs = append(b.ParentRefs, *values[i]) + } + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressroutetcp.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressroutetcp.go new file mode 100644 index 000000000..7a07a2d40 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressroutetcp.go @@ -0,0 +1,241 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// IngressRouteTCPApplyConfiguration represents a declarative configuration of the IngressRouteTCP type for use +// with apply. +type IngressRouteTCPApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *IngressRouteTCPSpecApplyConfiguration `json:"spec,omitempty"` +} + +// IngressRouteTCP constructs a declarative configuration of the IngressRouteTCP type for use with +// apply. +func IngressRouteTCP(name, namespace string) *IngressRouteTCPApplyConfiguration { + b := &IngressRouteTCPApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("IngressRouteTCP") + b.WithAPIVersion("traefik.io/v1alpha1") + return b +} +func (b IngressRouteTCPApplyConfiguration) IsApplyConfiguration() {} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *IngressRouteTCPApplyConfiguration) WithKind(value string) *IngressRouteTCPApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *IngressRouteTCPApplyConfiguration) WithAPIVersion(value string) *IngressRouteTCPApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *IngressRouteTCPApplyConfiguration) WithName(value string) *IngressRouteTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *IngressRouteTCPApplyConfiguration) WithGenerateName(value string) *IngressRouteTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *IngressRouteTCPApplyConfiguration) WithNamespace(value string) *IngressRouteTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *IngressRouteTCPApplyConfiguration) WithUID(value types.UID) *IngressRouteTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *IngressRouteTCPApplyConfiguration) WithResourceVersion(value string) *IngressRouteTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *IngressRouteTCPApplyConfiguration) WithGeneration(value int64) *IngressRouteTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *IngressRouteTCPApplyConfiguration) WithCreationTimestamp(value metav1.Time) *IngressRouteTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *IngressRouteTCPApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *IngressRouteTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *IngressRouteTCPApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *IngressRouteTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *IngressRouteTCPApplyConfiguration) WithLabels(entries map[string]string) *IngressRouteTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *IngressRouteTCPApplyConfiguration) WithAnnotations(entries map[string]string) *IngressRouteTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *IngressRouteTCPApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *IngressRouteTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *IngressRouteTCPApplyConfiguration) WithFinalizers(values ...string) *IngressRouteTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *IngressRouteTCPApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} + } +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spec field is set to the value of the last call. +func (b *IngressRouteTCPApplyConfiguration) WithSpec(value *IngressRouteTCPSpecApplyConfiguration) *IngressRouteTCPApplyConfiguration { + b.Spec = value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *IngressRouteTCPApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *IngressRouteTCPApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *IngressRouteTCPApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *IngressRouteTCPApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressroutetcpspec.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressroutetcpspec.go new file mode 100644 index 000000000..545452619 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressroutetcpspec.go @@ -0,0 +1,72 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// IngressRouteTCPSpecApplyConfiguration represents a declarative configuration of the IngressRouteTCPSpec type for use +// with apply. +type IngressRouteTCPSpecApplyConfiguration struct { + Routes []RouteTCPApplyConfiguration `json:"routes,omitempty"` + EntryPoints []string `json:"entryPoints,omitempty"` + TLS *TLSTCPApplyConfiguration `json:"tls,omitempty"` +} + +// IngressRouteTCPSpecApplyConfiguration constructs a declarative configuration of the IngressRouteTCPSpec type for use with +// apply. +func IngressRouteTCPSpec() *IngressRouteTCPSpecApplyConfiguration { + return &IngressRouteTCPSpecApplyConfiguration{} +} + +// WithRoutes adds the given value to the Routes field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Routes field. +func (b *IngressRouteTCPSpecApplyConfiguration) WithRoutes(values ...*RouteTCPApplyConfiguration) *IngressRouteTCPSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithRoutes") + } + b.Routes = append(b.Routes, *values[i]) + } + return b +} + +// WithEntryPoints adds the given value to the EntryPoints field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the EntryPoints field. +func (b *IngressRouteTCPSpecApplyConfiguration) WithEntryPoints(values ...string) *IngressRouteTCPSpecApplyConfiguration { + for i := range values { + b.EntryPoints = append(b.EntryPoints, values[i]) + } + return b +} + +// WithTLS sets the TLS field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the TLS field is set to the value of the last call. +func (b *IngressRouteTCPSpecApplyConfiguration) WithTLS(value *TLSTCPApplyConfiguration) *IngressRouteTCPSpecApplyConfiguration { + b.TLS = value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressrouteudp.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressrouteudp.go new file mode 100644 index 000000000..487e9c87a --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressrouteudp.go @@ -0,0 +1,241 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// IngressRouteUDPApplyConfiguration represents a declarative configuration of the IngressRouteUDP type for use +// with apply. +type IngressRouteUDPApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *IngressRouteUDPSpecApplyConfiguration `json:"spec,omitempty"` +} + +// IngressRouteUDP constructs a declarative configuration of the IngressRouteUDP type for use with +// apply. +func IngressRouteUDP(name, namespace string) *IngressRouteUDPApplyConfiguration { + b := &IngressRouteUDPApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("IngressRouteUDP") + b.WithAPIVersion("traefik.io/v1alpha1") + return b +} +func (b IngressRouteUDPApplyConfiguration) IsApplyConfiguration() {} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *IngressRouteUDPApplyConfiguration) WithKind(value string) *IngressRouteUDPApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *IngressRouteUDPApplyConfiguration) WithAPIVersion(value string) *IngressRouteUDPApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *IngressRouteUDPApplyConfiguration) WithName(value string) *IngressRouteUDPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *IngressRouteUDPApplyConfiguration) WithGenerateName(value string) *IngressRouteUDPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *IngressRouteUDPApplyConfiguration) WithNamespace(value string) *IngressRouteUDPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *IngressRouteUDPApplyConfiguration) WithUID(value types.UID) *IngressRouteUDPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *IngressRouteUDPApplyConfiguration) WithResourceVersion(value string) *IngressRouteUDPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *IngressRouteUDPApplyConfiguration) WithGeneration(value int64) *IngressRouteUDPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *IngressRouteUDPApplyConfiguration) WithCreationTimestamp(value metav1.Time) *IngressRouteUDPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *IngressRouteUDPApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *IngressRouteUDPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *IngressRouteUDPApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *IngressRouteUDPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *IngressRouteUDPApplyConfiguration) WithLabels(entries map[string]string) *IngressRouteUDPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *IngressRouteUDPApplyConfiguration) WithAnnotations(entries map[string]string) *IngressRouteUDPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *IngressRouteUDPApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *IngressRouteUDPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *IngressRouteUDPApplyConfiguration) WithFinalizers(values ...string) *IngressRouteUDPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *IngressRouteUDPApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} + } +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spec field is set to the value of the last call. +func (b *IngressRouteUDPApplyConfiguration) WithSpec(value *IngressRouteUDPSpecApplyConfiguration) *IngressRouteUDPApplyConfiguration { + b.Spec = value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *IngressRouteUDPApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *IngressRouteUDPApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *IngressRouteUDPApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *IngressRouteUDPApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressrouteudpspec.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressrouteudpspec.go new file mode 100644 index 000000000..96b317fed --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ingressrouteudpspec.go @@ -0,0 +1,63 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// IngressRouteUDPSpecApplyConfiguration represents a declarative configuration of the IngressRouteUDPSpec type for use +// with apply. +type IngressRouteUDPSpecApplyConfiguration struct { + Routes []RouteUDPApplyConfiguration `json:"routes,omitempty"` + EntryPoints []string `json:"entryPoints,omitempty"` +} + +// IngressRouteUDPSpecApplyConfiguration constructs a declarative configuration of the IngressRouteUDPSpec type for use with +// apply. +func IngressRouteUDPSpec() *IngressRouteUDPSpecApplyConfiguration { + return &IngressRouteUDPSpecApplyConfiguration{} +} + +// WithRoutes adds the given value to the Routes field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Routes field. +func (b *IngressRouteUDPSpecApplyConfiguration) WithRoutes(values ...*RouteUDPApplyConfiguration) *IngressRouteUDPSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithRoutes") + } + b.Routes = append(b.Routes, *values[i]) + } + return b +} + +// WithEntryPoints adds the given value to the EntryPoints field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the EntryPoints field. +func (b *IngressRouteUDPSpecApplyConfiguration) WithEntryPoints(values ...string) *IngressRouteUDPSpecApplyConfiguration { + for i := range values { + b.EntryPoints = append(b.EntryPoints, values[i]) + } + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/loadbalancerspec.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/loadbalancerspec.go new file mode 100644 index 000000000..726f425a2 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/loadbalancerspec.go @@ -0,0 +1,178 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + dynamic "github.com/traefik/traefik/v3/pkg/config/dynamic" + intstr "k8s.io/apimachinery/pkg/util/intstr" +) + +// LoadBalancerSpecApplyConfiguration represents a declarative configuration of the LoadBalancerSpec type for use +// with apply. +type LoadBalancerSpecApplyConfiguration struct { + Name *string `json:"name,omitempty"` + Kind *string `json:"kind,omitempty"` + Namespace *string `json:"namespace,omitempty"` + Sticky *dynamic.Sticky `json:"sticky,omitempty"` + Port *intstr.IntOrString `json:"port,omitempty"` + Scheme *string `json:"scheme,omitempty"` + Strategy *dynamic.BalancerStrategy `json:"strategy,omitempty"` + PassHostHeader *bool `json:"passHostHeader,omitempty"` + ResponseForwarding *ResponseForwardingApplyConfiguration `json:"responseForwarding,omitempty"` + ServersTransport *string `json:"serversTransport,omitempty"` + Weight *int `json:"weight,omitempty"` + NativeLB *bool `json:"nativeLB,omitempty"` + NodePortLB *bool `json:"nodePortLB,omitempty"` + HealthCheck *ServerHealthCheckApplyConfiguration `json:"healthCheck,omitempty"` + PassiveHealthCheck *PassiveServerHealthCheckApplyConfiguration `json:"passiveHealthCheck,omitempty"` +} + +// LoadBalancerSpecApplyConfiguration constructs a declarative configuration of the LoadBalancerSpec type for use with +// apply. +func LoadBalancerSpec() *LoadBalancerSpecApplyConfiguration { + return &LoadBalancerSpecApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *LoadBalancerSpecApplyConfiguration) WithName(value string) *LoadBalancerSpecApplyConfiguration { + b.Name = &value + return b +} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *LoadBalancerSpecApplyConfiguration) WithKind(value string) *LoadBalancerSpecApplyConfiguration { + b.Kind = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *LoadBalancerSpecApplyConfiguration) WithNamespace(value string) *LoadBalancerSpecApplyConfiguration { + b.Namespace = &value + return b +} + +// WithSticky sets the Sticky field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Sticky field is set to the value of the last call. +func (b *LoadBalancerSpecApplyConfiguration) WithSticky(value dynamic.Sticky) *LoadBalancerSpecApplyConfiguration { + b.Sticky = &value + return b +} + +// WithPort sets the Port field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Port field is set to the value of the last call. +func (b *LoadBalancerSpecApplyConfiguration) WithPort(value intstr.IntOrString) *LoadBalancerSpecApplyConfiguration { + b.Port = &value + return b +} + +// WithScheme sets the Scheme field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Scheme field is set to the value of the last call. +func (b *LoadBalancerSpecApplyConfiguration) WithScheme(value string) *LoadBalancerSpecApplyConfiguration { + b.Scheme = &value + return b +} + +// WithStrategy sets the Strategy field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Strategy field is set to the value of the last call. +func (b *LoadBalancerSpecApplyConfiguration) WithStrategy(value dynamic.BalancerStrategy) *LoadBalancerSpecApplyConfiguration { + b.Strategy = &value + return b +} + +// WithPassHostHeader sets the PassHostHeader field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PassHostHeader field is set to the value of the last call. +func (b *LoadBalancerSpecApplyConfiguration) WithPassHostHeader(value bool) *LoadBalancerSpecApplyConfiguration { + b.PassHostHeader = &value + return b +} + +// WithResponseForwarding sets the ResponseForwarding field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResponseForwarding field is set to the value of the last call. +func (b *LoadBalancerSpecApplyConfiguration) WithResponseForwarding(value *ResponseForwardingApplyConfiguration) *LoadBalancerSpecApplyConfiguration { + b.ResponseForwarding = value + return b +} + +// WithServersTransport sets the ServersTransport field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ServersTransport field is set to the value of the last call. +func (b *LoadBalancerSpecApplyConfiguration) WithServersTransport(value string) *LoadBalancerSpecApplyConfiguration { + b.ServersTransport = &value + return b +} + +// WithWeight sets the Weight field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Weight field is set to the value of the last call. +func (b *LoadBalancerSpecApplyConfiguration) WithWeight(value int) *LoadBalancerSpecApplyConfiguration { + b.Weight = &value + return b +} + +// WithNativeLB sets the NativeLB field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the NativeLB field is set to the value of the last call. +func (b *LoadBalancerSpecApplyConfiguration) WithNativeLB(value bool) *LoadBalancerSpecApplyConfiguration { + b.NativeLB = &value + return b +} + +// WithNodePortLB sets the NodePortLB field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the NodePortLB field is set to the value of the last call. +func (b *LoadBalancerSpecApplyConfiguration) WithNodePortLB(value bool) *LoadBalancerSpecApplyConfiguration { + b.NodePortLB = &value + return b +} + +// WithHealthCheck sets the HealthCheck field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the HealthCheck field is set to the value of the last call. +func (b *LoadBalancerSpecApplyConfiguration) WithHealthCheck(value *ServerHealthCheckApplyConfiguration) *LoadBalancerSpecApplyConfiguration { + b.HealthCheck = value + return b +} + +// WithPassiveHealthCheck sets the PassiveHealthCheck field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PassiveHealthCheck field is set to the value of the last call. +func (b *LoadBalancerSpecApplyConfiguration) WithPassiveHealthCheck(value *PassiveServerHealthCheckApplyConfiguration) *LoadBalancerSpecApplyConfiguration { + b.PassiveHealthCheck = value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/middleware.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/middleware.go new file mode 100644 index 000000000..98cbf2853 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/middleware.go @@ -0,0 +1,241 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// MiddlewareApplyConfiguration represents a declarative configuration of the Middleware type for use +// with apply. +type MiddlewareApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *MiddlewareSpecApplyConfiguration `json:"spec,omitempty"` +} + +// Middleware constructs a declarative configuration of the Middleware type for use with +// apply. +func Middleware(name, namespace string) *MiddlewareApplyConfiguration { + b := &MiddlewareApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("Middleware") + b.WithAPIVersion("traefik.io/v1alpha1") + return b +} +func (b MiddlewareApplyConfiguration) IsApplyConfiguration() {} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *MiddlewareApplyConfiguration) WithKind(value string) *MiddlewareApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *MiddlewareApplyConfiguration) WithAPIVersion(value string) *MiddlewareApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *MiddlewareApplyConfiguration) WithName(value string) *MiddlewareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *MiddlewareApplyConfiguration) WithGenerateName(value string) *MiddlewareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *MiddlewareApplyConfiguration) WithNamespace(value string) *MiddlewareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *MiddlewareApplyConfiguration) WithUID(value types.UID) *MiddlewareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *MiddlewareApplyConfiguration) WithResourceVersion(value string) *MiddlewareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *MiddlewareApplyConfiguration) WithGeneration(value int64) *MiddlewareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *MiddlewareApplyConfiguration) WithCreationTimestamp(value metav1.Time) *MiddlewareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *MiddlewareApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *MiddlewareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *MiddlewareApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *MiddlewareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *MiddlewareApplyConfiguration) WithLabels(entries map[string]string) *MiddlewareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *MiddlewareApplyConfiguration) WithAnnotations(entries map[string]string) *MiddlewareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *MiddlewareApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *MiddlewareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *MiddlewareApplyConfiguration) WithFinalizers(values ...string) *MiddlewareApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *MiddlewareApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} + } +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spec field is set to the value of the last call. +func (b *MiddlewareApplyConfiguration) WithSpec(value *MiddlewareSpecApplyConfiguration) *MiddlewareApplyConfiguration { + b.Spec = value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *MiddlewareApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *MiddlewareApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *MiddlewareApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *MiddlewareApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/middlewareref.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/middlewareref.go new file mode 100644 index 000000000..393a71232 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/middlewareref.go @@ -0,0 +1,56 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// MiddlewareRefApplyConfiguration represents a declarative configuration of the MiddlewareRef type for use +// with apply. +type MiddlewareRefApplyConfiguration struct { + Name *string `json:"name,omitempty"` + Namespace *string `json:"namespace,omitempty"` +} + +// MiddlewareRefApplyConfiguration constructs a declarative configuration of the MiddlewareRef type for use with +// apply. +func MiddlewareRef() *MiddlewareRefApplyConfiguration { + return &MiddlewareRefApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *MiddlewareRefApplyConfiguration) WithName(value string) *MiddlewareRefApplyConfiguration { + b.Name = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *MiddlewareRefApplyConfiguration) WithNamespace(value string) *MiddlewareRefApplyConfiguration { + b.Namespace = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/middlewarespec.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/middlewarespec.go new file mode 100644 index 000000000..4e4a4c063 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/middlewarespec.go @@ -0,0 +1,274 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + dynamic "github.com/traefik/traefik/v3/pkg/config/dynamic" + v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" +) + +// MiddlewareSpecApplyConfiguration represents a declarative configuration of the MiddlewareSpec type for use +// with apply. +type MiddlewareSpecApplyConfiguration struct { + AddPrefix *dynamic.AddPrefix `json:"addPrefix,omitempty"` + StripPrefix *dynamic.StripPrefix `json:"stripPrefix,omitempty"` + StripPrefixRegex *dynamic.StripPrefixRegex `json:"stripPrefixRegex,omitempty"` + ReplacePath *dynamic.ReplacePath `json:"replacePath,omitempty"` + ReplacePathRegex *dynamic.ReplacePathRegex `json:"replacePathRegex,omitempty"` + Chain *ChainApplyConfiguration `json:"chain,omitempty"` + IPWhiteList *dynamic.IPWhiteList `json:"ipWhiteList,omitempty"` + IPAllowList *dynamic.IPAllowList `json:"ipAllowList,omitempty"` + Headers *dynamic.Headers `json:"headers,omitempty"` + Errors *ErrorPageApplyConfiguration `json:"errors,omitempty"` + RateLimit *RateLimitApplyConfiguration `json:"rateLimit,omitempty"` + RedirectRegex *dynamic.RedirectRegex `json:"redirectRegex,omitempty"` + RedirectScheme *dynamic.RedirectScheme `json:"redirectScheme,omitempty"` + BasicAuth *BasicAuthApplyConfiguration `json:"basicAuth,omitempty"` + DigestAuth *DigestAuthApplyConfiguration `json:"digestAuth,omitempty"` + ForwardAuth *ForwardAuthApplyConfiguration `json:"forwardAuth,omitempty"` + InFlightReq *dynamic.InFlightReq `json:"inFlightReq,omitempty"` + Buffering *dynamic.Buffering `json:"buffering,omitempty"` + CircuitBreaker *CircuitBreakerApplyConfiguration `json:"circuitBreaker,omitempty"` + Compress *CompressApplyConfiguration `json:"compress,omitempty"` + PassTLSClientCert *dynamic.PassTLSClientCert `json:"passTLSClientCert,omitempty"` + Retry *RetryApplyConfiguration `json:"retry,omitempty"` + ContentType *dynamic.ContentType `json:"contentType,omitempty"` + GrpcWeb *dynamic.GrpcWeb `json:"grpcWeb,omitempty"` + Plugin map[string]v1.JSON `json:"plugin,omitempty"` +} + +// MiddlewareSpecApplyConfiguration constructs a declarative configuration of the MiddlewareSpec type for use with +// apply. +func MiddlewareSpec() *MiddlewareSpecApplyConfiguration { + return &MiddlewareSpecApplyConfiguration{} +} + +// WithAddPrefix sets the AddPrefix field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the AddPrefix field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithAddPrefix(value dynamic.AddPrefix) *MiddlewareSpecApplyConfiguration { + b.AddPrefix = &value + return b +} + +// WithStripPrefix sets the StripPrefix field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the StripPrefix field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithStripPrefix(value dynamic.StripPrefix) *MiddlewareSpecApplyConfiguration { + b.StripPrefix = &value + return b +} + +// WithStripPrefixRegex sets the StripPrefixRegex field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the StripPrefixRegex field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithStripPrefixRegex(value dynamic.StripPrefixRegex) *MiddlewareSpecApplyConfiguration { + b.StripPrefixRegex = &value + return b +} + +// WithReplacePath sets the ReplacePath field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ReplacePath field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithReplacePath(value dynamic.ReplacePath) *MiddlewareSpecApplyConfiguration { + b.ReplacePath = &value + return b +} + +// WithReplacePathRegex sets the ReplacePathRegex field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ReplacePathRegex field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithReplacePathRegex(value dynamic.ReplacePathRegex) *MiddlewareSpecApplyConfiguration { + b.ReplacePathRegex = &value + return b +} + +// WithChain sets the Chain field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Chain field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithChain(value *ChainApplyConfiguration) *MiddlewareSpecApplyConfiguration { + b.Chain = value + return b +} + +// WithIPWhiteList sets the IPWhiteList field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the IPWhiteList field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithIPWhiteList(value dynamic.IPWhiteList) *MiddlewareSpecApplyConfiguration { + b.IPWhiteList = &value + return b +} + +// WithIPAllowList sets the IPAllowList field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the IPAllowList field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithIPAllowList(value dynamic.IPAllowList) *MiddlewareSpecApplyConfiguration { + b.IPAllowList = &value + return b +} + +// WithHeaders sets the Headers field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Headers field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithHeaders(value dynamic.Headers) *MiddlewareSpecApplyConfiguration { + b.Headers = &value + return b +} + +// WithErrors sets the Errors field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Errors field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithErrors(value *ErrorPageApplyConfiguration) *MiddlewareSpecApplyConfiguration { + b.Errors = value + return b +} + +// WithRateLimit sets the RateLimit field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the RateLimit field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithRateLimit(value *RateLimitApplyConfiguration) *MiddlewareSpecApplyConfiguration { + b.RateLimit = value + return b +} + +// WithRedirectRegex sets the RedirectRegex field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the RedirectRegex field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithRedirectRegex(value dynamic.RedirectRegex) *MiddlewareSpecApplyConfiguration { + b.RedirectRegex = &value + return b +} + +// WithRedirectScheme sets the RedirectScheme field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the RedirectScheme field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithRedirectScheme(value dynamic.RedirectScheme) *MiddlewareSpecApplyConfiguration { + b.RedirectScheme = &value + return b +} + +// WithBasicAuth sets the BasicAuth field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the BasicAuth field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithBasicAuth(value *BasicAuthApplyConfiguration) *MiddlewareSpecApplyConfiguration { + b.BasicAuth = value + return b +} + +// WithDigestAuth sets the DigestAuth field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DigestAuth field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithDigestAuth(value *DigestAuthApplyConfiguration) *MiddlewareSpecApplyConfiguration { + b.DigestAuth = value + return b +} + +// WithForwardAuth sets the ForwardAuth field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ForwardAuth field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithForwardAuth(value *ForwardAuthApplyConfiguration) *MiddlewareSpecApplyConfiguration { + b.ForwardAuth = value + return b +} + +// WithInFlightReq sets the InFlightReq field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the InFlightReq field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithInFlightReq(value dynamic.InFlightReq) *MiddlewareSpecApplyConfiguration { + b.InFlightReq = &value + return b +} + +// WithBuffering sets the Buffering field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Buffering field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithBuffering(value dynamic.Buffering) *MiddlewareSpecApplyConfiguration { + b.Buffering = &value + return b +} + +// WithCircuitBreaker sets the CircuitBreaker field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CircuitBreaker field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithCircuitBreaker(value *CircuitBreakerApplyConfiguration) *MiddlewareSpecApplyConfiguration { + b.CircuitBreaker = value + return b +} + +// WithCompress sets the Compress field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Compress field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithCompress(value *CompressApplyConfiguration) *MiddlewareSpecApplyConfiguration { + b.Compress = value + return b +} + +// WithPassTLSClientCert sets the PassTLSClientCert field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PassTLSClientCert field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithPassTLSClientCert(value dynamic.PassTLSClientCert) *MiddlewareSpecApplyConfiguration { + b.PassTLSClientCert = &value + return b +} + +// WithRetry sets the Retry field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Retry field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithRetry(value *RetryApplyConfiguration) *MiddlewareSpecApplyConfiguration { + b.Retry = value + return b +} + +// WithContentType sets the ContentType field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ContentType field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithContentType(value dynamic.ContentType) *MiddlewareSpecApplyConfiguration { + b.ContentType = &value + return b +} + +// WithGrpcWeb sets the GrpcWeb field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GrpcWeb field is set to the value of the last call. +func (b *MiddlewareSpecApplyConfiguration) WithGrpcWeb(value dynamic.GrpcWeb) *MiddlewareSpecApplyConfiguration { + b.GrpcWeb = &value + return b +} + +// WithPlugin puts the entries into the Plugin field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Plugin field, +// overwriting an existing map entries in Plugin field with the same key. +func (b *MiddlewareSpecApplyConfiguration) WithPlugin(entries map[string]v1.JSON) *MiddlewareSpecApplyConfiguration { + if b.Plugin == nil && len(entries) > 0 { + b.Plugin = make(map[string]v1.JSON, len(entries)) + } + for k, v := range entries { + b.Plugin[k] = v + } + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/middlewaretcp.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/middlewaretcp.go new file mode 100644 index 000000000..7a8b92f55 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/middlewaretcp.go @@ -0,0 +1,241 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// MiddlewareTCPApplyConfiguration represents a declarative configuration of the MiddlewareTCP type for use +// with apply. +type MiddlewareTCPApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *MiddlewareTCPSpecApplyConfiguration `json:"spec,omitempty"` +} + +// MiddlewareTCP constructs a declarative configuration of the MiddlewareTCP type for use with +// apply. +func MiddlewareTCP(name, namespace string) *MiddlewareTCPApplyConfiguration { + b := &MiddlewareTCPApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("MiddlewareTCP") + b.WithAPIVersion("traefik.io/v1alpha1") + return b +} +func (b MiddlewareTCPApplyConfiguration) IsApplyConfiguration() {} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *MiddlewareTCPApplyConfiguration) WithKind(value string) *MiddlewareTCPApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *MiddlewareTCPApplyConfiguration) WithAPIVersion(value string) *MiddlewareTCPApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *MiddlewareTCPApplyConfiguration) WithName(value string) *MiddlewareTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *MiddlewareTCPApplyConfiguration) WithGenerateName(value string) *MiddlewareTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *MiddlewareTCPApplyConfiguration) WithNamespace(value string) *MiddlewareTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *MiddlewareTCPApplyConfiguration) WithUID(value types.UID) *MiddlewareTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *MiddlewareTCPApplyConfiguration) WithResourceVersion(value string) *MiddlewareTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *MiddlewareTCPApplyConfiguration) WithGeneration(value int64) *MiddlewareTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *MiddlewareTCPApplyConfiguration) WithCreationTimestamp(value metav1.Time) *MiddlewareTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *MiddlewareTCPApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *MiddlewareTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *MiddlewareTCPApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *MiddlewareTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *MiddlewareTCPApplyConfiguration) WithLabels(entries map[string]string) *MiddlewareTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *MiddlewareTCPApplyConfiguration) WithAnnotations(entries map[string]string) *MiddlewareTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *MiddlewareTCPApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *MiddlewareTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *MiddlewareTCPApplyConfiguration) WithFinalizers(values ...string) *MiddlewareTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *MiddlewareTCPApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} + } +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spec field is set to the value of the last call. +func (b *MiddlewareTCPApplyConfiguration) WithSpec(value *MiddlewareTCPSpecApplyConfiguration) *MiddlewareTCPApplyConfiguration { + b.Spec = value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *MiddlewareTCPApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *MiddlewareTCPApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *MiddlewareTCPApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *MiddlewareTCPApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/middlewaretcpspec.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/middlewaretcpspec.go new file mode 100644 index 000000000..81e751c57 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/middlewaretcpspec.go @@ -0,0 +1,69 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + dynamic "github.com/traefik/traefik/v3/pkg/config/dynamic" +) + +// MiddlewareTCPSpecApplyConfiguration represents a declarative configuration of the MiddlewareTCPSpec type for use +// with apply. +type MiddlewareTCPSpecApplyConfiguration struct { + InFlightConn *dynamic.TCPInFlightConn `json:"inFlightConn,omitempty"` + IPWhiteList *dynamic.TCPIPWhiteList `json:"ipWhiteList,omitempty"` + IPAllowList *dynamic.TCPIPAllowList `json:"ipAllowList,omitempty"` +} + +// MiddlewareTCPSpecApplyConfiguration constructs a declarative configuration of the MiddlewareTCPSpec type for use with +// apply. +func MiddlewareTCPSpec() *MiddlewareTCPSpecApplyConfiguration { + return &MiddlewareTCPSpecApplyConfiguration{} +} + +// WithInFlightConn sets the InFlightConn field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the InFlightConn field is set to the value of the last call. +func (b *MiddlewareTCPSpecApplyConfiguration) WithInFlightConn(value dynamic.TCPInFlightConn) *MiddlewareTCPSpecApplyConfiguration { + b.InFlightConn = &value + return b +} + +// WithIPWhiteList sets the IPWhiteList field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the IPWhiteList field is set to the value of the last call. +func (b *MiddlewareTCPSpecApplyConfiguration) WithIPWhiteList(value dynamic.TCPIPWhiteList) *MiddlewareTCPSpecApplyConfiguration { + b.IPWhiteList = &value + return b +} + +// WithIPAllowList sets the IPAllowList field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the IPAllowList field is set to the value of the last call. +func (b *MiddlewareTCPSpecApplyConfiguration) WithIPAllowList(value dynamic.TCPIPAllowList) *MiddlewareTCPSpecApplyConfiguration { + b.IPAllowList = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/mirroring.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/mirroring.go new file mode 100644 index 000000000..7140f1c37 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/mirroring.go @@ -0,0 +1,196 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + dynamic "github.com/traefik/traefik/v3/pkg/config/dynamic" + intstr "k8s.io/apimachinery/pkg/util/intstr" +) + +// MirroringApplyConfiguration represents a declarative configuration of the Mirroring type for use +// with apply. +type MirroringApplyConfiguration struct { + LoadBalancerSpecApplyConfiguration `json:",inline"` + MirrorBody *bool `json:"mirrorBody,omitempty"` + MaxBodySize *int64 `json:"maxBodySize,omitempty"` + Mirrors []MirrorServiceApplyConfiguration `json:"mirrors,omitempty"` +} + +// MirroringApplyConfiguration constructs a declarative configuration of the Mirroring type for use with +// apply. +func Mirroring() *MirroringApplyConfiguration { + return &MirroringApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *MirroringApplyConfiguration) WithName(value string) *MirroringApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Name = &value + return b +} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *MirroringApplyConfiguration) WithKind(value string) *MirroringApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Kind = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *MirroringApplyConfiguration) WithNamespace(value string) *MirroringApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Namespace = &value + return b +} + +// WithSticky sets the Sticky field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Sticky field is set to the value of the last call. +func (b *MirroringApplyConfiguration) WithSticky(value dynamic.Sticky) *MirroringApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Sticky = &value + return b +} + +// WithPort sets the Port field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Port field is set to the value of the last call. +func (b *MirroringApplyConfiguration) WithPort(value intstr.IntOrString) *MirroringApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Port = &value + return b +} + +// WithScheme sets the Scheme field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Scheme field is set to the value of the last call. +func (b *MirroringApplyConfiguration) WithScheme(value string) *MirroringApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Scheme = &value + return b +} + +// WithStrategy sets the Strategy field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Strategy field is set to the value of the last call. +func (b *MirroringApplyConfiguration) WithStrategy(value dynamic.BalancerStrategy) *MirroringApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Strategy = &value + return b +} + +// WithPassHostHeader sets the PassHostHeader field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PassHostHeader field is set to the value of the last call. +func (b *MirroringApplyConfiguration) WithPassHostHeader(value bool) *MirroringApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.PassHostHeader = &value + return b +} + +// WithResponseForwarding sets the ResponseForwarding field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResponseForwarding field is set to the value of the last call. +func (b *MirroringApplyConfiguration) WithResponseForwarding(value *ResponseForwardingApplyConfiguration) *MirroringApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.ResponseForwarding = value + return b +} + +// WithServersTransport sets the ServersTransport field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ServersTransport field is set to the value of the last call. +func (b *MirroringApplyConfiguration) WithServersTransport(value string) *MirroringApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.ServersTransport = &value + return b +} + +// WithWeight sets the Weight field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Weight field is set to the value of the last call. +func (b *MirroringApplyConfiguration) WithWeight(value int) *MirroringApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Weight = &value + return b +} + +// WithNativeLB sets the NativeLB field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the NativeLB field is set to the value of the last call. +func (b *MirroringApplyConfiguration) WithNativeLB(value bool) *MirroringApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.NativeLB = &value + return b +} + +// WithNodePortLB sets the NodePortLB field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the NodePortLB field is set to the value of the last call. +func (b *MirroringApplyConfiguration) WithNodePortLB(value bool) *MirroringApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.NodePortLB = &value + return b +} + +// WithHealthCheck sets the HealthCheck field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the HealthCheck field is set to the value of the last call. +func (b *MirroringApplyConfiguration) WithHealthCheck(value *ServerHealthCheckApplyConfiguration) *MirroringApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.HealthCheck = value + return b +} + +// WithPassiveHealthCheck sets the PassiveHealthCheck field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PassiveHealthCheck field is set to the value of the last call. +func (b *MirroringApplyConfiguration) WithPassiveHealthCheck(value *PassiveServerHealthCheckApplyConfiguration) *MirroringApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.PassiveHealthCheck = value + return b +} + +// WithMirrorBody sets the MirrorBody field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the MirrorBody field is set to the value of the last call. +func (b *MirroringApplyConfiguration) WithMirrorBody(value bool) *MirroringApplyConfiguration { + b.MirrorBody = &value + return b +} + +// WithMaxBodySize sets the MaxBodySize field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the MaxBodySize field is set to the value of the last call. +func (b *MirroringApplyConfiguration) WithMaxBodySize(value int64) *MirroringApplyConfiguration { + b.MaxBodySize = &value + return b +} + +// WithMirrors adds the given value to the Mirrors field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Mirrors field. +func (b *MirroringApplyConfiguration) WithMirrors(values ...*MirrorServiceApplyConfiguration) *MirroringApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithMirrors") + } + b.Mirrors = append(b.Mirrors, *values[i]) + } + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/mirrorservice.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/mirrorservice.go new file mode 100644 index 000000000..f7fde8d3d --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/mirrorservice.go @@ -0,0 +1,173 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + dynamic "github.com/traefik/traefik/v3/pkg/config/dynamic" + intstr "k8s.io/apimachinery/pkg/util/intstr" +) + +// MirrorServiceApplyConfiguration represents a declarative configuration of the MirrorService type for use +// with apply. +type MirrorServiceApplyConfiguration struct { + LoadBalancerSpecApplyConfiguration `json:",inline"` + Percent *int `json:"percent,omitempty"` +} + +// MirrorServiceApplyConfiguration constructs a declarative configuration of the MirrorService type for use with +// apply. +func MirrorService() *MirrorServiceApplyConfiguration { + return &MirrorServiceApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *MirrorServiceApplyConfiguration) WithName(value string) *MirrorServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Name = &value + return b +} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *MirrorServiceApplyConfiguration) WithKind(value string) *MirrorServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Kind = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *MirrorServiceApplyConfiguration) WithNamespace(value string) *MirrorServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Namespace = &value + return b +} + +// WithSticky sets the Sticky field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Sticky field is set to the value of the last call. +func (b *MirrorServiceApplyConfiguration) WithSticky(value dynamic.Sticky) *MirrorServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Sticky = &value + return b +} + +// WithPort sets the Port field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Port field is set to the value of the last call. +func (b *MirrorServiceApplyConfiguration) WithPort(value intstr.IntOrString) *MirrorServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Port = &value + return b +} + +// WithScheme sets the Scheme field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Scheme field is set to the value of the last call. +func (b *MirrorServiceApplyConfiguration) WithScheme(value string) *MirrorServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Scheme = &value + return b +} + +// WithStrategy sets the Strategy field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Strategy field is set to the value of the last call. +func (b *MirrorServiceApplyConfiguration) WithStrategy(value dynamic.BalancerStrategy) *MirrorServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Strategy = &value + return b +} + +// WithPassHostHeader sets the PassHostHeader field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PassHostHeader field is set to the value of the last call. +func (b *MirrorServiceApplyConfiguration) WithPassHostHeader(value bool) *MirrorServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.PassHostHeader = &value + return b +} + +// WithResponseForwarding sets the ResponseForwarding field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResponseForwarding field is set to the value of the last call. +func (b *MirrorServiceApplyConfiguration) WithResponseForwarding(value *ResponseForwardingApplyConfiguration) *MirrorServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.ResponseForwarding = value + return b +} + +// WithServersTransport sets the ServersTransport field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ServersTransport field is set to the value of the last call. +func (b *MirrorServiceApplyConfiguration) WithServersTransport(value string) *MirrorServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.ServersTransport = &value + return b +} + +// WithWeight sets the Weight field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Weight field is set to the value of the last call. +func (b *MirrorServiceApplyConfiguration) WithWeight(value int) *MirrorServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Weight = &value + return b +} + +// WithNativeLB sets the NativeLB field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the NativeLB field is set to the value of the last call. +func (b *MirrorServiceApplyConfiguration) WithNativeLB(value bool) *MirrorServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.NativeLB = &value + return b +} + +// WithNodePortLB sets the NodePortLB field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the NodePortLB field is set to the value of the last call. +func (b *MirrorServiceApplyConfiguration) WithNodePortLB(value bool) *MirrorServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.NodePortLB = &value + return b +} + +// WithHealthCheck sets the HealthCheck field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the HealthCheck field is set to the value of the last call. +func (b *MirrorServiceApplyConfiguration) WithHealthCheck(value *ServerHealthCheckApplyConfiguration) *MirrorServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.HealthCheck = value + return b +} + +// WithPassiveHealthCheck sets the PassiveHealthCheck field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PassiveHealthCheck field is set to the value of the last call. +func (b *MirrorServiceApplyConfiguration) WithPassiveHealthCheck(value *PassiveServerHealthCheckApplyConfiguration) *MirrorServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.PassiveHealthCheck = value + return b +} + +// WithPercent sets the Percent field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Percent field is set to the value of the last call. +func (b *MirrorServiceApplyConfiguration) WithPercent(value int) *MirrorServiceApplyConfiguration { + b.Percent = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/objectreference.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/objectreference.go new file mode 100644 index 000000000..bf48afb3e --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/objectreference.go @@ -0,0 +1,56 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// ObjectReferenceApplyConfiguration represents a declarative configuration of the ObjectReference type for use +// with apply. +type ObjectReferenceApplyConfiguration struct { + Name *string `json:"name,omitempty"` + Namespace *string `json:"namespace,omitempty"` +} + +// ObjectReferenceApplyConfiguration constructs a declarative configuration of the ObjectReference type for use with +// apply. +func ObjectReference() *ObjectReferenceApplyConfiguration { + return &ObjectReferenceApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *ObjectReferenceApplyConfiguration) WithName(value string) *ObjectReferenceApplyConfiguration { + b.Name = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *ObjectReferenceApplyConfiguration) WithNamespace(value string) *ObjectReferenceApplyConfiguration { + b.Namespace = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/passiveserverhealthcheck.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/passiveserverhealthcheck.go new file mode 100644 index 000000000..f584e214c --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/passiveserverhealthcheck.go @@ -0,0 +1,60 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + intstr "k8s.io/apimachinery/pkg/util/intstr" +) + +// PassiveServerHealthCheckApplyConfiguration represents a declarative configuration of the PassiveServerHealthCheck type for use +// with apply. +type PassiveServerHealthCheckApplyConfiguration struct { + FailureWindow *intstr.IntOrString `json:"failureWindow,omitempty"` + MaxFailedAttempts *int `json:"maxFailedAttempts,omitempty"` +} + +// PassiveServerHealthCheckApplyConfiguration constructs a declarative configuration of the PassiveServerHealthCheck type for use with +// apply. +func PassiveServerHealthCheck() *PassiveServerHealthCheckApplyConfiguration { + return &PassiveServerHealthCheckApplyConfiguration{} +} + +// WithFailureWindow sets the FailureWindow field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the FailureWindow field is set to the value of the last call. +func (b *PassiveServerHealthCheckApplyConfiguration) WithFailureWindow(value intstr.IntOrString) *PassiveServerHealthCheckApplyConfiguration { + b.FailureWindow = &value + return b +} + +// WithMaxFailedAttempts sets the MaxFailedAttempts field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the MaxFailedAttempts field is set to the value of the last call. +func (b *PassiveServerHealthCheckApplyConfiguration) WithMaxFailedAttempts(value int) *PassiveServerHealthCheckApplyConfiguration { + b.MaxFailedAttempts = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ratelimit.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ratelimit.go new file mode 100644 index 000000000..8af058d67 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/ratelimit.go @@ -0,0 +1,88 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + dynamic "github.com/traefik/traefik/v3/pkg/config/dynamic" + intstr "k8s.io/apimachinery/pkg/util/intstr" +) + +// RateLimitApplyConfiguration represents a declarative configuration of the RateLimit type for use +// with apply. +type RateLimitApplyConfiguration struct { + Average *int64 `json:"average,omitempty"` + Period *intstr.IntOrString `json:"period,omitempty"` + Burst *int64 `json:"burst,omitempty"` + SourceCriterion *dynamic.SourceCriterion `json:"sourceCriterion,omitempty"` + Redis *RedisApplyConfiguration `json:"redis,omitempty"` +} + +// RateLimitApplyConfiguration constructs a declarative configuration of the RateLimit type for use with +// apply. +func RateLimit() *RateLimitApplyConfiguration { + return &RateLimitApplyConfiguration{} +} + +// WithAverage sets the Average field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Average field is set to the value of the last call. +func (b *RateLimitApplyConfiguration) WithAverage(value int64) *RateLimitApplyConfiguration { + b.Average = &value + return b +} + +// WithPeriod sets the Period field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Period field is set to the value of the last call. +func (b *RateLimitApplyConfiguration) WithPeriod(value intstr.IntOrString) *RateLimitApplyConfiguration { + b.Period = &value + return b +} + +// WithBurst sets the Burst field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Burst field is set to the value of the last call. +func (b *RateLimitApplyConfiguration) WithBurst(value int64) *RateLimitApplyConfiguration { + b.Burst = &value + return b +} + +// WithSourceCriterion sets the SourceCriterion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SourceCriterion field is set to the value of the last call. +func (b *RateLimitApplyConfiguration) WithSourceCriterion(value dynamic.SourceCriterion) *RateLimitApplyConfiguration { + b.SourceCriterion = &value + return b +} + +// WithRedis sets the Redis field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Redis field is set to the value of the last call. +func (b *RateLimitApplyConfiguration) WithRedis(value *RedisApplyConfiguration) *RateLimitApplyConfiguration { + b.Redis = value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/redis.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/redis.go new file mode 100644 index 000000000..f402ac375 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/redis.go @@ -0,0 +1,134 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + intstr "k8s.io/apimachinery/pkg/util/intstr" +) + +// RedisApplyConfiguration represents a declarative configuration of the Redis type for use +// with apply. +type RedisApplyConfiguration struct { + Endpoints []string `json:"endpoints,omitempty"` + TLS *ClientTLSApplyConfiguration `json:"tls,omitempty"` + Secret *string `json:"secret,omitempty"` + DB *int `json:"db,omitempty"` + PoolSize *int `json:"poolSize,omitempty"` + MinIdleConns *int `json:"minIdleConns,omitempty"` + MaxActiveConns *int `json:"maxActiveConns,omitempty"` + ReadTimeout *intstr.IntOrString `json:"readTimeout,omitempty"` + WriteTimeout *intstr.IntOrString `json:"writeTimeout,omitempty"` + DialTimeout *intstr.IntOrString `json:"dialTimeout,omitempty"` +} + +// RedisApplyConfiguration constructs a declarative configuration of the Redis type for use with +// apply. +func Redis() *RedisApplyConfiguration { + return &RedisApplyConfiguration{} +} + +// WithEndpoints adds the given value to the Endpoints field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Endpoints field. +func (b *RedisApplyConfiguration) WithEndpoints(values ...string) *RedisApplyConfiguration { + for i := range values { + b.Endpoints = append(b.Endpoints, values[i]) + } + return b +} + +// WithTLS sets the TLS field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the TLS field is set to the value of the last call. +func (b *RedisApplyConfiguration) WithTLS(value *ClientTLSApplyConfiguration) *RedisApplyConfiguration { + b.TLS = value + return b +} + +// WithSecret sets the Secret field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Secret field is set to the value of the last call. +func (b *RedisApplyConfiguration) WithSecret(value string) *RedisApplyConfiguration { + b.Secret = &value + return b +} + +// WithDB sets the DB field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DB field is set to the value of the last call. +func (b *RedisApplyConfiguration) WithDB(value int) *RedisApplyConfiguration { + b.DB = &value + return b +} + +// WithPoolSize sets the PoolSize field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PoolSize field is set to the value of the last call. +func (b *RedisApplyConfiguration) WithPoolSize(value int) *RedisApplyConfiguration { + b.PoolSize = &value + return b +} + +// WithMinIdleConns sets the MinIdleConns field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the MinIdleConns field is set to the value of the last call. +func (b *RedisApplyConfiguration) WithMinIdleConns(value int) *RedisApplyConfiguration { + b.MinIdleConns = &value + return b +} + +// WithMaxActiveConns sets the MaxActiveConns field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the MaxActiveConns field is set to the value of the last call. +func (b *RedisApplyConfiguration) WithMaxActiveConns(value int) *RedisApplyConfiguration { + b.MaxActiveConns = &value + return b +} + +// WithReadTimeout sets the ReadTimeout field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ReadTimeout field is set to the value of the last call. +func (b *RedisApplyConfiguration) WithReadTimeout(value intstr.IntOrString) *RedisApplyConfiguration { + b.ReadTimeout = &value + return b +} + +// WithWriteTimeout sets the WriteTimeout field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the WriteTimeout field is set to the value of the last call. +func (b *RedisApplyConfiguration) WithWriteTimeout(value intstr.IntOrString) *RedisApplyConfiguration { + b.WriteTimeout = &value + return b +} + +// WithDialTimeout sets the DialTimeout field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DialTimeout field is set to the value of the last call. +func (b *RedisApplyConfiguration) WithDialTimeout(value intstr.IntOrString) *RedisApplyConfiguration { + b.DialTimeout = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/responseforwarding.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/responseforwarding.go new file mode 100644 index 000000000..73b5936f6 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/responseforwarding.go @@ -0,0 +1,47 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// ResponseForwardingApplyConfiguration represents a declarative configuration of the ResponseForwarding type for use +// with apply. +type ResponseForwardingApplyConfiguration struct { + FlushInterval *string `json:"flushInterval,omitempty"` +} + +// ResponseForwardingApplyConfiguration constructs a declarative configuration of the ResponseForwarding type for use with +// apply. +func ResponseForwarding() *ResponseForwardingApplyConfiguration { + return &ResponseForwardingApplyConfiguration{} +} + +// WithFlushInterval sets the FlushInterval field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the FlushInterval field is set to the value of the last call. +func (b *ResponseForwardingApplyConfiguration) WithFlushInterval(value string) *ResponseForwardingApplyConfiguration { + b.FlushInterval = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/retry.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/retry.go new file mode 100644 index 000000000..93c06a0f9 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/retry.go @@ -0,0 +1,60 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + intstr "k8s.io/apimachinery/pkg/util/intstr" +) + +// RetryApplyConfiguration represents a declarative configuration of the Retry type for use +// with apply. +type RetryApplyConfiguration struct { + Attempts *int `json:"attempts,omitempty"` + InitialInterval *intstr.IntOrString `json:"initialInterval,omitempty"` +} + +// RetryApplyConfiguration constructs a declarative configuration of the Retry type for use with +// apply. +func Retry() *RetryApplyConfiguration { + return &RetryApplyConfiguration{} +} + +// WithAttempts sets the Attempts field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Attempts field is set to the value of the last call. +func (b *RetryApplyConfiguration) WithAttempts(value int) *RetryApplyConfiguration { + b.Attempts = &value + return b +} + +// WithInitialInterval sets the InitialInterval field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the InitialInterval field is set to the value of the last call. +func (b *RetryApplyConfiguration) WithInitialInterval(value intstr.IntOrString) *RetryApplyConfiguration { + b.InitialInterval = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/rootca.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/rootca.go new file mode 100644 index 000000000..04df7315a --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/rootca.go @@ -0,0 +1,56 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// RootCAApplyConfiguration represents a declarative configuration of the RootCA type for use +// with apply. +type RootCAApplyConfiguration struct { + Secret *string `json:"secret,omitempty"` + ConfigMap *string `json:"configMap,omitempty"` +} + +// RootCAApplyConfiguration constructs a declarative configuration of the RootCA type for use with +// apply. +func RootCA() *RootCAApplyConfiguration { + return &RootCAApplyConfiguration{} +} + +// WithSecret sets the Secret field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Secret field is set to the value of the last call. +func (b *RootCAApplyConfiguration) WithSecret(value string) *RootCAApplyConfiguration { + b.Secret = &value + return b +} + +// WithConfigMap sets the ConfigMap field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ConfigMap field is set to the value of the last call. +func (b *RootCAApplyConfiguration) WithConfigMap(value string) *RootCAApplyConfiguration { + b.ConfigMap = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/route.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/route.go new file mode 100644 index 000000000..ef9703108 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/route.go @@ -0,0 +1,115 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + dynamic "github.com/traefik/traefik/v3/pkg/config/dynamic" +) + +// RouteApplyConfiguration represents a declarative configuration of the Route type for use +// with apply. +type RouteApplyConfiguration struct { + Match *string `json:"match,omitempty"` + Kind *string `json:"kind,omitempty"` + Priority *int `json:"priority,omitempty"` + Syntax *string `json:"syntax,omitempty"` + Services []ServiceApplyConfiguration `json:"services,omitempty"` + Middlewares []MiddlewareRefApplyConfiguration `json:"middlewares,omitempty"` + Observability *dynamic.RouterObservabilityConfig `json:"observability,omitempty"` +} + +// RouteApplyConfiguration constructs a declarative configuration of the Route type for use with +// apply. +func Route() *RouteApplyConfiguration { + return &RouteApplyConfiguration{} +} + +// WithMatch sets the Match field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Match field is set to the value of the last call. +func (b *RouteApplyConfiguration) WithMatch(value string) *RouteApplyConfiguration { + b.Match = &value + return b +} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *RouteApplyConfiguration) WithKind(value string) *RouteApplyConfiguration { + b.Kind = &value + return b +} + +// WithPriority sets the Priority field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Priority field is set to the value of the last call. +func (b *RouteApplyConfiguration) WithPriority(value int) *RouteApplyConfiguration { + b.Priority = &value + return b +} + +// WithSyntax sets the Syntax field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Syntax field is set to the value of the last call. +func (b *RouteApplyConfiguration) WithSyntax(value string) *RouteApplyConfiguration { + b.Syntax = &value + return b +} + +// WithServices adds the given value to the Services field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Services field. +func (b *RouteApplyConfiguration) WithServices(values ...*ServiceApplyConfiguration) *RouteApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithServices") + } + b.Services = append(b.Services, *values[i]) + } + return b +} + +// WithMiddlewares adds the given value to the Middlewares field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Middlewares field. +func (b *RouteApplyConfiguration) WithMiddlewares(values ...*MiddlewareRefApplyConfiguration) *RouteApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithMiddlewares") + } + b.Middlewares = append(b.Middlewares, *values[i]) + } + return b +} + +// WithObservability sets the Observability field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Observability field is set to the value of the last call. +func (b *RouteApplyConfiguration) WithObservability(value dynamic.RouterObservabilityConfig) *RouteApplyConfiguration { + b.Observability = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/routetcp.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/routetcp.go new file mode 100644 index 000000000..a6e7c263e --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/routetcp.go @@ -0,0 +1,93 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// RouteTCPApplyConfiguration represents a declarative configuration of the RouteTCP type for use +// with apply. +type RouteTCPApplyConfiguration struct { + Match *string `json:"match,omitempty"` + Priority *int `json:"priority,omitempty"` + Syntax *string `json:"syntax,omitempty"` + Services []ServiceTCPApplyConfiguration `json:"services,omitempty"` + Middlewares []ObjectReferenceApplyConfiguration `json:"middlewares,omitempty"` +} + +// RouteTCPApplyConfiguration constructs a declarative configuration of the RouteTCP type for use with +// apply. +func RouteTCP() *RouteTCPApplyConfiguration { + return &RouteTCPApplyConfiguration{} +} + +// WithMatch sets the Match field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Match field is set to the value of the last call. +func (b *RouteTCPApplyConfiguration) WithMatch(value string) *RouteTCPApplyConfiguration { + b.Match = &value + return b +} + +// WithPriority sets the Priority field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Priority field is set to the value of the last call. +func (b *RouteTCPApplyConfiguration) WithPriority(value int) *RouteTCPApplyConfiguration { + b.Priority = &value + return b +} + +// WithSyntax sets the Syntax field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Syntax field is set to the value of the last call. +func (b *RouteTCPApplyConfiguration) WithSyntax(value string) *RouteTCPApplyConfiguration { + b.Syntax = &value + return b +} + +// WithServices adds the given value to the Services field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Services field. +func (b *RouteTCPApplyConfiguration) WithServices(values ...*ServiceTCPApplyConfiguration) *RouteTCPApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithServices") + } + b.Services = append(b.Services, *values[i]) + } + return b +} + +// WithMiddlewares adds the given value to the Middlewares field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Middlewares field. +func (b *RouteTCPApplyConfiguration) WithMiddlewares(values ...*ObjectReferenceApplyConfiguration) *RouteTCPApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithMiddlewares") + } + b.Middlewares = append(b.Middlewares, *values[i]) + } + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/routeudp.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/routeudp.go new file mode 100644 index 000000000..13a49e2ed --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/routeudp.go @@ -0,0 +1,52 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// RouteUDPApplyConfiguration represents a declarative configuration of the RouteUDP type for use +// with apply. +type RouteUDPApplyConfiguration struct { + Services []ServiceUDPApplyConfiguration `json:"services,omitempty"` +} + +// RouteUDPApplyConfiguration constructs a declarative configuration of the RouteUDP type for use with +// apply. +func RouteUDP() *RouteUDPApplyConfiguration { + return &RouteUDPApplyConfiguration{} +} + +// WithServices adds the given value to the Services field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Services field. +func (b *RouteUDPApplyConfiguration) WithServices(values ...*ServiceUDPApplyConfiguration) *RouteUDPApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithServices") + } + b.Services = append(b.Services, *values[i]) + } + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serverhealthcheck.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serverhealthcheck.go new file mode 100644 index 000000000..c39a2ac05 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serverhealthcheck.go @@ -0,0 +1,156 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + intstr "k8s.io/apimachinery/pkg/util/intstr" +) + +// ServerHealthCheckApplyConfiguration represents a declarative configuration of the ServerHealthCheck type for use +// with apply. +type ServerHealthCheckApplyConfiguration struct { + Scheme *string `json:"scheme,omitempty"` + Mode *string `json:"mode,omitempty"` + Path *string `json:"path,omitempty"` + Method *string `json:"method,omitempty"` + Status *int `json:"status,omitempty"` + Port *int `json:"port,omitempty"` + Interval *intstr.IntOrString `json:"interval,omitempty"` + UnhealthyInterval *intstr.IntOrString `json:"unhealthyInterval,omitempty"` + Timeout *intstr.IntOrString `json:"timeout,omitempty"` + Hostname *string `json:"hostname,omitempty"` + FollowRedirects *bool `json:"followRedirects,omitempty"` + Headers map[string]string `json:"headers,omitempty"` +} + +// ServerHealthCheckApplyConfiguration constructs a declarative configuration of the ServerHealthCheck type for use with +// apply. +func ServerHealthCheck() *ServerHealthCheckApplyConfiguration { + return &ServerHealthCheckApplyConfiguration{} +} + +// WithScheme sets the Scheme field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Scheme field is set to the value of the last call. +func (b *ServerHealthCheckApplyConfiguration) WithScheme(value string) *ServerHealthCheckApplyConfiguration { + b.Scheme = &value + return b +} + +// WithMode sets the Mode field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Mode field is set to the value of the last call. +func (b *ServerHealthCheckApplyConfiguration) WithMode(value string) *ServerHealthCheckApplyConfiguration { + b.Mode = &value + return b +} + +// WithPath sets the Path field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Path field is set to the value of the last call. +func (b *ServerHealthCheckApplyConfiguration) WithPath(value string) *ServerHealthCheckApplyConfiguration { + b.Path = &value + return b +} + +// WithMethod sets the Method field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Method field is set to the value of the last call. +func (b *ServerHealthCheckApplyConfiguration) WithMethod(value string) *ServerHealthCheckApplyConfiguration { + b.Method = &value + return b +} + +// WithStatus sets the Status field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Status field is set to the value of the last call. +func (b *ServerHealthCheckApplyConfiguration) WithStatus(value int) *ServerHealthCheckApplyConfiguration { + b.Status = &value + return b +} + +// WithPort sets the Port field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Port field is set to the value of the last call. +func (b *ServerHealthCheckApplyConfiguration) WithPort(value int) *ServerHealthCheckApplyConfiguration { + b.Port = &value + return b +} + +// WithInterval sets the Interval field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Interval field is set to the value of the last call. +func (b *ServerHealthCheckApplyConfiguration) WithInterval(value intstr.IntOrString) *ServerHealthCheckApplyConfiguration { + b.Interval = &value + return b +} + +// WithUnhealthyInterval sets the UnhealthyInterval field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UnhealthyInterval field is set to the value of the last call. +func (b *ServerHealthCheckApplyConfiguration) WithUnhealthyInterval(value intstr.IntOrString) *ServerHealthCheckApplyConfiguration { + b.UnhealthyInterval = &value + return b +} + +// WithTimeout sets the Timeout field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Timeout field is set to the value of the last call. +func (b *ServerHealthCheckApplyConfiguration) WithTimeout(value intstr.IntOrString) *ServerHealthCheckApplyConfiguration { + b.Timeout = &value + return b +} + +// WithHostname sets the Hostname field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Hostname field is set to the value of the last call. +func (b *ServerHealthCheckApplyConfiguration) WithHostname(value string) *ServerHealthCheckApplyConfiguration { + b.Hostname = &value + return b +} + +// WithFollowRedirects sets the FollowRedirects field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the FollowRedirects field is set to the value of the last call. +func (b *ServerHealthCheckApplyConfiguration) WithFollowRedirects(value bool) *ServerHealthCheckApplyConfiguration { + b.FollowRedirects = &value + return b +} + +// WithHeaders puts the entries into the Headers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Headers field, +// overwriting an existing map entries in Headers field with the same key. +func (b *ServerHealthCheckApplyConfiguration) WithHeaders(entries map[string]string) *ServerHealthCheckApplyConfiguration { + if b.Headers == nil && len(entries) > 0 { + b.Headers = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.Headers[k] = v + } + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serverstransport.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serverstransport.go new file mode 100644 index 000000000..95cd7848e --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serverstransport.go @@ -0,0 +1,241 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// ServersTransportApplyConfiguration represents a declarative configuration of the ServersTransport type for use +// with apply. +type ServersTransportApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *ServersTransportSpecApplyConfiguration `json:"spec,omitempty"` +} + +// ServersTransport constructs a declarative configuration of the ServersTransport type for use with +// apply. +func ServersTransport(name, namespace string) *ServersTransportApplyConfiguration { + b := &ServersTransportApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("ServersTransport") + b.WithAPIVersion("traefik.io/v1alpha1") + return b +} +func (b ServersTransportApplyConfiguration) IsApplyConfiguration() {} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *ServersTransportApplyConfiguration) WithKind(value string) *ServersTransportApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *ServersTransportApplyConfiguration) WithAPIVersion(value string) *ServersTransportApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *ServersTransportApplyConfiguration) WithName(value string) *ServersTransportApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *ServersTransportApplyConfiguration) WithGenerateName(value string) *ServersTransportApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *ServersTransportApplyConfiguration) WithNamespace(value string) *ServersTransportApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *ServersTransportApplyConfiguration) WithUID(value types.UID) *ServersTransportApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *ServersTransportApplyConfiguration) WithResourceVersion(value string) *ServersTransportApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *ServersTransportApplyConfiguration) WithGeneration(value int64) *ServersTransportApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *ServersTransportApplyConfiguration) WithCreationTimestamp(value metav1.Time) *ServersTransportApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *ServersTransportApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *ServersTransportApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *ServersTransportApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *ServersTransportApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *ServersTransportApplyConfiguration) WithLabels(entries map[string]string) *ServersTransportApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *ServersTransportApplyConfiguration) WithAnnotations(entries map[string]string) *ServersTransportApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *ServersTransportApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *ServersTransportApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *ServersTransportApplyConfiguration) WithFinalizers(values ...string) *ServersTransportApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *ServersTransportApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} + } +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spec field is set to the value of the last call. +func (b *ServersTransportApplyConfiguration) WithSpec(value *ServersTransportSpecApplyConfiguration) *ServersTransportApplyConfiguration { + b.Spec = value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *ServersTransportApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *ServersTransportApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *ServersTransportApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *ServersTransportApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serverstransportspec.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serverstransportspec.go new file mode 100644 index 000000000..a2507cdb2 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serverstransportspec.go @@ -0,0 +1,141 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + dynamic "github.com/traefik/traefik/v3/pkg/config/dynamic" +) + +// ServersTransportSpecApplyConfiguration represents a declarative configuration of the ServersTransportSpec type for use +// with apply. +type ServersTransportSpecApplyConfiguration struct { + ServerName *string `json:"serverName,omitempty"` + InsecureSkipVerify *bool `json:"insecureSkipVerify,omitempty"` + RootCAs []RootCAApplyConfiguration `json:"rootCAs,omitempty"` + RootCAsSecrets []string `json:"rootCAsSecrets,omitempty"` + CertificatesSecrets []string `json:"certificatesSecrets,omitempty"` + MaxIdleConnsPerHost *int `json:"maxIdleConnsPerHost,omitempty"` + ForwardingTimeouts *ForwardingTimeoutsApplyConfiguration `json:"forwardingTimeouts,omitempty"` + DisableHTTP2 *bool `json:"disableHTTP2,omitempty"` + PeerCertURI *string `json:"peerCertURI,omitempty"` + Spiffe *dynamic.Spiffe `json:"spiffe,omitempty"` +} + +// ServersTransportSpecApplyConfiguration constructs a declarative configuration of the ServersTransportSpec type for use with +// apply. +func ServersTransportSpec() *ServersTransportSpecApplyConfiguration { + return &ServersTransportSpecApplyConfiguration{} +} + +// WithServerName sets the ServerName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ServerName field is set to the value of the last call. +func (b *ServersTransportSpecApplyConfiguration) WithServerName(value string) *ServersTransportSpecApplyConfiguration { + b.ServerName = &value + return b +} + +// WithInsecureSkipVerify sets the InsecureSkipVerify field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the InsecureSkipVerify field is set to the value of the last call. +func (b *ServersTransportSpecApplyConfiguration) WithInsecureSkipVerify(value bool) *ServersTransportSpecApplyConfiguration { + b.InsecureSkipVerify = &value + return b +} + +// WithRootCAs adds the given value to the RootCAs field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the RootCAs field. +func (b *ServersTransportSpecApplyConfiguration) WithRootCAs(values ...*RootCAApplyConfiguration) *ServersTransportSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithRootCAs") + } + b.RootCAs = append(b.RootCAs, *values[i]) + } + return b +} + +// WithRootCAsSecrets adds the given value to the RootCAsSecrets field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the RootCAsSecrets field. +func (b *ServersTransportSpecApplyConfiguration) WithRootCAsSecrets(values ...string) *ServersTransportSpecApplyConfiguration { + for i := range values { + b.RootCAsSecrets = append(b.RootCAsSecrets, values[i]) + } + return b +} + +// WithCertificatesSecrets adds the given value to the CertificatesSecrets field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the CertificatesSecrets field. +func (b *ServersTransportSpecApplyConfiguration) WithCertificatesSecrets(values ...string) *ServersTransportSpecApplyConfiguration { + for i := range values { + b.CertificatesSecrets = append(b.CertificatesSecrets, values[i]) + } + return b +} + +// WithMaxIdleConnsPerHost sets the MaxIdleConnsPerHost field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the MaxIdleConnsPerHost field is set to the value of the last call. +func (b *ServersTransportSpecApplyConfiguration) WithMaxIdleConnsPerHost(value int) *ServersTransportSpecApplyConfiguration { + b.MaxIdleConnsPerHost = &value + return b +} + +// WithForwardingTimeouts sets the ForwardingTimeouts field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ForwardingTimeouts field is set to the value of the last call. +func (b *ServersTransportSpecApplyConfiguration) WithForwardingTimeouts(value *ForwardingTimeoutsApplyConfiguration) *ServersTransportSpecApplyConfiguration { + b.ForwardingTimeouts = value + return b +} + +// WithDisableHTTP2 sets the DisableHTTP2 field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DisableHTTP2 field is set to the value of the last call. +func (b *ServersTransportSpecApplyConfiguration) WithDisableHTTP2(value bool) *ServersTransportSpecApplyConfiguration { + b.DisableHTTP2 = &value + return b +} + +// WithPeerCertURI sets the PeerCertURI field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PeerCertURI field is set to the value of the last call. +func (b *ServersTransportSpecApplyConfiguration) WithPeerCertURI(value string) *ServersTransportSpecApplyConfiguration { + b.PeerCertURI = &value + return b +} + +// WithSpiffe sets the Spiffe field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spiffe field is set to the value of the last call. +func (b *ServersTransportSpecApplyConfiguration) WithSpiffe(value dynamic.Spiffe) *ServersTransportSpecApplyConfiguration { + b.Spiffe = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serverstransporttcp.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serverstransporttcp.go new file mode 100644 index 000000000..46b9ac7a1 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serverstransporttcp.go @@ -0,0 +1,241 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// ServersTransportTCPApplyConfiguration represents a declarative configuration of the ServersTransportTCP type for use +// with apply. +type ServersTransportTCPApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *ServersTransportTCPSpecApplyConfiguration `json:"spec,omitempty"` +} + +// ServersTransportTCP constructs a declarative configuration of the ServersTransportTCP type for use with +// apply. +func ServersTransportTCP(name, namespace string) *ServersTransportTCPApplyConfiguration { + b := &ServersTransportTCPApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("ServersTransportTCP") + b.WithAPIVersion("traefik.io/v1alpha1") + return b +} +func (b ServersTransportTCPApplyConfiguration) IsApplyConfiguration() {} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *ServersTransportTCPApplyConfiguration) WithKind(value string) *ServersTransportTCPApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *ServersTransportTCPApplyConfiguration) WithAPIVersion(value string) *ServersTransportTCPApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *ServersTransportTCPApplyConfiguration) WithName(value string) *ServersTransportTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *ServersTransportTCPApplyConfiguration) WithGenerateName(value string) *ServersTransportTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *ServersTransportTCPApplyConfiguration) WithNamespace(value string) *ServersTransportTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *ServersTransportTCPApplyConfiguration) WithUID(value types.UID) *ServersTransportTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *ServersTransportTCPApplyConfiguration) WithResourceVersion(value string) *ServersTransportTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *ServersTransportTCPApplyConfiguration) WithGeneration(value int64) *ServersTransportTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *ServersTransportTCPApplyConfiguration) WithCreationTimestamp(value metav1.Time) *ServersTransportTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *ServersTransportTCPApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *ServersTransportTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *ServersTransportTCPApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *ServersTransportTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *ServersTransportTCPApplyConfiguration) WithLabels(entries map[string]string) *ServersTransportTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *ServersTransportTCPApplyConfiguration) WithAnnotations(entries map[string]string) *ServersTransportTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *ServersTransportTCPApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *ServersTransportTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *ServersTransportTCPApplyConfiguration) WithFinalizers(values ...string) *ServersTransportTCPApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *ServersTransportTCPApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} + } +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spec field is set to the value of the last call. +func (b *ServersTransportTCPApplyConfiguration) WithSpec(value *ServersTransportTCPSpecApplyConfiguration) *ServersTransportTCPApplyConfiguration { + b.Spec = value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *ServersTransportTCPApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *ServersTransportTCPApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *ServersTransportTCPApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *ServersTransportTCPApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serverstransporttcpspec.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serverstransporttcpspec.go new file mode 100644 index 000000000..157012eac --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serverstransporttcpspec.go @@ -0,0 +1,88 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + dynamic "github.com/traefik/traefik/v3/pkg/config/dynamic" + intstr "k8s.io/apimachinery/pkg/util/intstr" +) + +// ServersTransportTCPSpecApplyConfiguration represents a declarative configuration of the ServersTransportTCPSpec type for use +// with apply. +type ServersTransportTCPSpecApplyConfiguration struct { + DialTimeout *intstr.IntOrString `json:"dialTimeout,omitempty"` + DialKeepAlive *intstr.IntOrString `json:"dialKeepAlive,omitempty"` + ProxyProtocol *dynamic.ProxyProtocol `json:"proxyProtocol,omitempty"` + TerminationDelay *intstr.IntOrString `json:"terminationDelay,omitempty"` + TLS *TLSClientConfigApplyConfiguration `json:"tls,omitempty"` +} + +// ServersTransportTCPSpecApplyConfiguration constructs a declarative configuration of the ServersTransportTCPSpec type for use with +// apply. +func ServersTransportTCPSpec() *ServersTransportTCPSpecApplyConfiguration { + return &ServersTransportTCPSpecApplyConfiguration{} +} + +// WithDialTimeout sets the DialTimeout field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DialTimeout field is set to the value of the last call. +func (b *ServersTransportTCPSpecApplyConfiguration) WithDialTimeout(value intstr.IntOrString) *ServersTransportTCPSpecApplyConfiguration { + b.DialTimeout = &value + return b +} + +// WithDialKeepAlive sets the DialKeepAlive field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DialKeepAlive field is set to the value of the last call. +func (b *ServersTransportTCPSpecApplyConfiguration) WithDialKeepAlive(value intstr.IntOrString) *ServersTransportTCPSpecApplyConfiguration { + b.DialKeepAlive = &value + return b +} + +// WithProxyProtocol sets the ProxyProtocol field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ProxyProtocol field is set to the value of the last call. +func (b *ServersTransportTCPSpecApplyConfiguration) WithProxyProtocol(value dynamic.ProxyProtocol) *ServersTransportTCPSpecApplyConfiguration { + b.ProxyProtocol = &value + return b +} + +// WithTerminationDelay sets the TerminationDelay field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the TerminationDelay field is set to the value of the last call. +func (b *ServersTransportTCPSpecApplyConfiguration) WithTerminationDelay(value intstr.IntOrString) *ServersTransportTCPSpecApplyConfiguration { + b.TerminationDelay = &value + return b +} + +// WithTLS sets the TLS field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the TLS field is set to the value of the last call. +func (b *ServersTransportTCPSpecApplyConfiguration) WithTLS(value *TLSClientConfigApplyConfiguration) *ServersTransportTCPSpecApplyConfiguration { + b.TLS = value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/service.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/service.go new file mode 100644 index 000000000..d0b8342c1 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/service.go @@ -0,0 +1,164 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + dynamic "github.com/traefik/traefik/v3/pkg/config/dynamic" + intstr "k8s.io/apimachinery/pkg/util/intstr" +) + +// ServiceApplyConfiguration represents a declarative configuration of the Service type for use +// with apply. +type ServiceApplyConfiguration struct { + LoadBalancerSpecApplyConfiguration `json:",inline"` +} + +// ServiceApplyConfiguration constructs a declarative configuration of the Service type for use with +// apply. +func Service() *ServiceApplyConfiguration { + return &ServiceApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *ServiceApplyConfiguration) WithName(value string) *ServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Name = &value + return b +} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *ServiceApplyConfiguration) WithKind(value string) *ServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Kind = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *ServiceApplyConfiguration) WithNamespace(value string) *ServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Namespace = &value + return b +} + +// WithSticky sets the Sticky field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Sticky field is set to the value of the last call. +func (b *ServiceApplyConfiguration) WithSticky(value dynamic.Sticky) *ServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Sticky = &value + return b +} + +// WithPort sets the Port field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Port field is set to the value of the last call. +func (b *ServiceApplyConfiguration) WithPort(value intstr.IntOrString) *ServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Port = &value + return b +} + +// WithScheme sets the Scheme field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Scheme field is set to the value of the last call. +func (b *ServiceApplyConfiguration) WithScheme(value string) *ServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Scheme = &value + return b +} + +// WithStrategy sets the Strategy field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Strategy field is set to the value of the last call. +func (b *ServiceApplyConfiguration) WithStrategy(value dynamic.BalancerStrategy) *ServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Strategy = &value + return b +} + +// WithPassHostHeader sets the PassHostHeader field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PassHostHeader field is set to the value of the last call. +func (b *ServiceApplyConfiguration) WithPassHostHeader(value bool) *ServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.PassHostHeader = &value + return b +} + +// WithResponseForwarding sets the ResponseForwarding field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResponseForwarding field is set to the value of the last call. +func (b *ServiceApplyConfiguration) WithResponseForwarding(value *ResponseForwardingApplyConfiguration) *ServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.ResponseForwarding = value + return b +} + +// WithServersTransport sets the ServersTransport field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ServersTransport field is set to the value of the last call. +func (b *ServiceApplyConfiguration) WithServersTransport(value string) *ServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.ServersTransport = &value + return b +} + +// WithWeight sets the Weight field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Weight field is set to the value of the last call. +func (b *ServiceApplyConfiguration) WithWeight(value int) *ServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.Weight = &value + return b +} + +// WithNativeLB sets the NativeLB field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the NativeLB field is set to the value of the last call. +func (b *ServiceApplyConfiguration) WithNativeLB(value bool) *ServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.NativeLB = &value + return b +} + +// WithNodePortLB sets the NodePortLB field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the NodePortLB field is set to the value of the last call. +func (b *ServiceApplyConfiguration) WithNodePortLB(value bool) *ServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.NodePortLB = &value + return b +} + +// WithHealthCheck sets the HealthCheck field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the HealthCheck field is set to the value of the last call. +func (b *ServiceApplyConfiguration) WithHealthCheck(value *ServerHealthCheckApplyConfiguration) *ServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.HealthCheck = value + return b +} + +// WithPassiveHealthCheck sets the PassiveHealthCheck field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PassiveHealthCheck field is set to the value of the last call. +func (b *ServiceApplyConfiguration) WithPassiveHealthCheck(value *PassiveServerHealthCheckApplyConfiguration) *ServiceApplyConfiguration { + b.LoadBalancerSpecApplyConfiguration.PassiveHealthCheck = value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/servicetcp.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/servicetcp.go new file mode 100644 index 000000000..b5bdc0833 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/servicetcp.go @@ -0,0 +1,133 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + dynamic "github.com/traefik/traefik/v3/pkg/config/dynamic" + intstr "k8s.io/apimachinery/pkg/util/intstr" +) + +// ServiceTCPApplyConfiguration represents a declarative configuration of the ServiceTCP type for use +// with apply. +type ServiceTCPApplyConfiguration struct { + Name *string `json:"name,omitempty"` + Namespace *string `json:"namespace,omitempty"` + Port *intstr.IntOrString `json:"port,omitempty"` + Weight *int `json:"weight,omitempty"` + TerminationDelay *int `json:"terminationDelay,omitempty"` + ProxyProtocol *dynamic.ProxyProtocol `json:"proxyProtocol,omitempty"` + ServersTransport *string `json:"serversTransport,omitempty"` + TLS *bool `json:"tls,omitempty"` + NativeLB *bool `json:"nativeLB,omitempty"` + NodePortLB *bool `json:"nodePortLB,omitempty"` +} + +// ServiceTCPApplyConfiguration constructs a declarative configuration of the ServiceTCP type for use with +// apply. +func ServiceTCP() *ServiceTCPApplyConfiguration { + return &ServiceTCPApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *ServiceTCPApplyConfiguration) WithName(value string) *ServiceTCPApplyConfiguration { + b.Name = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *ServiceTCPApplyConfiguration) WithNamespace(value string) *ServiceTCPApplyConfiguration { + b.Namespace = &value + return b +} + +// WithPort sets the Port field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Port field is set to the value of the last call. +func (b *ServiceTCPApplyConfiguration) WithPort(value intstr.IntOrString) *ServiceTCPApplyConfiguration { + b.Port = &value + return b +} + +// WithWeight sets the Weight field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Weight field is set to the value of the last call. +func (b *ServiceTCPApplyConfiguration) WithWeight(value int) *ServiceTCPApplyConfiguration { + b.Weight = &value + return b +} + +// WithTerminationDelay sets the TerminationDelay field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the TerminationDelay field is set to the value of the last call. +func (b *ServiceTCPApplyConfiguration) WithTerminationDelay(value int) *ServiceTCPApplyConfiguration { + b.TerminationDelay = &value + return b +} + +// WithProxyProtocol sets the ProxyProtocol field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ProxyProtocol field is set to the value of the last call. +func (b *ServiceTCPApplyConfiguration) WithProxyProtocol(value dynamic.ProxyProtocol) *ServiceTCPApplyConfiguration { + b.ProxyProtocol = &value + return b +} + +// WithServersTransport sets the ServersTransport field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ServersTransport field is set to the value of the last call. +func (b *ServiceTCPApplyConfiguration) WithServersTransport(value string) *ServiceTCPApplyConfiguration { + b.ServersTransport = &value + return b +} + +// WithTLS sets the TLS field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the TLS field is set to the value of the last call. +func (b *ServiceTCPApplyConfiguration) WithTLS(value bool) *ServiceTCPApplyConfiguration { + b.TLS = &value + return b +} + +// WithNativeLB sets the NativeLB field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the NativeLB field is set to the value of the last call. +func (b *ServiceTCPApplyConfiguration) WithNativeLB(value bool) *ServiceTCPApplyConfiguration { + b.NativeLB = &value + return b +} + +// WithNodePortLB sets the NodePortLB field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the NodePortLB field is set to the value of the last call. +func (b *ServiceTCPApplyConfiguration) WithNodePortLB(value bool) *ServiceTCPApplyConfiguration { + b.NodePortLB = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serviceudp.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serviceudp.go new file mode 100644 index 000000000..325b83c33 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/serviceudp.go @@ -0,0 +1,96 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + intstr "k8s.io/apimachinery/pkg/util/intstr" +) + +// ServiceUDPApplyConfiguration represents a declarative configuration of the ServiceUDP type for use +// with apply. +type ServiceUDPApplyConfiguration struct { + Name *string `json:"name,omitempty"` + Namespace *string `json:"namespace,omitempty"` + Port *intstr.IntOrString `json:"port,omitempty"` + Weight *int `json:"weight,omitempty"` + NativeLB *bool `json:"nativeLB,omitempty"` + NodePortLB *bool `json:"nodePortLB,omitempty"` +} + +// ServiceUDPApplyConfiguration constructs a declarative configuration of the ServiceUDP type for use with +// apply. +func ServiceUDP() *ServiceUDPApplyConfiguration { + return &ServiceUDPApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *ServiceUDPApplyConfiguration) WithName(value string) *ServiceUDPApplyConfiguration { + b.Name = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *ServiceUDPApplyConfiguration) WithNamespace(value string) *ServiceUDPApplyConfiguration { + b.Namespace = &value + return b +} + +// WithPort sets the Port field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Port field is set to the value of the last call. +func (b *ServiceUDPApplyConfiguration) WithPort(value intstr.IntOrString) *ServiceUDPApplyConfiguration { + b.Port = &value + return b +} + +// WithWeight sets the Weight field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Weight field is set to the value of the last call. +func (b *ServiceUDPApplyConfiguration) WithWeight(value int) *ServiceUDPApplyConfiguration { + b.Weight = &value + return b +} + +// WithNativeLB sets the NativeLB field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the NativeLB field is set to the value of the last call. +func (b *ServiceUDPApplyConfiguration) WithNativeLB(value bool) *ServiceUDPApplyConfiguration { + b.NativeLB = &value + return b +} + +// WithNodePortLB sets the NodePortLB field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the NodePortLB field is set to the value of the last call. +func (b *ServiceUDPApplyConfiguration) WithNodePortLB(value bool) *ServiceUDPApplyConfiguration { + b.NodePortLB = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tls.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tls.go new file mode 100644 index 000000000..9e628535b --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tls.go @@ -0,0 +1,89 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + types "github.com/traefik/traefik/v3/pkg/types" +) + +// TLSApplyConfiguration represents a declarative configuration of the TLS type for use +// with apply. +type TLSApplyConfiguration struct { + SecretName *string `json:"secretName,omitempty"` + Options *TLSOptionRefApplyConfiguration `json:"options,omitempty"` + Store *TLSStoreRefApplyConfiguration `json:"store,omitempty"` + CertResolver *string `json:"certResolver,omitempty"` + Domains []types.Domain `json:"domains,omitempty"` +} + +// TLSApplyConfiguration constructs a declarative configuration of the TLS type for use with +// apply. +func TLS() *TLSApplyConfiguration { + return &TLSApplyConfiguration{} +} + +// WithSecretName sets the SecretName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SecretName field is set to the value of the last call. +func (b *TLSApplyConfiguration) WithSecretName(value string) *TLSApplyConfiguration { + b.SecretName = &value + return b +} + +// WithOptions sets the Options field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Options field is set to the value of the last call. +func (b *TLSApplyConfiguration) WithOptions(value *TLSOptionRefApplyConfiguration) *TLSApplyConfiguration { + b.Options = value + return b +} + +// WithStore sets the Store field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Store field is set to the value of the last call. +func (b *TLSApplyConfiguration) WithStore(value *TLSStoreRefApplyConfiguration) *TLSApplyConfiguration { + b.Store = value + return b +} + +// WithCertResolver sets the CertResolver field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CertResolver field is set to the value of the last call. +func (b *TLSApplyConfiguration) WithCertResolver(value string) *TLSApplyConfiguration { + b.CertResolver = &value + return b +} + +// WithDomains adds the given value to the Domains field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Domains field. +func (b *TLSApplyConfiguration) WithDomains(values ...types.Domain) *TLSApplyConfiguration { + for i := range values { + b.Domains = append(b.Domains, values[i]) + } + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsclientconfig.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsclientconfig.go new file mode 100644 index 000000000..db409216d --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsclientconfig.go @@ -0,0 +1,114 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + dynamic "github.com/traefik/traefik/v3/pkg/config/dynamic" +) + +// TLSClientConfigApplyConfiguration represents a declarative configuration of the TLSClientConfig type for use +// with apply. +type TLSClientConfigApplyConfiguration struct { + ServerName *string `json:"serverName,omitempty"` + InsecureSkipVerify *bool `json:"insecureSkipVerify,omitempty"` + RootCAs []RootCAApplyConfiguration `json:"rootCAs,omitempty"` + RootCAsSecrets []string `json:"rootCAsSecrets,omitempty"` + CertificatesSecrets []string `json:"certificatesSecrets,omitempty"` + PeerCertURI *string `json:"peerCertURI,omitempty"` + Spiffe *dynamic.Spiffe `json:"spiffe,omitempty"` +} + +// TLSClientConfigApplyConfiguration constructs a declarative configuration of the TLSClientConfig type for use with +// apply. +func TLSClientConfig() *TLSClientConfigApplyConfiguration { + return &TLSClientConfigApplyConfiguration{} +} + +// WithServerName sets the ServerName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ServerName field is set to the value of the last call. +func (b *TLSClientConfigApplyConfiguration) WithServerName(value string) *TLSClientConfigApplyConfiguration { + b.ServerName = &value + return b +} + +// WithInsecureSkipVerify sets the InsecureSkipVerify field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the InsecureSkipVerify field is set to the value of the last call. +func (b *TLSClientConfigApplyConfiguration) WithInsecureSkipVerify(value bool) *TLSClientConfigApplyConfiguration { + b.InsecureSkipVerify = &value + return b +} + +// WithRootCAs adds the given value to the RootCAs field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the RootCAs field. +func (b *TLSClientConfigApplyConfiguration) WithRootCAs(values ...*RootCAApplyConfiguration) *TLSClientConfigApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithRootCAs") + } + b.RootCAs = append(b.RootCAs, *values[i]) + } + return b +} + +// WithRootCAsSecrets adds the given value to the RootCAsSecrets field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the RootCAsSecrets field. +func (b *TLSClientConfigApplyConfiguration) WithRootCAsSecrets(values ...string) *TLSClientConfigApplyConfiguration { + for i := range values { + b.RootCAsSecrets = append(b.RootCAsSecrets, values[i]) + } + return b +} + +// WithCertificatesSecrets adds the given value to the CertificatesSecrets field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the CertificatesSecrets field. +func (b *TLSClientConfigApplyConfiguration) WithCertificatesSecrets(values ...string) *TLSClientConfigApplyConfiguration { + for i := range values { + b.CertificatesSecrets = append(b.CertificatesSecrets, values[i]) + } + return b +} + +// WithPeerCertURI sets the PeerCertURI field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PeerCertURI field is set to the value of the last call. +func (b *TLSClientConfigApplyConfiguration) WithPeerCertURI(value string) *TLSClientConfigApplyConfiguration { + b.PeerCertURI = &value + return b +} + +// WithSpiffe sets the Spiffe field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spiffe field is set to the value of the last call. +func (b *TLSClientConfigApplyConfiguration) WithSpiffe(value dynamic.Spiffe) *TLSClientConfigApplyConfiguration { + b.Spiffe = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsoption.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsoption.go new file mode 100644 index 000000000..e391978ec --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsoption.go @@ -0,0 +1,241 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// TLSOptionApplyConfiguration represents a declarative configuration of the TLSOption type for use +// with apply. +type TLSOptionApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *TLSOptionSpecApplyConfiguration `json:"spec,omitempty"` +} + +// TLSOption constructs a declarative configuration of the TLSOption type for use with +// apply. +func TLSOption(name, namespace string) *TLSOptionApplyConfiguration { + b := &TLSOptionApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("TLSOption") + b.WithAPIVersion("traefik.io/v1alpha1") + return b +} +func (b TLSOptionApplyConfiguration) IsApplyConfiguration() {} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *TLSOptionApplyConfiguration) WithKind(value string) *TLSOptionApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *TLSOptionApplyConfiguration) WithAPIVersion(value string) *TLSOptionApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *TLSOptionApplyConfiguration) WithName(value string) *TLSOptionApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *TLSOptionApplyConfiguration) WithGenerateName(value string) *TLSOptionApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *TLSOptionApplyConfiguration) WithNamespace(value string) *TLSOptionApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *TLSOptionApplyConfiguration) WithUID(value types.UID) *TLSOptionApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *TLSOptionApplyConfiguration) WithResourceVersion(value string) *TLSOptionApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *TLSOptionApplyConfiguration) WithGeneration(value int64) *TLSOptionApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *TLSOptionApplyConfiguration) WithCreationTimestamp(value metav1.Time) *TLSOptionApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *TLSOptionApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *TLSOptionApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *TLSOptionApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *TLSOptionApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *TLSOptionApplyConfiguration) WithLabels(entries map[string]string) *TLSOptionApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *TLSOptionApplyConfiguration) WithAnnotations(entries map[string]string) *TLSOptionApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *TLSOptionApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *TLSOptionApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *TLSOptionApplyConfiguration) WithFinalizers(values ...string) *TLSOptionApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *TLSOptionApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} + } +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spec field is set to the value of the last call. +func (b *TLSOptionApplyConfiguration) WithSpec(value *TLSOptionSpecApplyConfiguration) *TLSOptionApplyConfiguration { + b.Spec = value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *TLSOptionApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *TLSOptionApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *TLSOptionApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *TLSOptionApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsoptionref.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsoptionref.go new file mode 100644 index 000000000..a3abfd27a --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsoptionref.go @@ -0,0 +1,56 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// TLSOptionRefApplyConfiguration represents a declarative configuration of the TLSOptionRef type for use +// with apply. +type TLSOptionRefApplyConfiguration struct { + Name *string `json:"name,omitempty"` + Namespace *string `json:"namespace,omitempty"` +} + +// TLSOptionRefApplyConfiguration constructs a declarative configuration of the TLSOptionRef type for use with +// apply. +func TLSOptionRef() *TLSOptionRefApplyConfiguration { + return &TLSOptionRefApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *TLSOptionRefApplyConfiguration) WithName(value string) *TLSOptionRefApplyConfiguration { + b.Name = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *TLSOptionRefApplyConfiguration) WithNamespace(value string) *TLSOptionRefApplyConfiguration { + b.Namespace = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsoptionspec.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsoptionspec.go new file mode 100644 index 000000000..adf256b6e --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsoptionspec.go @@ -0,0 +1,125 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// TLSOptionSpecApplyConfiguration represents a declarative configuration of the TLSOptionSpec type for use +// with apply. +type TLSOptionSpecApplyConfiguration struct { + MinVersion *string `json:"minVersion,omitempty"` + MaxVersion *string `json:"maxVersion,omitempty"` + CipherSuites []string `json:"cipherSuites,omitempty"` + CurvePreferences []string `json:"curvePreferences,omitempty"` + ClientAuth *ClientAuthApplyConfiguration `json:"clientAuth,omitempty"` + SniStrict *bool `json:"sniStrict,omitempty"` + ALPNProtocols []string `json:"alpnProtocols,omitempty"` + DisableSessionTickets *bool `json:"disableSessionTickets,omitempty"` + PreferServerCipherSuites *bool `json:"preferServerCipherSuites,omitempty"` +} + +// TLSOptionSpecApplyConfiguration constructs a declarative configuration of the TLSOptionSpec type for use with +// apply. +func TLSOptionSpec() *TLSOptionSpecApplyConfiguration { + return &TLSOptionSpecApplyConfiguration{} +} + +// WithMinVersion sets the MinVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the MinVersion field is set to the value of the last call. +func (b *TLSOptionSpecApplyConfiguration) WithMinVersion(value string) *TLSOptionSpecApplyConfiguration { + b.MinVersion = &value + return b +} + +// WithMaxVersion sets the MaxVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the MaxVersion field is set to the value of the last call. +func (b *TLSOptionSpecApplyConfiguration) WithMaxVersion(value string) *TLSOptionSpecApplyConfiguration { + b.MaxVersion = &value + return b +} + +// WithCipherSuites adds the given value to the CipherSuites field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the CipherSuites field. +func (b *TLSOptionSpecApplyConfiguration) WithCipherSuites(values ...string) *TLSOptionSpecApplyConfiguration { + for i := range values { + b.CipherSuites = append(b.CipherSuites, values[i]) + } + return b +} + +// WithCurvePreferences adds the given value to the CurvePreferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the CurvePreferences field. +func (b *TLSOptionSpecApplyConfiguration) WithCurvePreferences(values ...string) *TLSOptionSpecApplyConfiguration { + for i := range values { + b.CurvePreferences = append(b.CurvePreferences, values[i]) + } + return b +} + +// WithClientAuth sets the ClientAuth field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ClientAuth field is set to the value of the last call. +func (b *TLSOptionSpecApplyConfiguration) WithClientAuth(value *ClientAuthApplyConfiguration) *TLSOptionSpecApplyConfiguration { + b.ClientAuth = value + return b +} + +// WithSniStrict sets the SniStrict field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SniStrict field is set to the value of the last call. +func (b *TLSOptionSpecApplyConfiguration) WithSniStrict(value bool) *TLSOptionSpecApplyConfiguration { + b.SniStrict = &value + return b +} + +// WithALPNProtocols adds the given value to the ALPNProtocols field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the ALPNProtocols field. +func (b *TLSOptionSpecApplyConfiguration) WithALPNProtocols(values ...string) *TLSOptionSpecApplyConfiguration { + for i := range values { + b.ALPNProtocols = append(b.ALPNProtocols, values[i]) + } + return b +} + +// WithDisableSessionTickets sets the DisableSessionTickets field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DisableSessionTickets field is set to the value of the last call. +func (b *TLSOptionSpecApplyConfiguration) WithDisableSessionTickets(value bool) *TLSOptionSpecApplyConfiguration { + b.DisableSessionTickets = &value + return b +} + +// WithPreferServerCipherSuites sets the PreferServerCipherSuites field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the PreferServerCipherSuites field is set to the value of the last call. +func (b *TLSOptionSpecApplyConfiguration) WithPreferServerCipherSuites(value bool) *TLSOptionSpecApplyConfiguration { + b.PreferServerCipherSuites = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsstore.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsstore.go new file mode 100644 index 000000000..1f0a2858d --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsstore.go @@ -0,0 +1,241 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// TLSStoreApplyConfiguration represents a declarative configuration of the TLSStore type for use +// with apply. +type TLSStoreApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *TLSStoreSpecApplyConfiguration `json:"spec,omitempty"` +} + +// TLSStore constructs a declarative configuration of the TLSStore type for use with +// apply. +func TLSStore(name, namespace string) *TLSStoreApplyConfiguration { + b := &TLSStoreApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("TLSStore") + b.WithAPIVersion("traefik.io/v1alpha1") + return b +} +func (b TLSStoreApplyConfiguration) IsApplyConfiguration() {} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *TLSStoreApplyConfiguration) WithKind(value string) *TLSStoreApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *TLSStoreApplyConfiguration) WithAPIVersion(value string) *TLSStoreApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *TLSStoreApplyConfiguration) WithName(value string) *TLSStoreApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *TLSStoreApplyConfiguration) WithGenerateName(value string) *TLSStoreApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *TLSStoreApplyConfiguration) WithNamespace(value string) *TLSStoreApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *TLSStoreApplyConfiguration) WithUID(value types.UID) *TLSStoreApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *TLSStoreApplyConfiguration) WithResourceVersion(value string) *TLSStoreApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *TLSStoreApplyConfiguration) WithGeneration(value int64) *TLSStoreApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *TLSStoreApplyConfiguration) WithCreationTimestamp(value metav1.Time) *TLSStoreApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *TLSStoreApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *TLSStoreApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *TLSStoreApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *TLSStoreApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *TLSStoreApplyConfiguration) WithLabels(entries map[string]string) *TLSStoreApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *TLSStoreApplyConfiguration) WithAnnotations(entries map[string]string) *TLSStoreApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *TLSStoreApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *TLSStoreApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *TLSStoreApplyConfiguration) WithFinalizers(values ...string) *TLSStoreApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *TLSStoreApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} + } +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spec field is set to the value of the last call. +func (b *TLSStoreApplyConfiguration) WithSpec(value *TLSStoreSpecApplyConfiguration) *TLSStoreApplyConfiguration { + b.Spec = value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *TLSStoreApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *TLSStoreApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *TLSStoreApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *TLSStoreApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsstoreref.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsstoreref.go new file mode 100644 index 000000000..ac89490c8 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsstoreref.go @@ -0,0 +1,56 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// TLSStoreRefApplyConfiguration represents a declarative configuration of the TLSStoreRef type for use +// with apply. +type TLSStoreRefApplyConfiguration struct { + Name *string `json:"name,omitempty"` + Namespace *string `json:"namespace,omitempty"` +} + +// TLSStoreRefApplyConfiguration constructs a declarative configuration of the TLSStoreRef type for use with +// apply. +func TLSStoreRef() *TLSStoreRefApplyConfiguration { + return &TLSStoreRefApplyConfiguration{} +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *TLSStoreRefApplyConfiguration) WithName(value string) *TLSStoreRefApplyConfiguration { + b.Name = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *TLSStoreRefApplyConfiguration) WithNamespace(value string) *TLSStoreRefApplyConfiguration { + b.Namespace = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsstorespec.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsstorespec.go new file mode 100644 index 000000000..344ef8104 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlsstorespec.go @@ -0,0 +1,74 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + tls "github.com/traefik/traefik/v3/pkg/tls" +) + +// TLSStoreSpecApplyConfiguration represents a declarative configuration of the TLSStoreSpec type for use +// with apply. +type TLSStoreSpecApplyConfiguration struct { + DefaultCertificate *CertificateApplyConfiguration `json:"defaultCertificate,omitempty"` + DefaultGeneratedCert *tls.GeneratedCert `json:"defaultGeneratedCert,omitempty"` + Certificates []CertificateApplyConfiguration `json:"certificates,omitempty"` +} + +// TLSStoreSpecApplyConfiguration constructs a declarative configuration of the TLSStoreSpec type for use with +// apply. +func TLSStoreSpec() *TLSStoreSpecApplyConfiguration { + return &TLSStoreSpecApplyConfiguration{} +} + +// WithDefaultCertificate sets the DefaultCertificate field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DefaultCertificate field is set to the value of the last call. +func (b *TLSStoreSpecApplyConfiguration) WithDefaultCertificate(value *CertificateApplyConfiguration) *TLSStoreSpecApplyConfiguration { + b.DefaultCertificate = value + return b +} + +// WithDefaultGeneratedCert sets the DefaultGeneratedCert field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DefaultGeneratedCert field is set to the value of the last call. +func (b *TLSStoreSpecApplyConfiguration) WithDefaultGeneratedCert(value tls.GeneratedCert) *TLSStoreSpecApplyConfiguration { + b.DefaultGeneratedCert = &value + return b +} + +// WithCertificates adds the given value to the Certificates field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Certificates field. +func (b *TLSStoreSpecApplyConfiguration) WithCertificates(values ...*CertificateApplyConfiguration) *TLSStoreSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithCertificates") + } + b.Certificates = append(b.Certificates, *values[i]) + } + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlstcp.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlstcp.go new file mode 100644 index 000000000..8728fbd2a --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/tlstcp.go @@ -0,0 +1,98 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + types "github.com/traefik/traefik/v3/pkg/types" +) + +// TLSTCPApplyConfiguration represents a declarative configuration of the TLSTCP type for use +// with apply. +type TLSTCPApplyConfiguration struct { + SecretName *string `json:"secretName,omitempty"` + Passthrough *bool `json:"passthrough,omitempty"` + Options *ObjectReferenceApplyConfiguration `json:"options,omitempty"` + Store *ObjectReferenceApplyConfiguration `json:"store,omitempty"` + CertResolver *string `json:"certResolver,omitempty"` + Domains []types.Domain `json:"domains,omitempty"` +} + +// TLSTCPApplyConfiguration constructs a declarative configuration of the TLSTCP type for use with +// apply. +func TLSTCP() *TLSTCPApplyConfiguration { + return &TLSTCPApplyConfiguration{} +} + +// WithSecretName sets the SecretName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the SecretName field is set to the value of the last call. +func (b *TLSTCPApplyConfiguration) WithSecretName(value string) *TLSTCPApplyConfiguration { + b.SecretName = &value + return b +} + +// WithPassthrough sets the Passthrough field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Passthrough field is set to the value of the last call. +func (b *TLSTCPApplyConfiguration) WithPassthrough(value bool) *TLSTCPApplyConfiguration { + b.Passthrough = &value + return b +} + +// WithOptions sets the Options field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Options field is set to the value of the last call. +func (b *TLSTCPApplyConfiguration) WithOptions(value *ObjectReferenceApplyConfiguration) *TLSTCPApplyConfiguration { + b.Options = value + return b +} + +// WithStore sets the Store field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Store field is set to the value of the last call. +func (b *TLSTCPApplyConfiguration) WithStore(value *ObjectReferenceApplyConfiguration) *TLSTCPApplyConfiguration { + b.Store = value + return b +} + +// WithCertResolver sets the CertResolver field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CertResolver field is set to the value of the last call. +func (b *TLSTCPApplyConfiguration) WithCertResolver(value string) *TLSTCPApplyConfiguration { + b.CertResolver = &value + return b +} + +// WithDomains adds the given value to the Domains field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Domains field. +func (b *TLSTCPApplyConfiguration) WithDomains(values ...types.Domain) *TLSTCPApplyConfiguration { + for i := range values { + b.Domains = append(b.Domains, values[i]) + } + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/traefikservice.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/traefikservice.go new file mode 100644 index 000000000..908418aae --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/traefikservice.go @@ -0,0 +1,241 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// TraefikServiceApplyConfiguration represents a declarative configuration of the TraefikService type for use +// with apply. +type TraefikServiceApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *TraefikServiceSpecApplyConfiguration `json:"spec,omitempty"` +} + +// TraefikService constructs a declarative configuration of the TraefikService type for use with +// apply. +func TraefikService(name, namespace string) *TraefikServiceApplyConfiguration { + b := &TraefikServiceApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("TraefikService") + b.WithAPIVersion("traefik.io/v1alpha1") + return b +} +func (b TraefikServiceApplyConfiguration) IsApplyConfiguration() {} + +// WithKind sets the Kind field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Kind field is set to the value of the last call. +func (b *TraefikServiceApplyConfiguration) WithKind(value string) *TraefikServiceApplyConfiguration { + b.TypeMetaApplyConfiguration.Kind = &value + return b +} + +// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the APIVersion field is set to the value of the last call. +func (b *TraefikServiceApplyConfiguration) WithAPIVersion(value string) *TraefikServiceApplyConfiguration { + b.TypeMetaApplyConfiguration.APIVersion = &value + return b +} + +// WithName sets the Name field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Name field is set to the value of the last call. +func (b *TraefikServiceApplyConfiguration) WithName(value string) *TraefikServiceApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Name = &value + return b +} + +// WithGenerateName sets the GenerateName field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the GenerateName field is set to the value of the last call. +func (b *TraefikServiceApplyConfiguration) WithGenerateName(value string) *TraefikServiceApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.GenerateName = &value + return b +} + +// WithNamespace sets the Namespace field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Namespace field is set to the value of the last call. +func (b *TraefikServiceApplyConfiguration) WithNamespace(value string) *TraefikServiceApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Namespace = &value + return b +} + +// WithUID sets the UID field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the UID field is set to the value of the last call. +func (b *TraefikServiceApplyConfiguration) WithUID(value types.UID) *TraefikServiceApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.UID = &value + return b +} + +// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the ResourceVersion field is set to the value of the last call. +func (b *TraefikServiceApplyConfiguration) WithResourceVersion(value string) *TraefikServiceApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.ResourceVersion = &value + return b +} + +// WithGeneration sets the Generation field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Generation field is set to the value of the last call. +func (b *TraefikServiceApplyConfiguration) WithGeneration(value int64) *TraefikServiceApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.Generation = &value + return b +} + +// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the CreationTimestamp field is set to the value of the last call. +func (b *TraefikServiceApplyConfiguration) WithCreationTimestamp(value metav1.Time) *TraefikServiceApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.CreationTimestamp = &value + return b +} + +// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionTimestamp field is set to the value of the last call. +func (b *TraefikServiceApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *TraefikServiceApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value + return b +} + +// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. +func (b *TraefikServiceApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *TraefikServiceApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value + return b +} + +// WithLabels puts the entries into the Labels field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Labels field, +// overwriting an existing map entries in Labels field with the same key. +func (b *TraefikServiceApplyConfiguration) WithLabels(entries map[string]string) *TraefikServiceApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Labels[k] = v + } + return b +} + +// WithAnnotations puts the entries into the Annotations field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, the entries provided by each call will be put on the Annotations field, +// overwriting an existing map entries in Annotations field with the same key. +func (b *TraefikServiceApplyConfiguration) WithAnnotations(entries map[string]string) *TraefikServiceApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { + b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.ObjectMetaApplyConfiguration.Annotations[k] = v + } + return b +} + +// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the OwnerReferences field. +func (b *TraefikServiceApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *TraefikServiceApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + if values[i] == nil { + panic("nil value passed to WithOwnerReferences") + } + b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) + } + return b +} + +// WithFinalizers adds the given value to the Finalizers field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Finalizers field. +func (b *TraefikServiceApplyConfiguration) WithFinalizers(values ...string) *TraefikServiceApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *TraefikServiceApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { + if b.ObjectMetaApplyConfiguration == nil { + b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{} + } +} + +// WithSpec sets the Spec field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Spec field is set to the value of the last call. +func (b *TraefikServiceApplyConfiguration) WithSpec(value *TraefikServiceSpecApplyConfiguration) *TraefikServiceApplyConfiguration { + b.Spec = value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *TraefikServiceApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *TraefikServiceApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *TraefikServiceApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *TraefikServiceApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/traefikservicespec.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/traefikservicespec.go new file mode 100644 index 000000000..7ddaac9e6 --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/traefikservicespec.go @@ -0,0 +1,65 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +// TraefikServiceSpecApplyConfiguration represents a declarative configuration of the TraefikServiceSpec type for use +// with apply. +type TraefikServiceSpecApplyConfiguration struct { + Weighted *WeightedRoundRobinApplyConfiguration `json:"weighted,omitempty"` + Mirroring *MirroringApplyConfiguration `json:"mirroring,omitempty"` + HighestRandomWeight *HighestRandomWeightApplyConfiguration `json:"highestRandomWeight,omitempty"` +} + +// TraefikServiceSpecApplyConfiguration constructs a declarative configuration of the TraefikServiceSpec type for use with +// apply. +func TraefikServiceSpec() *TraefikServiceSpecApplyConfiguration { + return &TraefikServiceSpecApplyConfiguration{} +} + +// WithWeighted sets the Weighted field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Weighted field is set to the value of the last call. +func (b *TraefikServiceSpecApplyConfiguration) WithWeighted(value *WeightedRoundRobinApplyConfiguration) *TraefikServiceSpecApplyConfiguration { + b.Weighted = value + return b +} + +// WithMirroring sets the Mirroring field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Mirroring field is set to the value of the last call. +func (b *TraefikServiceSpecApplyConfiguration) WithMirroring(value *MirroringApplyConfiguration) *TraefikServiceSpecApplyConfiguration { + b.Mirroring = value + return b +} + +// WithHighestRandomWeight sets the HighestRandomWeight field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the HighestRandomWeight field is set to the value of the last call. +func (b *TraefikServiceSpecApplyConfiguration) WithHighestRandomWeight(value *HighestRandomWeightApplyConfiguration) *TraefikServiceSpecApplyConfiguration { + b.HighestRandomWeight = value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/weightedroundrobin.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/weightedroundrobin.go new file mode 100644 index 000000000..691e9d09e --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1/weightedroundrobin.go @@ -0,0 +1,65 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + dynamic "github.com/traefik/traefik/v3/pkg/config/dynamic" +) + +// WeightedRoundRobinApplyConfiguration represents a declarative configuration of the WeightedRoundRobin type for use +// with apply. +type WeightedRoundRobinApplyConfiguration struct { + Services []ServiceApplyConfiguration `json:"services,omitempty"` + Sticky *dynamic.Sticky `json:"sticky,omitempty"` +} + +// WeightedRoundRobinApplyConfiguration constructs a declarative configuration of the WeightedRoundRobin type for use with +// apply. +func WeightedRoundRobin() *WeightedRoundRobinApplyConfiguration { + return &WeightedRoundRobinApplyConfiguration{} +} + +// WithServices adds the given value to the Services field in the declarative configuration +// and returns the receiver, so that objects can be build by chaining "With" function invocations. +// If called multiple times, values provided by each call will be appended to the Services field. +func (b *WeightedRoundRobinApplyConfiguration) WithServices(values ...*ServiceApplyConfiguration) *WeightedRoundRobinApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithServices") + } + b.Services = append(b.Services, *values[i]) + } + return b +} + +// WithSticky sets the Sticky field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the Sticky field is set to the value of the last call. +func (b *WeightedRoundRobinApplyConfiguration) WithSticky(value dynamic.Sticky) *WeightedRoundRobinApplyConfiguration { + b.Sticky = &value + return b +} diff --git a/pkg/provider/kubernetes/crd/generated/applyconfiguration/utils.go b/pkg/provider/kubernetes/crd/generated/applyconfiguration/utils.go new file mode 100644 index 000000000..aa7141acc --- /dev/null +++ b/pkg/provider/kubernetes/crd/generated/applyconfiguration/utils.go @@ -0,0 +1,166 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +// Code generated by applyconfiguration-gen. DO NOT EDIT. + +package applyconfiguration + +import ( + internal "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/internal" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" + v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + managedfields "k8s.io/apimachinery/pkg/util/managedfields" +) + +// ForKind returns an apply configuration type for the given GroupVersionKind, or nil if no +// apply configuration type exists for the given GroupVersionKind. +func ForKind(kind schema.GroupVersionKind) interface{} { + switch kind { + // Group=traefik.io, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithKind("BasicAuth"): + return &traefikiov1alpha1.BasicAuthApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("Certificate"): + return &traefikiov1alpha1.CertificateApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("Chain"): + return &traefikiov1alpha1.ChainApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("CircuitBreaker"): + return &traefikiov1alpha1.CircuitBreakerApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ClientAuth"): + return &traefikiov1alpha1.ClientAuthApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ClientTLS"): + return &traefikiov1alpha1.ClientTLSApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ClientTLSWithCAOptional"): + return &traefikiov1alpha1.ClientTLSWithCAOptionalApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("Compress"): + return &traefikiov1alpha1.CompressApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("DigestAuth"): + return &traefikiov1alpha1.DigestAuthApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ErrorPage"): + return &traefikiov1alpha1.ErrorPageApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ForwardAuth"): + return &traefikiov1alpha1.ForwardAuthApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ForwardingTimeouts"): + return &traefikiov1alpha1.ForwardingTimeoutsApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("HighestRandomWeight"): + return &traefikiov1alpha1.HighestRandomWeightApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("IngressRoute"): + return &traefikiov1alpha1.IngressRouteApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("IngressRouteRef"): + return &traefikiov1alpha1.IngressRouteRefApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("IngressRouteSpec"): + return &traefikiov1alpha1.IngressRouteSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("IngressRouteTCP"): + return &traefikiov1alpha1.IngressRouteTCPApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("IngressRouteTCPSpec"): + return &traefikiov1alpha1.IngressRouteTCPSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("IngressRouteUDP"): + return &traefikiov1alpha1.IngressRouteUDPApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("IngressRouteUDPSpec"): + return &traefikiov1alpha1.IngressRouteUDPSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("LoadBalancerSpec"): + return &traefikiov1alpha1.LoadBalancerSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("Middleware"): + return &traefikiov1alpha1.MiddlewareApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("MiddlewareRef"): + return &traefikiov1alpha1.MiddlewareRefApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("MiddlewareSpec"): + return &traefikiov1alpha1.MiddlewareSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("MiddlewareTCP"): + return &traefikiov1alpha1.MiddlewareTCPApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("MiddlewareTCPSpec"): + return &traefikiov1alpha1.MiddlewareTCPSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("Mirroring"): + return &traefikiov1alpha1.MirroringApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("MirrorService"): + return &traefikiov1alpha1.MirrorServiceApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ObjectReference"): + return &traefikiov1alpha1.ObjectReferenceApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("PassiveServerHealthCheck"): + return &traefikiov1alpha1.PassiveServerHealthCheckApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("RateLimit"): + return &traefikiov1alpha1.RateLimitApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("Redis"): + return &traefikiov1alpha1.RedisApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ResponseForwarding"): + return &traefikiov1alpha1.ResponseForwardingApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("Retry"): + return &traefikiov1alpha1.RetryApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("RootCA"): + return &traefikiov1alpha1.RootCAApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("Route"): + return &traefikiov1alpha1.RouteApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("RouteTCP"): + return &traefikiov1alpha1.RouteTCPApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("RouteUDP"): + return &traefikiov1alpha1.RouteUDPApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ServerHealthCheck"): + return &traefikiov1alpha1.ServerHealthCheckApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ServersTransport"): + return &traefikiov1alpha1.ServersTransportApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ServersTransportSpec"): + return &traefikiov1alpha1.ServersTransportSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ServersTransportTCP"): + return &traefikiov1alpha1.ServersTransportTCPApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ServersTransportTCPSpec"): + return &traefikiov1alpha1.ServersTransportTCPSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("Service"): + return &traefikiov1alpha1.ServiceApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ServiceTCP"): + return &traefikiov1alpha1.ServiceTCPApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("ServiceUDP"): + return &traefikiov1alpha1.ServiceUDPApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TLS"): + return &traefikiov1alpha1.TLSApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TLSClientConfig"): + return &traefikiov1alpha1.TLSClientConfigApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TLSOption"): + return &traefikiov1alpha1.TLSOptionApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TLSOptionRef"): + return &traefikiov1alpha1.TLSOptionRefApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TLSOptionSpec"): + return &traefikiov1alpha1.TLSOptionSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TLSStore"): + return &traefikiov1alpha1.TLSStoreApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TLSStoreRef"): + return &traefikiov1alpha1.TLSStoreRefApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TLSStoreSpec"): + return &traefikiov1alpha1.TLSStoreSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TLSTCP"): + return &traefikiov1alpha1.TLSTCPApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TraefikService"): + return &traefikiov1alpha1.TraefikServiceApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("TraefikServiceSpec"): + return &traefikiov1alpha1.TraefikServiceSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("WeightedRoundRobin"): + return &traefikiov1alpha1.WeightedRoundRobinApplyConfiguration{} + + } + return nil +} + +func NewTypeConverter(scheme *runtime.Scheme) managedfields.TypeConverter { + return managedfields.NewSchemeTypeConverter(scheme, internal.Parser()) +} diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/clientset.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/clientset.go index 81e985720..e99337aba 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/clientset.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/clientset.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,8 +27,8 @@ THE SOFTWARE. package versioned import ( - "fmt" - "net/http" + fmt "fmt" + http "net/http" traefikv1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1" discovery "k8s.io/client-go/discovery" diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake/clientset_generated.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake/clientset_generated.go index 771afb9cc..b8091758d 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake/clientset_generated.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake/clientset_generated.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,9 +27,11 @@ THE SOFTWARE. package fake import ( + applyconfiguration "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration" clientset "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned" traefikv1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1" faketraefikv1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/discovery" @@ -39,8 +41,12 @@ import ( // NewSimpleClientset returns a clientset that will respond with the provided objects. // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, -// without applying any validations and/or defaults. It shouldn't be considered a replacement +// without applying any field management, validations and/or defaults. It shouldn't be considered a replacement // for a real clientset and is mostly useful in simple unit tests. +// +// DEPRECATED: NewClientset replaces this with support for field management, which significantly improves +// server side apply testing. NewClientset is only available when apply configurations are generated (e.g. +// via --with-applyconfig). func NewSimpleClientset(objects ...runtime.Object) *Clientset { o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) for _, obj := range objects { @@ -53,9 +59,13 @@ func NewSimpleClientset(objects ...runtime.Object) *Clientset { cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} cs.AddReactor("*", "*", testing.ObjectReaction(o)) cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + var opts metav1.ListOptions + if watchActcion, ok := action.(testing.WatchActionImpl); ok { + opts = watchActcion.ListOptions + } gvr := action.GetResource() ns := action.GetNamespace() - watch, err := o.Watch(gvr, ns) + watch, err := o.Watch(gvr, ns, opts) if err != nil { return false, nil, err } @@ -82,6 +92,42 @@ func (c *Clientset) Tracker() testing.ObjectTracker { return c.tracker } +// NewClientset returns a clientset that will respond with the provided objects. +// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, +// without applying any validations and/or defaults. It shouldn't be considered a replacement +// for a real clientset and is mostly useful in simple unit tests. +func NewClientset(objects ...runtime.Object) *Clientset { + o := testing.NewFieldManagedObjectTracker( + scheme, + codecs.UniversalDecoder(), + applyconfiguration.NewTypeConverter(scheme), + ) + for _, obj := range objects { + if err := o.Add(obj); err != nil { + panic(err) + } + } + + cs := &Clientset{tracker: o} + cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} + cs.AddReactor("*", "*", testing.ObjectReaction(o)) + cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + var opts metav1.ListOptions + if watchAction, ok := action.(testing.WatchActionImpl); ok { + opts = watchAction.ListOptions + } + gvr := action.GetResource() + ns := action.GetNamespace() + watch, err := o.Watch(gvr, ns, opts) + if err != nil { + return false, nil, err + } + return true, watch, nil + }) + + return cs +} + var ( _ clientset.Interface = &Clientset{} _ testing.FakeClient = &Clientset{} diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake/doc.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake/doc.go index 8e14a2681..51f66e249 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake/doc.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake/doc.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake/register.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake/register.go index d89dd1d19..5981c3533 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake/register.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/fake/register.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme/doc.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme/doc.go index ce689e997..ab558f7a6 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme/doc.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme/doc.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme/register.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme/register.go index 23add3bf9..b4e546f4a 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme/register.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme/register.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/doc.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/doc.go index 19afe1581..84e289b0d 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/doc.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/doc.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/doc.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/doc.go index 7747ceae0..ff45ddce5 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/doc.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/doc.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_ingressroute.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_ingressroute.go index 66dec4591..7d23ec9fd 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_ingressroute.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_ingressroute.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,111 +27,35 @@ THE SOFTWARE. package fake import ( - "context" - + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" + typedtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1" v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + gentype "k8s.io/client-go/gentype" ) -// FakeIngressRoutes implements IngressRouteInterface -type FakeIngressRoutes struct { +// fakeIngressRoutes implements IngressRouteInterface +type fakeIngressRoutes struct { + *gentype.FakeClientWithListAndApply[*v1alpha1.IngressRoute, *v1alpha1.IngressRouteList, *traefikiov1alpha1.IngressRouteApplyConfiguration] Fake *FakeTraefikV1alpha1 - ns string } -var ingressroutesResource = v1alpha1.SchemeGroupVersion.WithResource("ingressroutes") - -var ingressroutesKind = v1alpha1.SchemeGroupVersion.WithKind("IngressRoute") - -// Get takes name of the ingressRoute, and returns the corresponding ingressRoute object, and an error if there is any. -func (c *FakeIngressRoutes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.IngressRoute, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(ingressroutesResource, c.ns, name), &v1alpha1.IngressRoute{}) - - if obj == nil { - return nil, err +func newFakeIngressRoutes(fake *FakeTraefikV1alpha1, namespace string) typedtraefikiov1alpha1.IngressRouteInterface { + return &fakeIngressRoutes{ + gentype.NewFakeClientWithListAndApply[*v1alpha1.IngressRoute, *v1alpha1.IngressRouteList, *traefikiov1alpha1.IngressRouteApplyConfiguration]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("ingressroutes"), + v1alpha1.SchemeGroupVersion.WithKind("IngressRoute"), + func() *v1alpha1.IngressRoute { return &v1alpha1.IngressRoute{} }, + func() *v1alpha1.IngressRouteList { return &v1alpha1.IngressRouteList{} }, + func(dst, src *v1alpha1.IngressRouteList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.IngressRouteList) []*v1alpha1.IngressRoute { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1alpha1.IngressRouteList, items []*v1alpha1.IngressRoute) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.IngressRoute), err -} - -// List takes label and field selectors, and returns the list of IngressRoutes that match those selectors. -func (c *FakeIngressRoutes) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.IngressRouteList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(ingressroutesResource, ingressroutesKind, c.ns, opts), &v1alpha1.IngressRouteList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.IngressRouteList{ListMeta: obj.(*v1alpha1.IngressRouteList).ListMeta} - for _, item := range obj.(*v1alpha1.IngressRouteList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested ingressRoutes. -func (c *FakeIngressRoutes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(ingressroutesResource, c.ns, opts)) - -} - -// Create takes the representation of a ingressRoute and creates it. Returns the server's representation of the ingressRoute, and an error, if there is any. -func (c *FakeIngressRoutes) Create(ctx context.Context, ingressRoute *v1alpha1.IngressRoute, opts v1.CreateOptions) (result *v1alpha1.IngressRoute, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(ingressroutesResource, c.ns, ingressRoute), &v1alpha1.IngressRoute{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.IngressRoute), err -} - -// Update takes the representation of a ingressRoute and updates it. Returns the server's representation of the ingressRoute, and an error, if there is any. -func (c *FakeIngressRoutes) Update(ctx context.Context, ingressRoute *v1alpha1.IngressRoute, opts v1.UpdateOptions) (result *v1alpha1.IngressRoute, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(ingressroutesResource, c.ns, ingressRoute), &v1alpha1.IngressRoute{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.IngressRoute), err -} - -// Delete takes name of the ingressRoute and deletes it. Returns an error if one occurs. -func (c *FakeIngressRoutes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(ingressroutesResource, c.ns, name, opts), &v1alpha1.IngressRoute{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeIngressRoutes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(ingressroutesResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.IngressRouteList{}) - return err -} - -// Patch applies the patch and returns the patched ingressRoute. -func (c *FakeIngressRoutes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.IngressRoute, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(ingressroutesResource, c.ns, name, pt, data, subresources...), &v1alpha1.IngressRoute{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.IngressRoute), err } diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_ingressroutetcp.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_ingressroutetcp.go index e88c24a59..5b8e0fdd4 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_ingressroutetcp.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_ingressroutetcp.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,111 +27,35 @@ THE SOFTWARE. package fake import ( - "context" - + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" + typedtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1" v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + gentype "k8s.io/client-go/gentype" ) -// FakeIngressRouteTCPs implements IngressRouteTCPInterface -type FakeIngressRouteTCPs struct { +// fakeIngressRouteTCPs implements IngressRouteTCPInterface +type fakeIngressRouteTCPs struct { + *gentype.FakeClientWithListAndApply[*v1alpha1.IngressRouteTCP, *v1alpha1.IngressRouteTCPList, *traefikiov1alpha1.IngressRouteTCPApplyConfiguration] Fake *FakeTraefikV1alpha1 - ns string } -var ingressroutetcpsResource = v1alpha1.SchemeGroupVersion.WithResource("ingressroutetcps") - -var ingressroutetcpsKind = v1alpha1.SchemeGroupVersion.WithKind("IngressRouteTCP") - -// Get takes name of the ingressRouteTCP, and returns the corresponding ingressRouteTCP object, and an error if there is any. -func (c *FakeIngressRouteTCPs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.IngressRouteTCP, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(ingressroutetcpsResource, c.ns, name), &v1alpha1.IngressRouteTCP{}) - - if obj == nil { - return nil, err +func newFakeIngressRouteTCPs(fake *FakeTraefikV1alpha1, namespace string) typedtraefikiov1alpha1.IngressRouteTCPInterface { + return &fakeIngressRouteTCPs{ + gentype.NewFakeClientWithListAndApply[*v1alpha1.IngressRouteTCP, *v1alpha1.IngressRouteTCPList, *traefikiov1alpha1.IngressRouteTCPApplyConfiguration]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("ingressroutetcps"), + v1alpha1.SchemeGroupVersion.WithKind("IngressRouteTCP"), + func() *v1alpha1.IngressRouteTCP { return &v1alpha1.IngressRouteTCP{} }, + func() *v1alpha1.IngressRouteTCPList { return &v1alpha1.IngressRouteTCPList{} }, + func(dst, src *v1alpha1.IngressRouteTCPList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.IngressRouteTCPList) []*v1alpha1.IngressRouteTCP { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1alpha1.IngressRouteTCPList, items []*v1alpha1.IngressRouteTCP) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.IngressRouteTCP), err -} - -// List takes label and field selectors, and returns the list of IngressRouteTCPs that match those selectors. -func (c *FakeIngressRouteTCPs) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.IngressRouteTCPList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(ingressroutetcpsResource, ingressroutetcpsKind, c.ns, opts), &v1alpha1.IngressRouteTCPList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.IngressRouteTCPList{ListMeta: obj.(*v1alpha1.IngressRouteTCPList).ListMeta} - for _, item := range obj.(*v1alpha1.IngressRouteTCPList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested ingressRouteTCPs. -func (c *FakeIngressRouteTCPs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(ingressroutetcpsResource, c.ns, opts)) - -} - -// Create takes the representation of a ingressRouteTCP and creates it. Returns the server's representation of the ingressRouteTCP, and an error, if there is any. -func (c *FakeIngressRouteTCPs) Create(ctx context.Context, ingressRouteTCP *v1alpha1.IngressRouteTCP, opts v1.CreateOptions) (result *v1alpha1.IngressRouteTCP, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(ingressroutetcpsResource, c.ns, ingressRouteTCP), &v1alpha1.IngressRouteTCP{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.IngressRouteTCP), err -} - -// Update takes the representation of a ingressRouteTCP and updates it. Returns the server's representation of the ingressRouteTCP, and an error, if there is any. -func (c *FakeIngressRouteTCPs) Update(ctx context.Context, ingressRouteTCP *v1alpha1.IngressRouteTCP, opts v1.UpdateOptions) (result *v1alpha1.IngressRouteTCP, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(ingressroutetcpsResource, c.ns, ingressRouteTCP), &v1alpha1.IngressRouteTCP{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.IngressRouteTCP), err -} - -// Delete takes name of the ingressRouteTCP and deletes it. Returns an error if one occurs. -func (c *FakeIngressRouteTCPs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(ingressroutetcpsResource, c.ns, name, opts), &v1alpha1.IngressRouteTCP{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeIngressRouteTCPs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(ingressroutetcpsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.IngressRouteTCPList{}) - return err -} - -// Patch applies the patch and returns the patched ingressRouteTCP. -func (c *FakeIngressRouteTCPs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.IngressRouteTCP, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(ingressroutetcpsResource, c.ns, name, pt, data, subresources...), &v1alpha1.IngressRouteTCP{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.IngressRouteTCP), err } diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_ingressrouteudp.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_ingressrouteudp.go index 34b448660..1523cce6a 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_ingressrouteudp.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_ingressrouteudp.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,111 +27,35 @@ THE SOFTWARE. package fake import ( - "context" - + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" + typedtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1" v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + gentype "k8s.io/client-go/gentype" ) -// FakeIngressRouteUDPs implements IngressRouteUDPInterface -type FakeIngressRouteUDPs struct { +// fakeIngressRouteUDPs implements IngressRouteUDPInterface +type fakeIngressRouteUDPs struct { + *gentype.FakeClientWithListAndApply[*v1alpha1.IngressRouteUDP, *v1alpha1.IngressRouteUDPList, *traefikiov1alpha1.IngressRouteUDPApplyConfiguration] Fake *FakeTraefikV1alpha1 - ns string } -var ingressrouteudpsResource = v1alpha1.SchemeGroupVersion.WithResource("ingressrouteudps") - -var ingressrouteudpsKind = v1alpha1.SchemeGroupVersion.WithKind("IngressRouteUDP") - -// Get takes name of the ingressRouteUDP, and returns the corresponding ingressRouteUDP object, and an error if there is any. -func (c *FakeIngressRouteUDPs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.IngressRouteUDP, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(ingressrouteudpsResource, c.ns, name), &v1alpha1.IngressRouteUDP{}) - - if obj == nil { - return nil, err +func newFakeIngressRouteUDPs(fake *FakeTraefikV1alpha1, namespace string) typedtraefikiov1alpha1.IngressRouteUDPInterface { + return &fakeIngressRouteUDPs{ + gentype.NewFakeClientWithListAndApply[*v1alpha1.IngressRouteUDP, *v1alpha1.IngressRouteUDPList, *traefikiov1alpha1.IngressRouteUDPApplyConfiguration]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("ingressrouteudps"), + v1alpha1.SchemeGroupVersion.WithKind("IngressRouteUDP"), + func() *v1alpha1.IngressRouteUDP { return &v1alpha1.IngressRouteUDP{} }, + func() *v1alpha1.IngressRouteUDPList { return &v1alpha1.IngressRouteUDPList{} }, + func(dst, src *v1alpha1.IngressRouteUDPList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.IngressRouteUDPList) []*v1alpha1.IngressRouteUDP { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1alpha1.IngressRouteUDPList, items []*v1alpha1.IngressRouteUDP) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.IngressRouteUDP), err -} - -// List takes label and field selectors, and returns the list of IngressRouteUDPs that match those selectors. -func (c *FakeIngressRouteUDPs) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.IngressRouteUDPList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(ingressrouteudpsResource, ingressrouteudpsKind, c.ns, opts), &v1alpha1.IngressRouteUDPList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.IngressRouteUDPList{ListMeta: obj.(*v1alpha1.IngressRouteUDPList).ListMeta} - for _, item := range obj.(*v1alpha1.IngressRouteUDPList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested ingressRouteUDPs. -func (c *FakeIngressRouteUDPs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(ingressrouteudpsResource, c.ns, opts)) - -} - -// Create takes the representation of a ingressRouteUDP and creates it. Returns the server's representation of the ingressRouteUDP, and an error, if there is any. -func (c *FakeIngressRouteUDPs) Create(ctx context.Context, ingressRouteUDP *v1alpha1.IngressRouteUDP, opts v1.CreateOptions) (result *v1alpha1.IngressRouteUDP, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(ingressrouteudpsResource, c.ns, ingressRouteUDP), &v1alpha1.IngressRouteUDP{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.IngressRouteUDP), err -} - -// Update takes the representation of a ingressRouteUDP and updates it. Returns the server's representation of the ingressRouteUDP, and an error, if there is any. -func (c *FakeIngressRouteUDPs) Update(ctx context.Context, ingressRouteUDP *v1alpha1.IngressRouteUDP, opts v1.UpdateOptions) (result *v1alpha1.IngressRouteUDP, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(ingressrouteudpsResource, c.ns, ingressRouteUDP), &v1alpha1.IngressRouteUDP{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.IngressRouteUDP), err -} - -// Delete takes name of the ingressRouteUDP and deletes it. Returns an error if one occurs. -func (c *FakeIngressRouteUDPs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(ingressrouteudpsResource, c.ns, name, opts), &v1alpha1.IngressRouteUDP{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeIngressRouteUDPs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(ingressrouteudpsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.IngressRouteUDPList{}) - return err -} - -// Patch applies the patch and returns the patched ingressRouteUDP. -func (c *FakeIngressRouteUDPs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.IngressRouteUDP, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(ingressrouteudpsResource, c.ns, name, pt, data, subresources...), &v1alpha1.IngressRouteUDP{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.IngressRouteUDP), err } diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_middleware.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_middleware.go index a27a91a4e..2d336991a 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_middleware.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_middleware.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,111 +27,33 @@ THE SOFTWARE. package fake import ( - "context" - + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" + typedtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1" v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + gentype "k8s.io/client-go/gentype" ) -// FakeMiddlewares implements MiddlewareInterface -type FakeMiddlewares struct { +// fakeMiddlewares implements MiddlewareInterface +type fakeMiddlewares struct { + *gentype.FakeClientWithListAndApply[*v1alpha1.Middleware, *v1alpha1.MiddlewareList, *traefikiov1alpha1.MiddlewareApplyConfiguration] Fake *FakeTraefikV1alpha1 - ns string } -var middlewaresResource = v1alpha1.SchemeGroupVersion.WithResource("middlewares") - -var middlewaresKind = v1alpha1.SchemeGroupVersion.WithKind("Middleware") - -// Get takes name of the middleware, and returns the corresponding middleware object, and an error if there is any. -func (c *FakeMiddlewares) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Middleware, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(middlewaresResource, c.ns, name), &v1alpha1.Middleware{}) - - if obj == nil { - return nil, err +func newFakeMiddlewares(fake *FakeTraefikV1alpha1, namespace string) typedtraefikiov1alpha1.MiddlewareInterface { + return &fakeMiddlewares{ + gentype.NewFakeClientWithListAndApply[*v1alpha1.Middleware, *v1alpha1.MiddlewareList, *traefikiov1alpha1.MiddlewareApplyConfiguration]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("middlewares"), + v1alpha1.SchemeGroupVersion.WithKind("Middleware"), + func() *v1alpha1.Middleware { return &v1alpha1.Middleware{} }, + func() *v1alpha1.MiddlewareList { return &v1alpha1.MiddlewareList{} }, + func(dst, src *v1alpha1.MiddlewareList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.MiddlewareList) []*v1alpha1.Middleware { return gentype.ToPointerSlice(list.Items) }, + func(list *v1alpha1.MiddlewareList, items []*v1alpha1.Middleware) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.Middleware), err -} - -// List takes label and field selectors, and returns the list of Middlewares that match those selectors. -func (c *FakeMiddlewares) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.MiddlewareList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(middlewaresResource, middlewaresKind, c.ns, opts), &v1alpha1.MiddlewareList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.MiddlewareList{ListMeta: obj.(*v1alpha1.MiddlewareList).ListMeta} - for _, item := range obj.(*v1alpha1.MiddlewareList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested middlewares. -func (c *FakeMiddlewares) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(middlewaresResource, c.ns, opts)) - -} - -// Create takes the representation of a middleware and creates it. Returns the server's representation of the middleware, and an error, if there is any. -func (c *FakeMiddlewares) Create(ctx context.Context, middleware *v1alpha1.Middleware, opts v1.CreateOptions) (result *v1alpha1.Middleware, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(middlewaresResource, c.ns, middleware), &v1alpha1.Middleware{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Middleware), err -} - -// Update takes the representation of a middleware and updates it. Returns the server's representation of the middleware, and an error, if there is any. -func (c *FakeMiddlewares) Update(ctx context.Context, middleware *v1alpha1.Middleware, opts v1.UpdateOptions) (result *v1alpha1.Middleware, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(middlewaresResource, c.ns, middleware), &v1alpha1.Middleware{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Middleware), err -} - -// Delete takes name of the middleware and deletes it. Returns an error if one occurs. -func (c *FakeMiddlewares) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(middlewaresResource, c.ns, name, opts), &v1alpha1.Middleware{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeMiddlewares) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(middlewaresResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.MiddlewareList{}) - return err -} - -// Patch applies the patch and returns the patched middleware. -func (c *FakeMiddlewares) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Middleware, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(middlewaresResource, c.ns, name, pt, data, subresources...), &v1alpha1.Middleware{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Middleware), err } diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_middlewaretcp.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_middlewaretcp.go index 4a01eba10..db27a281d 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_middlewaretcp.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_middlewaretcp.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,111 +27,35 @@ THE SOFTWARE. package fake import ( - "context" - + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" + typedtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1" v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + gentype "k8s.io/client-go/gentype" ) -// FakeMiddlewareTCPs implements MiddlewareTCPInterface -type FakeMiddlewareTCPs struct { +// fakeMiddlewareTCPs implements MiddlewareTCPInterface +type fakeMiddlewareTCPs struct { + *gentype.FakeClientWithListAndApply[*v1alpha1.MiddlewareTCP, *v1alpha1.MiddlewareTCPList, *traefikiov1alpha1.MiddlewareTCPApplyConfiguration] Fake *FakeTraefikV1alpha1 - ns string } -var middlewaretcpsResource = v1alpha1.SchemeGroupVersion.WithResource("middlewaretcps") - -var middlewaretcpsKind = v1alpha1.SchemeGroupVersion.WithKind("MiddlewareTCP") - -// Get takes name of the middlewareTCP, and returns the corresponding middlewareTCP object, and an error if there is any. -func (c *FakeMiddlewareTCPs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.MiddlewareTCP, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(middlewaretcpsResource, c.ns, name), &v1alpha1.MiddlewareTCP{}) - - if obj == nil { - return nil, err +func newFakeMiddlewareTCPs(fake *FakeTraefikV1alpha1, namespace string) typedtraefikiov1alpha1.MiddlewareTCPInterface { + return &fakeMiddlewareTCPs{ + gentype.NewFakeClientWithListAndApply[*v1alpha1.MiddlewareTCP, *v1alpha1.MiddlewareTCPList, *traefikiov1alpha1.MiddlewareTCPApplyConfiguration]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("middlewaretcps"), + v1alpha1.SchemeGroupVersion.WithKind("MiddlewareTCP"), + func() *v1alpha1.MiddlewareTCP { return &v1alpha1.MiddlewareTCP{} }, + func() *v1alpha1.MiddlewareTCPList { return &v1alpha1.MiddlewareTCPList{} }, + func(dst, src *v1alpha1.MiddlewareTCPList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.MiddlewareTCPList) []*v1alpha1.MiddlewareTCP { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1alpha1.MiddlewareTCPList, items []*v1alpha1.MiddlewareTCP) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.MiddlewareTCP), err -} - -// List takes label and field selectors, and returns the list of MiddlewareTCPs that match those selectors. -func (c *FakeMiddlewareTCPs) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.MiddlewareTCPList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(middlewaretcpsResource, middlewaretcpsKind, c.ns, opts), &v1alpha1.MiddlewareTCPList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.MiddlewareTCPList{ListMeta: obj.(*v1alpha1.MiddlewareTCPList).ListMeta} - for _, item := range obj.(*v1alpha1.MiddlewareTCPList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested middlewareTCPs. -func (c *FakeMiddlewareTCPs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(middlewaretcpsResource, c.ns, opts)) - -} - -// Create takes the representation of a middlewareTCP and creates it. Returns the server's representation of the middlewareTCP, and an error, if there is any. -func (c *FakeMiddlewareTCPs) Create(ctx context.Context, middlewareTCP *v1alpha1.MiddlewareTCP, opts v1.CreateOptions) (result *v1alpha1.MiddlewareTCP, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(middlewaretcpsResource, c.ns, middlewareTCP), &v1alpha1.MiddlewareTCP{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.MiddlewareTCP), err -} - -// Update takes the representation of a middlewareTCP and updates it. Returns the server's representation of the middlewareTCP, and an error, if there is any. -func (c *FakeMiddlewareTCPs) Update(ctx context.Context, middlewareTCP *v1alpha1.MiddlewareTCP, opts v1.UpdateOptions) (result *v1alpha1.MiddlewareTCP, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(middlewaretcpsResource, c.ns, middlewareTCP), &v1alpha1.MiddlewareTCP{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.MiddlewareTCP), err -} - -// Delete takes name of the middlewareTCP and deletes it. Returns an error if one occurs. -func (c *FakeMiddlewareTCPs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(middlewaretcpsResource, c.ns, name, opts), &v1alpha1.MiddlewareTCP{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeMiddlewareTCPs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(middlewaretcpsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.MiddlewareTCPList{}) - return err -} - -// Patch applies the patch and returns the patched middlewareTCP. -func (c *FakeMiddlewareTCPs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.MiddlewareTCP, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(middlewaretcpsResource, c.ns, name, pt, data, subresources...), &v1alpha1.MiddlewareTCP{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.MiddlewareTCP), err } diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_serverstransport.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_serverstransport.go index 562e5cc1f..85719e75e 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_serverstransport.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_serverstransport.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,111 +27,35 @@ THE SOFTWARE. package fake import ( - "context" - + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" + typedtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1" v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + gentype "k8s.io/client-go/gentype" ) -// FakeServersTransports implements ServersTransportInterface -type FakeServersTransports struct { +// fakeServersTransports implements ServersTransportInterface +type fakeServersTransports struct { + *gentype.FakeClientWithListAndApply[*v1alpha1.ServersTransport, *v1alpha1.ServersTransportList, *traefikiov1alpha1.ServersTransportApplyConfiguration] Fake *FakeTraefikV1alpha1 - ns string } -var serverstransportsResource = v1alpha1.SchemeGroupVersion.WithResource("serverstransports") - -var serverstransportsKind = v1alpha1.SchemeGroupVersion.WithKind("ServersTransport") - -// Get takes name of the serversTransport, and returns the corresponding serversTransport object, and an error if there is any. -func (c *FakeServersTransports) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ServersTransport, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(serverstransportsResource, c.ns, name), &v1alpha1.ServersTransport{}) - - if obj == nil { - return nil, err +func newFakeServersTransports(fake *FakeTraefikV1alpha1, namespace string) typedtraefikiov1alpha1.ServersTransportInterface { + return &fakeServersTransports{ + gentype.NewFakeClientWithListAndApply[*v1alpha1.ServersTransport, *v1alpha1.ServersTransportList, *traefikiov1alpha1.ServersTransportApplyConfiguration]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("serverstransports"), + v1alpha1.SchemeGroupVersion.WithKind("ServersTransport"), + func() *v1alpha1.ServersTransport { return &v1alpha1.ServersTransport{} }, + func() *v1alpha1.ServersTransportList { return &v1alpha1.ServersTransportList{} }, + func(dst, src *v1alpha1.ServersTransportList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.ServersTransportList) []*v1alpha1.ServersTransport { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1alpha1.ServersTransportList, items []*v1alpha1.ServersTransport) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.ServersTransport), err -} - -// List takes label and field selectors, and returns the list of ServersTransports that match those selectors. -func (c *FakeServersTransports) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ServersTransportList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(serverstransportsResource, serverstransportsKind, c.ns, opts), &v1alpha1.ServersTransportList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.ServersTransportList{ListMeta: obj.(*v1alpha1.ServersTransportList).ListMeta} - for _, item := range obj.(*v1alpha1.ServersTransportList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested serversTransports. -func (c *FakeServersTransports) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(serverstransportsResource, c.ns, opts)) - -} - -// Create takes the representation of a serversTransport and creates it. Returns the server's representation of the serversTransport, and an error, if there is any. -func (c *FakeServersTransports) Create(ctx context.Context, serversTransport *v1alpha1.ServersTransport, opts v1.CreateOptions) (result *v1alpha1.ServersTransport, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(serverstransportsResource, c.ns, serversTransport), &v1alpha1.ServersTransport{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ServersTransport), err -} - -// Update takes the representation of a serversTransport and updates it. Returns the server's representation of the serversTransport, and an error, if there is any. -func (c *FakeServersTransports) Update(ctx context.Context, serversTransport *v1alpha1.ServersTransport, opts v1.UpdateOptions) (result *v1alpha1.ServersTransport, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(serverstransportsResource, c.ns, serversTransport), &v1alpha1.ServersTransport{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ServersTransport), err -} - -// Delete takes name of the serversTransport and deletes it. Returns an error if one occurs. -func (c *FakeServersTransports) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(serverstransportsResource, c.ns, name, opts), &v1alpha1.ServersTransport{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeServersTransports) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(serverstransportsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.ServersTransportList{}) - return err -} - -// Patch applies the patch and returns the patched serversTransport. -func (c *FakeServersTransports) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServersTransport, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(serverstransportsResource, c.ns, name, pt, data, subresources...), &v1alpha1.ServersTransport{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ServersTransport), err } diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_serverstransporttcp.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_serverstransporttcp.go index 878e2e5dd..484fcb03e 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_serverstransporttcp.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_serverstransporttcp.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,111 +27,35 @@ THE SOFTWARE. package fake import ( - "context" - + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" + typedtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1" v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + gentype "k8s.io/client-go/gentype" ) -// FakeServersTransportTCPs implements ServersTransportTCPInterface -type FakeServersTransportTCPs struct { +// fakeServersTransportTCPs implements ServersTransportTCPInterface +type fakeServersTransportTCPs struct { + *gentype.FakeClientWithListAndApply[*v1alpha1.ServersTransportTCP, *v1alpha1.ServersTransportTCPList, *traefikiov1alpha1.ServersTransportTCPApplyConfiguration] Fake *FakeTraefikV1alpha1 - ns string } -var serverstransporttcpsResource = v1alpha1.SchemeGroupVersion.WithResource("serverstransporttcps") - -var serverstransporttcpsKind = v1alpha1.SchemeGroupVersion.WithKind("ServersTransportTCP") - -// Get takes name of the serversTransportTCP, and returns the corresponding serversTransportTCP object, and an error if there is any. -func (c *FakeServersTransportTCPs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ServersTransportTCP, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(serverstransporttcpsResource, c.ns, name), &v1alpha1.ServersTransportTCP{}) - - if obj == nil { - return nil, err +func newFakeServersTransportTCPs(fake *FakeTraefikV1alpha1, namespace string) typedtraefikiov1alpha1.ServersTransportTCPInterface { + return &fakeServersTransportTCPs{ + gentype.NewFakeClientWithListAndApply[*v1alpha1.ServersTransportTCP, *v1alpha1.ServersTransportTCPList, *traefikiov1alpha1.ServersTransportTCPApplyConfiguration]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("serverstransporttcps"), + v1alpha1.SchemeGroupVersion.WithKind("ServersTransportTCP"), + func() *v1alpha1.ServersTransportTCP { return &v1alpha1.ServersTransportTCP{} }, + func() *v1alpha1.ServersTransportTCPList { return &v1alpha1.ServersTransportTCPList{} }, + func(dst, src *v1alpha1.ServersTransportTCPList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.ServersTransportTCPList) []*v1alpha1.ServersTransportTCP { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1alpha1.ServersTransportTCPList, items []*v1alpha1.ServersTransportTCP) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.ServersTransportTCP), err -} - -// List takes label and field selectors, and returns the list of ServersTransportTCPs that match those selectors. -func (c *FakeServersTransportTCPs) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ServersTransportTCPList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(serverstransporttcpsResource, serverstransporttcpsKind, c.ns, opts), &v1alpha1.ServersTransportTCPList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.ServersTransportTCPList{ListMeta: obj.(*v1alpha1.ServersTransportTCPList).ListMeta} - for _, item := range obj.(*v1alpha1.ServersTransportTCPList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested serversTransportTCPs. -func (c *FakeServersTransportTCPs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(serverstransporttcpsResource, c.ns, opts)) - -} - -// Create takes the representation of a serversTransportTCP and creates it. Returns the server's representation of the serversTransportTCP, and an error, if there is any. -func (c *FakeServersTransportTCPs) Create(ctx context.Context, serversTransportTCP *v1alpha1.ServersTransportTCP, opts v1.CreateOptions) (result *v1alpha1.ServersTransportTCP, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(serverstransporttcpsResource, c.ns, serversTransportTCP), &v1alpha1.ServersTransportTCP{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ServersTransportTCP), err -} - -// Update takes the representation of a serversTransportTCP and updates it. Returns the server's representation of the serversTransportTCP, and an error, if there is any. -func (c *FakeServersTransportTCPs) Update(ctx context.Context, serversTransportTCP *v1alpha1.ServersTransportTCP, opts v1.UpdateOptions) (result *v1alpha1.ServersTransportTCP, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(serverstransporttcpsResource, c.ns, serversTransportTCP), &v1alpha1.ServersTransportTCP{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ServersTransportTCP), err -} - -// Delete takes name of the serversTransportTCP and deletes it. Returns an error if one occurs. -func (c *FakeServersTransportTCPs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(serverstransporttcpsResource, c.ns, name, opts), &v1alpha1.ServersTransportTCP{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeServersTransportTCPs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(serverstransporttcpsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.ServersTransportTCPList{}) - return err -} - -// Patch applies the patch and returns the patched serversTransportTCP. -func (c *FakeServersTransportTCPs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServersTransportTCP, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(serverstransporttcpsResource, c.ns, name, pt, data, subresources...), &v1alpha1.ServersTransportTCP{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ServersTransportTCP), err } diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_tlsoption.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_tlsoption.go index be344614f..0bef0dde5 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_tlsoption.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_tlsoption.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,111 +27,33 @@ THE SOFTWARE. package fake import ( - "context" - + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" + typedtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1" v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + gentype "k8s.io/client-go/gentype" ) -// FakeTLSOptions implements TLSOptionInterface -type FakeTLSOptions struct { +// fakeTLSOptions implements TLSOptionInterface +type fakeTLSOptions struct { + *gentype.FakeClientWithListAndApply[*v1alpha1.TLSOption, *v1alpha1.TLSOptionList, *traefikiov1alpha1.TLSOptionApplyConfiguration] Fake *FakeTraefikV1alpha1 - ns string } -var tlsoptionsResource = v1alpha1.SchemeGroupVersion.WithResource("tlsoptions") - -var tlsoptionsKind = v1alpha1.SchemeGroupVersion.WithKind("TLSOption") - -// Get takes name of the tLSOption, and returns the corresponding tLSOption object, and an error if there is any. -func (c *FakeTLSOptions) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.TLSOption, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(tlsoptionsResource, c.ns, name), &v1alpha1.TLSOption{}) - - if obj == nil { - return nil, err +func newFakeTLSOptions(fake *FakeTraefikV1alpha1, namespace string) typedtraefikiov1alpha1.TLSOptionInterface { + return &fakeTLSOptions{ + gentype.NewFakeClientWithListAndApply[*v1alpha1.TLSOption, *v1alpha1.TLSOptionList, *traefikiov1alpha1.TLSOptionApplyConfiguration]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("tlsoptions"), + v1alpha1.SchemeGroupVersion.WithKind("TLSOption"), + func() *v1alpha1.TLSOption { return &v1alpha1.TLSOption{} }, + func() *v1alpha1.TLSOptionList { return &v1alpha1.TLSOptionList{} }, + func(dst, src *v1alpha1.TLSOptionList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.TLSOptionList) []*v1alpha1.TLSOption { return gentype.ToPointerSlice(list.Items) }, + func(list *v1alpha1.TLSOptionList, items []*v1alpha1.TLSOption) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.TLSOption), err -} - -// List takes label and field selectors, and returns the list of TLSOptions that match those selectors. -func (c *FakeTLSOptions) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.TLSOptionList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(tlsoptionsResource, tlsoptionsKind, c.ns, opts), &v1alpha1.TLSOptionList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.TLSOptionList{ListMeta: obj.(*v1alpha1.TLSOptionList).ListMeta} - for _, item := range obj.(*v1alpha1.TLSOptionList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested tLSOptions. -func (c *FakeTLSOptions) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(tlsoptionsResource, c.ns, opts)) - -} - -// Create takes the representation of a tLSOption and creates it. Returns the server's representation of the tLSOption, and an error, if there is any. -func (c *FakeTLSOptions) Create(ctx context.Context, tLSOption *v1alpha1.TLSOption, opts v1.CreateOptions) (result *v1alpha1.TLSOption, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(tlsoptionsResource, c.ns, tLSOption), &v1alpha1.TLSOption{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.TLSOption), err -} - -// Update takes the representation of a tLSOption and updates it. Returns the server's representation of the tLSOption, and an error, if there is any. -func (c *FakeTLSOptions) Update(ctx context.Context, tLSOption *v1alpha1.TLSOption, opts v1.UpdateOptions) (result *v1alpha1.TLSOption, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(tlsoptionsResource, c.ns, tLSOption), &v1alpha1.TLSOption{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.TLSOption), err -} - -// Delete takes name of the tLSOption and deletes it. Returns an error if one occurs. -func (c *FakeTLSOptions) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(tlsoptionsResource, c.ns, name, opts), &v1alpha1.TLSOption{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeTLSOptions) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(tlsoptionsResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.TLSOptionList{}) - return err -} - -// Patch applies the patch and returns the patched tLSOption. -func (c *FakeTLSOptions) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TLSOption, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(tlsoptionsResource, c.ns, name, pt, data, subresources...), &v1alpha1.TLSOption{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.TLSOption), err } diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_tlsstore.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_tlsstore.go index a779aaa48..55abfda0d 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_tlsstore.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_tlsstore.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,111 +27,33 @@ THE SOFTWARE. package fake import ( - "context" - + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" + typedtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1" v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + gentype "k8s.io/client-go/gentype" ) -// FakeTLSStores implements TLSStoreInterface -type FakeTLSStores struct { +// fakeTLSStores implements TLSStoreInterface +type fakeTLSStores struct { + *gentype.FakeClientWithListAndApply[*v1alpha1.TLSStore, *v1alpha1.TLSStoreList, *traefikiov1alpha1.TLSStoreApplyConfiguration] Fake *FakeTraefikV1alpha1 - ns string } -var tlsstoresResource = v1alpha1.SchemeGroupVersion.WithResource("tlsstores") - -var tlsstoresKind = v1alpha1.SchemeGroupVersion.WithKind("TLSStore") - -// Get takes name of the tLSStore, and returns the corresponding tLSStore object, and an error if there is any. -func (c *FakeTLSStores) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.TLSStore, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(tlsstoresResource, c.ns, name), &v1alpha1.TLSStore{}) - - if obj == nil { - return nil, err +func newFakeTLSStores(fake *FakeTraefikV1alpha1, namespace string) typedtraefikiov1alpha1.TLSStoreInterface { + return &fakeTLSStores{ + gentype.NewFakeClientWithListAndApply[*v1alpha1.TLSStore, *v1alpha1.TLSStoreList, *traefikiov1alpha1.TLSStoreApplyConfiguration]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("tlsstores"), + v1alpha1.SchemeGroupVersion.WithKind("TLSStore"), + func() *v1alpha1.TLSStore { return &v1alpha1.TLSStore{} }, + func() *v1alpha1.TLSStoreList { return &v1alpha1.TLSStoreList{} }, + func(dst, src *v1alpha1.TLSStoreList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.TLSStoreList) []*v1alpha1.TLSStore { return gentype.ToPointerSlice(list.Items) }, + func(list *v1alpha1.TLSStoreList, items []*v1alpha1.TLSStore) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.TLSStore), err -} - -// List takes label and field selectors, and returns the list of TLSStores that match those selectors. -func (c *FakeTLSStores) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.TLSStoreList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(tlsstoresResource, tlsstoresKind, c.ns, opts), &v1alpha1.TLSStoreList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.TLSStoreList{ListMeta: obj.(*v1alpha1.TLSStoreList).ListMeta} - for _, item := range obj.(*v1alpha1.TLSStoreList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested tLSStores. -func (c *FakeTLSStores) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(tlsstoresResource, c.ns, opts)) - -} - -// Create takes the representation of a tLSStore and creates it. Returns the server's representation of the tLSStore, and an error, if there is any. -func (c *FakeTLSStores) Create(ctx context.Context, tLSStore *v1alpha1.TLSStore, opts v1.CreateOptions) (result *v1alpha1.TLSStore, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(tlsstoresResource, c.ns, tLSStore), &v1alpha1.TLSStore{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.TLSStore), err -} - -// Update takes the representation of a tLSStore and updates it. Returns the server's representation of the tLSStore, and an error, if there is any. -func (c *FakeTLSStores) Update(ctx context.Context, tLSStore *v1alpha1.TLSStore, opts v1.UpdateOptions) (result *v1alpha1.TLSStore, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(tlsstoresResource, c.ns, tLSStore), &v1alpha1.TLSStore{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.TLSStore), err -} - -// Delete takes name of the tLSStore and deletes it. Returns an error if one occurs. -func (c *FakeTLSStores) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(tlsstoresResource, c.ns, name, opts), &v1alpha1.TLSStore{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeTLSStores) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(tlsstoresResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.TLSStoreList{}) - return err -} - -// Patch applies the patch and returns the patched tLSStore. -func (c *FakeTLSStores) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TLSStore, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(tlsstoresResource, c.ns, name, pt, data, subresources...), &v1alpha1.TLSStore{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.TLSStore), err } diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_traefikio_client.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_traefikio_client.go index 2d9b5bb91..fa3eab775 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_traefikio_client.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_traefikio_client.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -37,43 +37,43 @@ type FakeTraefikV1alpha1 struct { } func (c *FakeTraefikV1alpha1) IngressRoutes(namespace string) v1alpha1.IngressRouteInterface { - return &FakeIngressRoutes{c, namespace} + return newFakeIngressRoutes(c, namespace) } func (c *FakeTraefikV1alpha1) IngressRouteTCPs(namespace string) v1alpha1.IngressRouteTCPInterface { - return &FakeIngressRouteTCPs{c, namespace} + return newFakeIngressRouteTCPs(c, namespace) } func (c *FakeTraefikV1alpha1) IngressRouteUDPs(namespace string) v1alpha1.IngressRouteUDPInterface { - return &FakeIngressRouteUDPs{c, namespace} + return newFakeIngressRouteUDPs(c, namespace) } func (c *FakeTraefikV1alpha1) Middlewares(namespace string) v1alpha1.MiddlewareInterface { - return &FakeMiddlewares{c, namespace} + return newFakeMiddlewares(c, namespace) } func (c *FakeTraefikV1alpha1) MiddlewareTCPs(namespace string) v1alpha1.MiddlewareTCPInterface { - return &FakeMiddlewareTCPs{c, namespace} + return newFakeMiddlewareTCPs(c, namespace) } func (c *FakeTraefikV1alpha1) ServersTransports(namespace string) v1alpha1.ServersTransportInterface { - return &FakeServersTransports{c, namespace} + return newFakeServersTransports(c, namespace) } func (c *FakeTraefikV1alpha1) ServersTransportTCPs(namespace string) v1alpha1.ServersTransportTCPInterface { - return &FakeServersTransportTCPs{c, namespace} + return newFakeServersTransportTCPs(c, namespace) } func (c *FakeTraefikV1alpha1) TLSOptions(namespace string) v1alpha1.TLSOptionInterface { - return &FakeTLSOptions{c, namespace} + return newFakeTLSOptions(c, namespace) } func (c *FakeTraefikV1alpha1) TLSStores(namespace string) v1alpha1.TLSStoreInterface { - return &FakeTLSStores{c, namespace} + return newFakeTLSStores(c, namespace) } func (c *FakeTraefikV1alpha1) TraefikServices(namespace string) v1alpha1.TraefikServiceInterface { - return &FakeTraefikServices{c, namespace} + return newFakeTraefikServices(c, namespace) } // RESTClient returns a RESTClient that is used to communicate diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_traefikservice.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_traefikservice.go index d5ad869fa..d9afee423 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_traefikservice.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/fake/fake_traefikservice.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,111 +27,35 @@ THE SOFTWARE. package fake import ( - "context" - + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" + typedtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1" v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" + gentype "k8s.io/client-go/gentype" ) -// FakeTraefikServices implements TraefikServiceInterface -type FakeTraefikServices struct { +// fakeTraefikServices implements TraefikServiceInterface +type fakeTraefikServices struct { + *gentype.FakeClientWithListAndApply[*v1alpha1.TraefikService, *v1alpha1.TraefikServiceList, *traefikiov1alpha1.TraefikServiceApplyConfiguration] Fake *FakeTraefikV1alpha1 - ns string } -var traefikservicesResource = v1alpha1.SchemeGroupVersion.WithResource("traefikservices") - -var traefikservicesKind = v1alpha1.SchemeGroupVersion.WithKind("TraefikService") - -// Get takes name of the traefikService, and returns the corresponding traefikService object, and an error if there is any. -func (c *FakeTraefikServices) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.TraefikService, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(traefikservicesResource, c.ns, name), &v1alpha1.TraefikService{}) - - if obj == nil { - return nil, err +func newFakeTraefikServices(fake *FakeTraefikV1alpha1, namespace string) typedtraefikiov1alpha1.TraefikServiceInterface { + return &fakeTraefikServices{ + gentype.NewFakeClientWithListAndApply[*v1alpha1.TraefikService, *v1alpha1.TraefikServiceList, *traefikiov1alpha1.TraefikServiceApplyConfiguration]( + fake.Fake, + namespace, + v1alpha1.SchemeGroupVersion.WithResource("traefikservices"), + v1alpha1.SchemeGroupVersion.WithKind("TraefikService"), + func() *v1alpha1.TraefikService { return &v1alpha1.TraefikService{} }, + func() *v1alpha1.TraefikServiceList { return &v1alpha1.TraefikServiceList{} }, + func(dst, src *v1alpha1.TraefikServiceList) { dst.ListMeta = src.ListMeta }, + func(list *v1alpha1.TraefikServiceList) []*v1alpha1.TraefikService { + return gentype.ToPointerSlice(list.Items) + }, + func(list *v1alpha1.TraefikServiceList, items []*v1alpha1.TraefikService) { + list.Items = gentype.FromPointerSlice(items) + }, + ), + fake, } - return obj.(*v1alpha1.TraefikService), err -} - -// List takes label and field selectors, and returns the list of TraefikServices that match those selectors. -func (c *FakeTraefikServices) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.TraefikServiceList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(traefikservicesResource, traefikservicesKind, c.ns, opts), &v1alpha1.TraefikServiceList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.TraefikServiceList{ListMeta: obj.(*v1alpha1.TraefikServiceList).ListMeta} - for _, item := range obj.(*v1alpha1.TraefikServiceList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested traefikServices. -func (c *FakeTraefikServices) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(traefikservicesResource, c.ns, opts)) - -} - -// Create takes the representation of a traefikService and creates it. Returns the server's representation of the traefikService, and an error, if there is any. -func (c *FakeTraefikServices) Create(ctx context.Context, traefikService *v1alpha1.TraefikService, opts v1.CreateOptions) (result *v1alpha1.TraefikService, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(traefikservicesResource, c.ns, traefikService), &v1alpha1.TraefikService{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.TraefikService), err -} - -// Update takes the representation of a traefikService and updates it. Returns the server's representation of the traefikService, and an error, if there is any. -func (c *FakeTraefikServices) Update(ctx context.Context, traefikService *v1alpha1.TraefikService, opts v1.UpdateOptions) (result *v1alpha1.TraefikService, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(traefikservicesResource, c.ns, traefikService), &v1alpha1.TraefikService{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.TraefikService), err -} - -// Delete takes name of the traefikService and deletes it. Returns an error if one occurs. -func (c *FakeTraefikServices) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteActionWithOptions(traefikservicesResource, c.ns, name, opts), &v1alpha1.TraefikService{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeTraefikServices) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(traefikservicesResource, c.ns, listOpts) - - _, err := c.Fake.Invokes(action, &v1alpha1.TraefikServiceList{}) - return err -} - -// Patch applies the patch and returns the patched traefikService. -func (c *FakeTraefikServices) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TraefikService, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(traefikservicesResource, c.ns, name, pt, data, subresources...), &v1alpha1.TraefikService{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.TraefikService), err } diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/generated_expansion.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/generated_expansion.go index 0de236543..527010c45 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/generated_expansion.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/generated_expansion.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/ingressroute.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/ingressroute.go index 7370d551f..66cd158a0 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/ingressroute.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/ingressroute.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,15 +27,15 @@ THE SOFTWARE. package v1alpha1 import ( - "context" - "time" + context "context" + applyconfigurationtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" scheme "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // IngressRoutesGetter has a method to return a IngressRouteInterface. @@ -46,141 +46,33 @@ type IngressRoutesGetter interface { // IngressRouteInterface has methods to work with IngressRoute resources. type IngressRouteInterface interface { - Create(ctx context.Context, ingressRoute *v1alpha1.IngressRoute, opts v1.CreateOptions) (*v1alpha1.IngressRoute, error) - Update(ctx context.Context, ingressRoute *v1alpha1.IngressRoute, opts v1.UpdateOptions) (*v1alpha1.IngressRoute, error) + Create(ctx context.Context, ingressRoute *traefikiov1alpha1.IngressRoute, opts v1.CreateOptions) (*traefikiov1alpha1.IngressRoute, error) + Update(ctx context.Context, ingressRoute *traefikiov1alpha1.IngressRoute, opts v1.UpdateOptions) (*traefikiov1alpha1.IngressRoute, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.IngressRoute, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.IngressRouteList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*traefikiov1alpha1.IngressRoute, error) + List(ctx context.Context, opts v1.ListOptions) (*traefikiov1alpha1.IngressRouteList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.IngressRoute, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *traefikiov1alpha1.IngressRoute, err error) + Apply(ctx context.Context, ingressRoute *applyconfigurationtraefikiov1alpha1.IngressRouteApplyConfiguration, opts v1.ApplyOptions) (result *traefikiov1alpha1.IngressRoute, err error) IngressRouteExpansion } // ingressRoutes implements IngressRouteInterface type ingressRoutes struct { - client rest.Interface - ns string + *gentype.ClientWithListAndApply[*traefikiov1alpha1.IngressRoute, *traefikiov1alpha1.IngressRouteList, *applyconfigurationtraefikiov1alpha1.IngressRouteApplyConfiguration] } // newIngressRoutes returns a IngressRoutes func newIngressRoutes(c *TraefikV1alpha1Client, namespace string) *ingressRoutes { return &ingressRoutes{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithListAndApply[*traefikiov1alpha1.IngressRoute, *traefikiov1alpha1.IngressRouteList, *applyconfigurationtraefikiov1alpha1.IngressRouteApplyConfiguration]( + "ingressroutes", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *traefikiov1alpha1.IngressRoute { return &traefikiov1alpha1.IngressRoute{} }, + func() *traefikiov1alpha1.IngressRouteList { return &traefikiov1alpha1.IngressRouteList{} }, + ), } } - -// Get takes name of the ingressRoute, and returns the corresponding ingressRoute object, and an error if there is any. -func (c *ingressRoutes) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.IngressRoute, err error) { - result = &v1alpha1.IngressRoute{} - err = c.client.Get(). - Namespace(c.ns). - Resource("ingressroutes"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of IngressRoutes that match those selectors. -func (c *ingressRoutes) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.IngressRouteList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.IngressRouteList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("ingressroutes"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested ingressRoutes. -func (c *ingressRoutes) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("ingressroutes"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a ingressRoute and creates it. Returns the server's representation of the ingressRoute, and an error, if there is any. -func (c *ingressRoutes) Create(ctx context.Context, ingressRoute *v1alpha1.IngressRoute, opts v1.CreateOptions) (result *v1alpha1.IngressRoute, err error) { - result = &v1alpha1.IngressRoute{} - err = c.client.Post(). - Namespace(c.ns). - Resource("ingressroutes"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(ingressRoute). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a ingressRoute and updates it. Returns the server's representation of the ingressRoute, and an error, if there is any. -func (c *ingressRoutes) Update(ctx context.Context, ingressRoute *v1alpha1.IngressRoute, opts v1.UpdateOptions) (result *v1alpha1.IngressRoute, err error) { - result = &v1alpha1.IngressRoute{} - err = c.client.Put(). - Namespace(c.ns). - Resource("ingressroutes"). - Name(ingressRoute.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(ingressRoute). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the ingressRoute and deletes it. Returns an error if one occurs. -func (c *ingressRoutes) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("ingressroutes"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *ingressRoutes) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("ingressroutes"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched ingressRoute. -func (c *ingressRoutes) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.IngressRoute, err error) { - result = &v1alpha1.IngressRoute{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("ingressroutes"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/ingressroutetcp.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/ingressroutetcp.go index 7255228fe..7449553cc 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/ingressroutetcp.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/ingressroutetcp.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,15 +27,15 @@ THE SOFTWARE. package v1alpha1 import ( - "context" - "time" + context "context" + applyconfigurationtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" scheme "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // IngressRouteTCPsGetter has a method to return a IngressRouteTCPInterface. @@ -46,141 +46,33 @@ type IngressRouteTCPsGetter interface { // IngressRouteTCPInterface has methods to work with IngressRouteTCP resources. type IngressRouteTCPInterface interface { - Create(ctx context.Context, ingressRouteTCP *v1alpha1.IngressRouteTCP, opts v1.CreateOptions) (*v1alpha1.IngressRouteTCP, error) - Update(ctx context.Context, ingressRouteTCP *v1alpha1.IngressRouteTCP, opts v1.UpdateOptions) (*v1alpha1.IngressRouteTCP, error) + Create(ctx context.Context, ingressRouteTCP *traefikiov1alpha1.IngressRouteTCP, opts v1.CreateOptions) (*traefikiov1alpha1.IngressRouteTCP, error) + Update(ctx context.Context, ingressRouteTCP *traefikiov1alpha1.IngressRouteTCP, opts v1.UpdateOptions) (*traefikiov1alpha1.IngressRouteTCP, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.IngressRouteTCP, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.IngressRouteTCPList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*traefikiov1alpha1.IngressRouteTCP, error) + List(ctx context.Context, opts v1.ListOptions) (*traefikiov1alpha1.IngressRouteTCPList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.IngressRouteTCP, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *traefikiov1alpha1.IngressRouteTCP, err error) + Apply(ctx context.Context, ingressRouteTCP *applyconfigurationtraefikiov1alpha1.IngressRouteTCPApplyConfiguration, opts v1.ApplyOptions) (result *traefikiov1alpha1.IngressRouteTCP, err error) IngressRouteTCPExpansion } // ingressRouteTCPs implements IngressRouteTCPInterface type ingressRouteTCPs struct { - client rest.Interface - ns string + *gentype.ClientWithListAndApply[*traefikiov1alpha1.IngressRouteTCP, *traefikiov1alpha1.IngressRouteTCPList, *applyconfigurationtraefikiov1alpha1.IngressRouteTCPApplyConfiguration] } // newIngressRouteTCPs returns a IngressRouteTCPs func newIngressRouteTCPs(c *TraefikV1alpha1Client, namespace string) *ingressRouteTCPs { return &ingressRouteTCPs{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithListAndApply[*traefikiov1alpha1.IngressRouteTCP, *traefikiov1alpha1.IngressRouteTCPList, *applyconfigurationtraefikiov1alpha1.IngressRouteTCPApplyConfiguration]( + "ingressroutetcps", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *traefikiov1alpha1.IngressRouteTCP { return &traefikiov1alpha1.IngressRouteTCP{} }, + func() *traefikiov1alpha1.IngressRouteTCPList { return &traefikiov1alpha1.IngressRouteTCPList{} }, + ), } } - -// Get takes name of the ingressRouteTCP, and returns the corresponding ingressRouteTCP object, and an error if there is any. -func (c *ingressRouteTCPs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.IngressRouteTCP, err error) { - result = &v1alpha1.IngressRouteTCP{} - err = c.client.Get(). - Namespace(c.ns). - Resource("ingressroutetcps"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of IngressRouteTCPs that match those selectors. -func (c *ingressRouteTCPs) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.IngressRouteTCPList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.IngressRouteTCPList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("ingressroutetcps"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested ingressRouteTCPs. -func (c *ingressRouteTCPs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("ingressroutetcps"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a ingressRouteTCP and creates it. Returns the server's representation of the ingressRouteTCP, and an error, if there is any. -func (c *ingressRouteTCPs) Create(ctx context.Context, ingressRouteTCP *v1alpha1.IngressRouteTCP, opts v1.CreateOptions) (result *v1alpha1.IngressRouteTCP, err error) { - result = &v1alpha1.IngressRouteTCP{} - err = c.client.Post(). - Namespace(c.ns). - Resource("ingressroutetcps"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(ingressRouteTCP). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a ingressRouteTCP and updates it. Returns the server's representation of the ingressRouteTCP, and an error, if there is any. -func (c *ingressRouteTCPs) Update(ctx context.Context, ingressRouteTCP *v1alpha1.IngressRouteTCP, opts v1.UpdateOptions) (result *v1alpha1.IngressRouteTCP, err error) { - result = &v1alpha1.IngressRouteTCP{} - err = c.client.Put(). - Namespace(c.ns). - Resource("ingressroutetcps"). - Name(ingressRouteTCP.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(ingressRouteTCP). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the ingressRouteTCP and deletes it. Returns an error if one occurs. -func (c *ingressRouteTCPs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("ingressroutetcps"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *ingressRouteTCPs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("ingressroutetcps"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched ingressRouteTCP. -func (c *ingressRouteTCPs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.IngressRouteTCP, err error) { - result = &v1alpha1.IngressRouteTCP{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("ingressroutetcps"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/ingressrouteudp.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/ingressrouteudp.go index 1b267886a..183791e17 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/ingressrouteudp.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/ingressrouteudp.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,15 +27,15 @@ THE SOFTWARE. package v1alpha1 import ( - "context" - "time" + context "context" + applyconfigurationtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" scheme "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // IngressRouteUDPsGetter has a method to return a IngressRouteUDPInterface. @@ -46,141 +46,33 @@ type IngressRouteUDPsGetter interface { // IngressRouteUDPInterface has methods to work with IngressRouteUDP resources. type IngressRouteUDPInterface interface { - Create(ctx context.Context, ingressRouteUDP *v1alpha1.IngressRouteUDP, opts v1.CreateOptions) (*v1alpha1.IngressRouteUDP, error) - Update(ctx context.Context, ingressRouteUDP *v1alpha1.IngressRouteUDP, opts v1.UpdateOptions) (*v1alpha1.IngressRouteUDP, error) + Create(ctx context.Context, ingressRouteUDP *traefikiov1alpha1.IngressRouteUDP, opts v1.CreateOptions) (*traefikiov1alpha1.IngressRouteUDP, error) + Update(ctx context.Context, ingressRouteUDP *traefikiov1alpha1.IngressRouteUDP, opts v1.UpdateOptions) (*traefikiov1alpha1.IngressRouteUDP, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.IngressRouteUDP, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.IngressRouteUDPList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*traefikiov1alpha1.IngressRouteUDP, error) + List(ctx context.Context, opts v1.ListOptions) (*traefikiov1alpha1.IngressRouteUDPList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.IngressRouteUDP, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *traefikiov1alpha1.IngressRouteUDP, err error) + Apply(ctx context.Context, ingressRouteUDP *applyconfigurationtraefikiov1alpha1.IngressRouteUDPApplyConfiguration, opts v1.ApplyOptions) (result *traefikiov1alpha1.IngressRouteUDP, err error) IngressRouteUDPExpansion } // ingressRouteUDPs implements IngressRouteUDPInterface type ingressRouteUDPs struct { - client rest.Interface - ns string + *gentype.ClientWithListAndApply[*traefikiov1alpha1.IngressRouteUDP, *traefikiov1alpha1.IngressRouteUDPList, *applyconfigurationtraefikiov1alpha1.IngressRouteUDPApplyConfiguration] } // newIngressRouteUDPs returns a IngressRouteUDPs func newIngressRouteUDPs(c *TraefikV1alpha1Client, namespace string) *ingressRouteUDPs { return &ingressRouteUDPs{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithListAndApply[*traefikiov1alpha1.IngressRouteUDP, *traefikiov1alpha1.IngressRouteUDPList, *applyconfigurationtraefikiov1alpha1.IngressRouteUDPApplyConfiguration]( + "ingressrouteudps", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *traefikiov1alpha1.IngressRouteUDP { return &traefikiov1alpha1.IngressRouteUDP{} }, + func() *traefikiov1alpha1.IngressRouteUDPList { return &traefikiov1alpha1.IngressRouteUDPList{} }, + ), } } - -// Get takes name of the ingressRouteUDP, and returns the corresponding ingressRouteUDP object, and an error if there is any. -func (c *ingressRouteUDPs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.IngressRouteUDP, err error) { - result = &v1alpha1.IngressRouteUDP{} - err = c.client.Get(). - Namespace(c.ns). - Resource("ingressrouteudps"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of IngressRouteUDPs that match those selectors. -func (c *ingressRouteUDPs) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.IngressRouteUDPList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.IngressRouteUDPList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("ingressrouteudps"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested ingressRouteUDPs. -func (c *ingressRouteUDPs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("ingressrouteudps"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a ingressRouteUDP and creates it. Returns the server's representation of the ingressRouteUDP, and an error, if there is any. -func (c *ingressRouteUDPs) Create(ctx context.Context, ingressRouteUDP *v1alpha1.IngressRouteUDP, opts v1.CreateOptions) (result *v1alpha1.IngressRouteUDP, err error) { - result = &v1alpha1.IngressRouteUDP{} - err = c.client.Post(). - Namespace(c.ns). - Resource("ingressrouteudps"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(ingressRouteUDP). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a ingressRouteUDP and updates it. Returns the server's representation of the ingressRouteUDP, and an error, if there is any. -func (c *ingressRouteUDPs) Update(ctx context.Context, ingressRouteUDP *v1alpha1.IngressRouteUDP, opts v1.UpdateOptions) (result *v1alpha1.IngressRouteUDP, err error) { - result = &v1alpha1.IngressRouteUDP{} - err = c.client.Put(). - Namespace(c.ns). - Resource("ingressrouteudps"). - Name(ingressRouteUDP.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(ingressRouteUDP). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the ingressRouteUDP and deletes it. Returns an error if one occurs. -func (c *ingressRouteUDPs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("ingressrouteudps"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *ingressRouteUDPs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("ingressrouteudps"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched ingressRouteUDP. -func (c *ingressRouteUDPs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.IngressRouteUDP, err error) { - result = &v1alpha1.IngressRouteUDP{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("ingressrouteudps"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/middleware.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/middleware.go index a4c743c28..330286151 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/middleware.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/middleware.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,15 +27,15 @@ THE SOFTWARE. package v1alpha1 import ( - "context" - "time" + context "context" + applyconfigurationtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" scheme "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // MiddlewaresGetter has a method to return a MiddlewareInterface. @@ -46,141 +46,33 @@ type MiddlewaresGetter interface { // MiddlewareInterface has methods to work with Middleware resources. type MiddlewareInterface interface { - Create(ctx context.Context, middleware *v1alpha1.Middleware, opts v1.CreateOptions) (*v1alpha1.Middleware, error) - Update(ctx context.Context, middleware *v1alpha1.Middleware, opts v1.UpdateOptions) (*v1alpha1.Middleware, error) + Create(ctx context.Context, middleware *traefikiov1alpha1.Middleware, opts v1.CreateOptions) (*traefikiov1alpha1.Middleware, error) + Update(ctx context.Context, middleware *traefikiov1alpha1.Middleware, opts v1.UpdateOptions) (*traefikiov1alpha1.Middleware, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.Middleware, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.MiddlewareList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*traefikiov1alpha1.Middleware, error) + List(ctx context.Context, opts v1.ListOptions) (*traefikiov1alpha1.MiddlewareList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Middleware, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *traefikiov1alpha1.Middleware, err error) + Apply(ctx context.Context, middleware *applyconfigurationtraefikiov1alpha1.MiddlewareApplyConfiguration, opts v1.ApplyOptions) (result *traefikiov1alpha1.Middleware, err error) MiddlewareExpansion } // middlewares implements MiddlewareInterface type middlewares struct { - client rest.Interface - ns string + *gentype.ClientWithListAndApply[*traefikiov1alpha1.Middleware, *traefikiov1alpha1.MiddlewareList, *applyconfigurationtraefikiov1alpha1.MiddlewareApplyConfiguration] } // newMiddlewares returns a Middlewares func newMiddlewares(c *TraefikV1alpha1Client, namespace string) *middlewares { return &middlewares{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithListAndApply[*traefikiov1alpha1.Middleware, *traefikiov1alpha1.MiddlewareList, *applyconfigurationtraefikiov1alpha1.MiddlewareApplyConfiguration]( + "middlewares", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *traefikiov1alpha1.Middleware { return &traefikiov1alpha1.Middleware{} }, + func() *traefikiov1alpha1.MiddlewareList { return &traefikiov1alpha1.MiddlewareList{} }, + ), } } - -// Get takes name of the middleware, and returns the corresponding middleware object, and an error if there is any. -func (c *middlewares) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.Middleware, err error) { - result = &v1alpha1.Middleware{} - err = c.client.Get(). - Namespace(c.ns). - Resource("middlewares"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Middlewares that match those selectors. -func (c *middlewares) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.MiddlewareList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.MiddlewareList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("middlewares"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested middlewares. -func (c *middlewares) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("middlewares"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a middleware and creates it. Returns the server's representation of the middleware, and an error, if there is any. -func (c *middlewares) Create(ctx context.Context, middleware *v1alpha1.Middleware, opts v1.CreateOptions) (result *v1alpha1.Middleware, err error) { - result = &v1alpha1.Middleware{} - err = c.client.Post(). - Namespace(c.ns). - Resource("middlewares"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(middleware). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a middleware and updates it. Returns the server's representation of the middleware, and an error, if there is any. -func (c *middlewares) Update(ctx context.Context, middleware *v1alpha1.Middleware, opts v1.UpdateOptions) (result *v1alpha1.Middleware, err error) { - result = &v1alpha1.Middleware{} - err = c.client.Put(). - Namespace(c.ns). - Resource("middlewares"). - Name(middleware.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(middleware). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the middleware and deletes it. Returns an error if one occurs. -func (c *middlewares) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("middlewares"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *middlewares) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("middlewares"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched middleware. -func (c *middlewares) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.Middleware, err error) { - result = &v1alpha1.Middleware{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("middlewares"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/middlewaretcp.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/middlewaretcp.go index 29e2901ab..534a57ff1 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/middlewaretcp.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/middlewaretcp.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,15 +27,15 @@ THE SOFTWARE. package v1alpha1 import ( - "context" - "time" + context "context" + applyconfigurationtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" scheme "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // MiddlewareTCPsGetter has a method to return a MiddlewareTCPInterface. @@ -46,141 +46,33 @@ type MiddlewareTCPsGetter interface { // MiddlewareTCPInterface has methods to work with MiddlewareTCP resources. type MiddlewareTCPInterface interface { - Create(ctx context.Context, middlewareTCP *v1alpha1.MiddlewareTCP, opts v1.CreateOptions) (*v1alpha1.MiddlewareTCP, error) - Update(ctx context.Context, middlewareTCP *v1alpha1.MiddlewareTCP, opts v1.UpdateOptions) (*v1alpha1.MiddlewareTCP, error) + Create(ctx context.Context, middlewareTCP *traefikiov1alpha1.MiddlewareTCP, opts v1.CreateOptions) (*traefikiov1alpha1.MiddlewareTCP, error) + Update(ctx context.Context, middlewareTCP *traefikiov1alpha1.MiddlewareTCP, opts v1.UpdateOptions) (*traefikiov1alpha1.MiddlewareTCP, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.MiddlewareTCP, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.MiddlewareTCPList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*traefikiov1alpha1.MiddlewareTCP, error) + List(ctx context.Context, opts v1.ListOptions) (*traefikiov1alpha1.MiddlewareTCPList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.MiddlewareTCP, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *traefikiov1alpha1.MiddlewareTCP, err error) + Apply(ctx context.Context, middlewareTCP *applyconfigurationtraefikiov1alpha1.MiddlewareTCPApplyConfiguration, opts v1.ApplyOptions) (result *traefikiov1alpha1.MiddlewareTCP, err error) MiddlewareTCPExpansion } // middlewareTCPs implements MiddlewareTCPInterface type middlewareTCPs struct { - client rest.Interface - ns string + *gentype.ClientWithListAndApply[*traefikiov1alpha1.MiddlewareTCP, *traefikiov1alpha1.MiddlewareTCPList, *applyconfigurationtraefikiov1alpha1.MiddlewareTCPApplyConfiguration] } // newMiddlewareTCPs returns a MiddlewareTCPs func newMiddlewareTCPs(c *TraefikV1alpha1Client, namespace string) *middlewareTCPs { return &middlewareTCPs{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithListAndApply[*traefikiov1alpha1.MiddlewareTCP, *traefikiov1alpha1.MiddlewareTCPList, *applyconfigurationtraefikiov1alpha1.MiddlewareTCPApplyConfiguration]( + "middlewaretcps", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *traefikiov1alpha1.MiddlewareTCP { return &traefikiov1alpha1.MiddlewareTCP{} }, + func() *traefikiov1alpha1.MiddlewareTCPList { return &traefikiov1alpha1.MiddlewareTCPList{} }, + ), } } - -// Get takes name of the middlewareTCP, and returns the corresponding middlewareTCP object, and an error if there is any. -func (c *middlewareTCPs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.MiddlewareTCP, err error) { - result = &v1alpha1.MiddlewareTCP{} - err = c.client.Get(). - Namespace(c.ns). - Resource("middlewaretcps"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of MiddlewareTCPs that match those selectors. -func (c *middlewareTCPs) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.MiddlewareTCPList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.MiddlewareTCPList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("middlewaretcps"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested middlewareTCPs. -func (c *middlewareTCPs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("middlewaretcps"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a middlewareTCP and creates it. Returns the server's representation of the middlewareTCP, and an error, if there is any. -func (c *middlewareTCPs) Create(ctx context.Context, middlewareTCP *v1alpha1.MiddlewareTCP, opts v1.CreateOptions) (result *v1alpha1.MiddlewareTCP, err error) { - result = &v1alpha1.MiddlewareTCP{} - err = c.client.Post(). - Namespace(c.ns). - Resource("middlewaretcps"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(middlewareTCP). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a middlewareTCP and updates it. Returns the server's representation of the middlewareTCP, and an error, if there is any. -func (c *middlewareTCPs) Update(ctx context.Context, middlewareTCP *v1alpha1.MiddlewareTCP, opts v1.UpdateOptions) (result *v1alpha1.MiddlewareTCP, err error) { - result = &v1alpha1.MiddlewareTCP{} - err = c.client.Put(). - Namespace(c.ns). - Resource("middlewaretcps"). - Name(middlewareTCP.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(middlewareTCP). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the middlewareTCP and deletes it. Returns an error if one occurs. -func (c *middlewareTCPs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("middlewaretcps"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *middlewareTCPs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("middlewaretcps"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched middlewareTCP. -func (c *middlewareTCPs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.MiddlewareTCP, err error) { - result = &v1alpha1.MiddlewareTCP{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("middlewaretcps"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/serverstransport.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/serverstransport.go index 3a267692d..187bb801f 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/serverstransport.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/serverstransport.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,15 +27,15 @@ THE SOFTWARE. package v1alpha1 import ( - "context" - "time" + context "context" + applyconfigurationtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" scheme "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // ServersTransportsGetter has a method to return a ServersTransportInterface. @@ -46,141 +46,33 @@ type ServersTransportsGetter interface { // ServersTransportInterface has methods to work with ServersTransport resources. type ServersTransportInterface interface { - Create(ctx context.Context, serversTransport *v1alpha1.ServersTransport, opts v1.CreateOptions) (*v1alpha1.ServersTransport, error) - Update(ctx context.Context, serversTransport *v1alpha1.ServersTransport, opts v1.UpdateOptions) (*v1alpha1.ServersTransport, error) + Create(ctx context.Context, serversTransport *traefikiov1alpha1.ServersTransport, opts v1.CreateOptions) (*traefikiov1alpha1.ServersTransport, error) + Update(ctx context.Context, serversTransport *traefikiov1alpha1.ServersTransport, opts v1.UpdateOptions) (*traefikiov1alpha1.ServersTransport, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ServersTransport, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ServersTransportList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*traefikiov1alpha1.ServersTransport, error) + List(ctx context.Context, opts v1.ListOptions) (*traefikiov1alpha1.ServersTransportList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServersTransport, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *traefikiov1alpha1.ServersTransport, err error) + Apply(ctx context.Context, serversTransport *applyconfigurationtraefikiov1alpha1.ServersTransportApplyConfiguration, opts v1.ApplyOptions) (result *traefikiov1alpha1.ServersTransport, err error) ServersTransportExpansion } // serversTransports implements ServersTransportInterface type serversTransports struct { - client rest.Interface - ns string + *gentype.ClientWithListAndApply[*traefikiov1alpha1.ServersTransport, *traefikiov1alpha1.ServersTransportList, *applyconfigurationtraefikiov1alpha1.ServersTransportApplyConfiguration] } // newServersTransports returns a ServersTransports func newServersTransports(c *TraefikV1alpha1Client, namespace string) *serversTransports { return &serversTransports{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithListAndApply[*traefikiov1alpha1.ServersTransport, *traefikiov1alpha1.ServersTransportList, *applyconfigurationtraefikiov1alpha1.ServersTransportApplyConfiguration]( + "serverstransports", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *traefikiov1alpha1.ServersTransport { return &traefikiov1alpha1.ServersTransport{} }, + func() *traefikiov1alpha1.ServersTransportList { return &traefikiov1alpha1.ServersTransportList{} }, + ), } } - -// Get takes name of the serversTransport, and returns the corresponding serversTransport object, and an error if there is any. -func (c *serversTransports) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ServersTransport, err error) { - result = &v1alpha1.ServersTransport{} - err = c.client.Get(). - Namespace(c.ns). - Resource("serverstransports"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of ServersTransports that match those selectors. -func (c *serversTransports) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ServersTransportList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.ServersTransportList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("serverstransports"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested serversTransports. -func (c *serversTransports) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("serverstransports"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a serversTransport and creates it. Returns the server's representation of the serversTransport, and an error, if there is any. -func (c *serversTransports) Create(ctx context.Context, serversTransport *v1alpha1.ServersTransport, opts v1.CreateOptions) (result *v1alpha1.ServersTransport, err error) { - result = &v1alpha1.ServersTransport{} - err = c.client.Post(). - Namespace(c.ns). - Resource("serverstransports"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(serversTransport). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a serversTransport and updates it. Returns the server's representation of the serversTransport, and an error, if there is any. -func (c *serversTransports) Update(ctx context.Context, serversTransport *v1alpha1.ServersTransport, opts v1.UpdateOptions) (result *v1alpha1.ServersTransport, err error) { - result = &v1alpha1.ServersTransport{} - err = c.client.Put(). - Namespace(c.ns). - Resource("serverstransports"). - Name(serversTransport.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(serversTransport). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the serversTransport and deletes it. Returns an error if one occurs. -func (c *serversTransports) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("serverstransports"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *serversTransports) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("serverstransports"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched serversTransport. -func (c *serversTransports) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServersTransport, err error) { - result = &v1alpha1.ServersTransport{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("serverstransports"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/serverstransporttcp.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/serverstransporttcp.go index 4dd49721d..f9b56fb3a 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/serverstransporttcp.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/serverstransporttcp.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,15 +27,15 @@ THE SOFTWARE. package v1alpha1 import ( - "context" - "time" + context "context" + applyconfigurationtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" scheme "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // ServersTransportTCPsGetter has a method to return a ServersTransportTCPInterface. @@ -46,141 +46,33 @@ type ServersTransportTCPsGetter interface { // ServersTransportTCPInterface has methods to work with ServersTransportTCP resources. type ServersTransportTCPInterface interface { - Create(ctx context.Context, serversTransportTCP *v1alpha1.ServersTransportTCP, opts v1.CreateOptions) (*v1alpha1.ServersTransportTCP, error) - Update(ctx context.Context, serversTransportTCP *v1alpha1.ServersTransportTCP, opts v1.UpdateOptions) (*v1alpha1.ServersTransportTCP, error) + Create(ctx context.Context, serversTransportTCP *traefikiov1alpha1.ServersTransportTCP, opts v1.CreateOptions) (*traefikiov1alpha1.ServersTransportTCP, error) + Update(ctx context.Context, serversTransportTCP *traefikiov1alpha1.ServersTransportTCP, opts v1.UpdateOptions) (*traefikiov1alpha1.ServersTransportTCP, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.ServersTransportTCP, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.ServersTransportTCPList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*traefikiov1alpha1.ServersTransportTCP, error) + List(ctx context.Context, opts v1.ListOptions) (*traefikiov1alpha1.ServersTransportTCPList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServersTransportTCP, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *traefikiov1alpha1.ServersTransportTCP, err error) + Apply(ctx context.Context, serversTransportTCP *applyconfigurationtraefikiov1alpha1.ServersTransportTCPApplyConfiguration, opts v1.ApplyOptions) (result *traefikiov1alpha1.ServersTransportTCP, err error) ServersTransportTCPExpansion } // serversTransportTCPs implements ServersTransportTCPInterface type serversTransportTCPs struct { - client rest.Interface - ns string + *gentype.ClientWithListAndApply[*traefikiov1alpha1.ServersTransportTCP, *traefikiov1alpha1.ServersTransportTCPList, *applyconfigurationtraefikiov1alpha1.ServersTransportTCPApplyConfiguration] } // newServersTransportTCPs returns a ServersTransportTCPs func newServersTransportTCPs(c *TraefikV1alpha1Client, namespace string) *serversTransportTCPs { return &serversTransportTCPs{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithListAndApply[*traefikiov1alpha1.ServersTransportTCP, *traefikiov1alpha1.ServersTransportTCPList, *applyconfigurationtraefikiov1alpha1.ServersTransportTCPApplyConfiguration]( + "serverstransporttcps", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *traefikiov1alpha1.ServersTransportTCP { return &traefikiov1alpha1.ServersTransportTCP{} }, + func() *traefikiov1alpha1.ServersTransportTCPList { return &traefikiov1alpha1.ServersTransportTCPList{} }, + ), } } - -// Get takes name of the serversTransportTCP, and returns the corresponding serversTransportTCP object, and an error if there is any. -func (c *serversTransportTCPs) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.ServersTransportTCP, err error) { - result = &v1alpha1.ServersTransportTCP{} - err = c.client.Get(). - Namespace(c.ns). - Resource("serverstransporttcps"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of ServersTransportTCPs that match those selectors. -func (c *serversTransportTCPs) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.ServersTransportTCPList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.ServersTransportTCPList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("serverstransporttcps"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested serversTransportTCPs. -func (c *serversTransportTCPs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("serverstransporttcps"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a serversTransportTCP and creates it. Returns the server's representation of the serversTransportTCP, and an error, if there is any. -func (c *serversTransportTCPs) Create(ctx context.Context, serversTransportTCP *v1alpha1.ServersTransportTCP, opts v1.CreateOptions) (result *v1alpha1.ServersTransportTCP, err error) { - result = &v1alpha1.ServersTransportTCP{} - err = c.client.Post(). - Namespace(c.ns). - Resource("serverstransporttcps"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(serversTransportTCP). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a serversTransportTCP and updates it. Returns the server's representation of the serversTransportTCP, and an error, if there is any. -func (c *serversTransportTCPs) Update(ctx context.Context, serversTransportTCP *v1alpha1.ServersTransportTCP, opts v1.UpdateOptions) (result *v1alpha1.ServersTransportTCP, err error) { - result = &v1alpha1.ServersTransportTCP{} - err = c.client.Put(). - Namespace(c.ns). - Resource("serverstransporttcps"). - Name(serversTransportTCP.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(serversTransportTCP). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the serversTransportTCP and deletes it. Returns an error if one occurs. -func (c *serversTransportTCPs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("serverstransporttcps"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *serversTransportTCPs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("serverstransporttcps"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched serversTransportTCP. -func (c *serversTransportTCPs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.ServersTransportTCP, err error) { - result = &v1alpha1.ServersTransportTCP{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("serverstransporttcps"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/tlsoption.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/tlsoption.go index 3f1dfb6b0..6f7842f53 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/tlsoption.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/tlsoption.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,15 +27,15 @@ THE SOFTWARE. package v1alpha1 import ( - "context" - "time" + context "context" + applyconfigurationtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" scheme "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // TLSOptionsGetter has a method to return a TLSOptionInterface. @@ -46,141 +46,33 @@ type TLSOptionsGetter interface { // TLSOptionInterface has methods to work with TLSOption resources. type TLSOptionInterface interface { - Create(ctx context.Context, tLSOption *v1alpha1.TLSOption, opts v1.CreateOptions) (*v1alpha1.TLSOption, error) - Update(ctx context.Context, tLSOption *v1alpha1.TLSOption, opts v1.UpdateOptions) (*v1alpha1.TLSOption, error) + Create(ctx context.Context, tLSOption *traefikiov1alpha1.TLSOption, opts v1.CreateOptions) (*traefikiov1alpha1.TLSOption, error) + Update(ctx context.Context, tLSOption *traefikiov1alpha1.TLSOption, opts v1.UpdateOptions) (*traefikiov1alpha1.TLSOption, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.TLSOption, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.TLSOptionList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*traefikiov1alpha1.TLSOption, error) + List(ctx context.Context, opts v1.ListOptions) (*traefikiov1alpha1.TLSOptionList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TLSOption, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *traefikiov1alpha1.TLSOption, err error) + Apply(ctx context.Context, tLSOption *applyconfigurationtraefikiov1alpha1.TLSOptionApplyConfiguration, opts v1.ApplyOptions) (result *traefikiov1alpha1.TLSOption, err error) TLSOptionExpansion } // tLSOptions implements TLSOptionInterface type tLSOptions struct { - client rest.Interface - ns string + *gentype.ClientWithListAndApply[*traefikiov1alpha1.TLSOption, *traefikiov1alpha1.TLSOptionList, *applyconfigurationtraefikiov1alpha1.TLSOptionApplyConfiguration] } // newTLSOptions returns a TLSOptions func newTLSOptions(c *TraefikV1alpha1Client, namespace string) *tLSOptions { return &tLSOptions{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithListAndApply[*traefikiov1alpha1.TLSOption, *traefikiov1alpha1.TLSOptionList, *applyconfigurationtraefikiov1alpha1.TLSOptionApplyConfiguration]( + "tlsoptions", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *traefikiov1alpha1.TLSOption { return &traefikiov1alpha1.TLSOption{} }, + func() *traefikiov1alpha1.TLSOptionList { return &traefikiov1alpha1.TLSOptionList{} }, + ), } } - -// Get takes name of the tLSOption, and returns the corresponding tLSOption object, and an error if there is any. -func (c *tLSOptions) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.TLSOption, err error) { - result = &v1alpha1.TLSOption{} - err = c.client.Get(). - Namespace(c.ns). - Resource("tlsoptions"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of TLSOptions that match those selectors. -func (c *tLSOptions) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.TLSOptionList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.TLSOptionList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("tlsoptions"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested tLSOptions. -func (c *tLSOptions) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("tlsoptions"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a tLSOption and creates it. Returns the server's representation of the tLSOption, and an error, if there is any. -func (c *tLSOptions) Create(ctx context.Context, tLSOption *v1alpha1.TLSOption, opts v1.CreateOptions) (result *v1alpha1.TLSOption, err error) { - result = &v1alpha1.TLSOption{} - err = c.client.Post(). - Namespace(c.ns). - Resource("tlsoptions"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(tLSOption). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a tLSOption and updates it. Returns the server's representation of the tLSOption, and an error, if there is any. -func (c *tLSOptions) Update(ctx context.Context, tLSOption *v1alpha1.TLSOption, opts v1.UpdateOptions) (result *v1alpha1.TLSOption, err error) { - result = &v1alpha1.TLSOption{} - err = c.client.Put(). - Namespace(c.ns). - Resource("tlsoptions"). - Name(tLSOption.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(tLSOption). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the tLSOption and deletes it. Returns an error if one occurs. -func (c *tLSOptions) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("tlsoptions"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *tLSOptions) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("tlsoptions"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched tLSOption. -func (c *tLSOptions) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TLSOption, err error) { - result = &v1alpha1.TLSOption{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("tlsoptions"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/tlsstore.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/tlsstore.go index 3756b990a..de517efdb 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/tlsstore.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/tlsstore.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,15 +27,15 @@ THE SOFTWARE. package v1alpha1 import ( - "context" - "time" + context "context" + applyconfigurationtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" scheme "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // TLSStoresGetter has a method to return a TLSStoreInterface. @@ -46,141 +46,33 @@ type TLSStoresGetter interface { // TLSStoreInterface has methods to work with TLSStore resources. type TLSStoreInterface interface { - Create(ctx context.Context, tLSStore *v1alpha1.TLSStore, opts v1.CreateOptions) (*v1alpha1.TLSStore, error) - Update(ctx context.Context, tLSStore *v1alpha1.TLSStore, opts v1.UpdateOptions) (*v1alpha1.TLSStore, error) + Create(ctx context.Context, tLSStore *traefikiov1alpha1.TLSStore, opts v1.CreateOptions) (*traefikiov1alpha1.TLSStore, error) + Update(ctx context.Context, tLSStore *traefikiov1alpha1.TLSStore, opts v1.UpdateOptions) (*traefikiov1alpha1.TLSStore, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.TLSStore, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.TLSStoreList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*traefikiov1alpha1.TLSStore, error) + List(ctx context.Context, opts v1.ListOptions) (*traefikiov1alpha1.TLSStoreList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TLSStore, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *traefikiov1alpha1.TLSStore, err error) + Apply(ctx context.Context, tLSStore *applyconfigurationtraefikiov1alpha1.TLSStoreApplyConfiguration, opts v1.ApplyOptions) (result *traefikiov1alpha1.TLSStore, err error) TLSStoreExpansion } // tLSStores implements TLSStoreInterface type tLSStores struct { - client rest.Interface - ns string + *gentype.ClientWithListAndApply[*traefikiov1alpha1.TLSStore, *traefikiov1alpha1.TLSStoreList, *applyconfigurationtraefikiov1alpha1.TLSStoreApplyConfiguration] } // newTLSStores returns a TLSStores func newTLSStores(c *TraefikV1alpha1Client, namespace string) *tLSStores { return &tLSStores{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithListAndApply[*traefikiov1alpha1.TLSStore, *traefikiov1alpha1.TLSStoreList, *applyconfigurationtraefikiov1alpha1.TLSStoreApplyConfiguration]( + "tlsstores", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *traefikiov1alpha1.TLSStore { return &traefikiov1alpha1.TLSStore{} }, + func() *traefikiov1alpha1.TLSStoreList { return &traefikiov1alpha1.TLSStoreList{} }, + ), } } - -// Get takes name of the tLSStore, and returns the corresponding tLSStore object, and an error if there is any. -func (c *tLSStores) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.TLSStore, err error) { - result = &v1alpha1.TLSStore{} - err = c.client.Get(). - Namespace(c.ns). - Resource("tlsstores"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of TLSStores that match those selectors. -func (c *tLSStores) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.TLSStoreList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.TLSStoreList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("tlsstores"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested tLSStores. -func (c *tLSStores) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("tlsstores"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a tLSStore and creates it. Returns the server's representation of the tLSStore, and an error, if there is any. -func (c *tLSStores) Create(ctx context.Context, tLSStore *v1alpha1.TLSStore, opts v1.CreateOptions) (result *v1alpha1.TLSStore, err error) { - result = &v1alpha1.TLSStore{} - err = c.client.Post(). - Namespace(c.ns). - Resource("tlsstores"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(tLSStore). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a tLSStore and updates it. Returns the server's representation of the tLSStore, and an error, if there is any. -func (c *tLSStores) Update(ctx context.Context, tLSStore *v1alpha1.TLSStore, opts v1.UpdateOptions) (result *v1alpha1.TLSStore, err error) { - result = &v1alpha1.TLSStore{} - err = c.client.Put(). - Namespace(c.ns). - Resource("tlsstores"). - Name(tLSStore.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(tLSStore). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the tLSStore and deletes it. Returns an error if one occurs. -func (c *tLSStores) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("tlsstores"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *tLSStores) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("tlsstores"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched tLSStore. -func (c *tLSStores) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TLSStore, err error) { - result = &v1alpha1.TLSStore{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("tlsstores"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/traefikio_client.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/traefikio_client.go index e0162c6df..e5c964747 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/traefikio_client.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/traefikio_client.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,10 +27,10 @@ THE SOFTWARE. package v1alpha1 import ( - "net/http" + http "net/http" - "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + scheme "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" rest "k8s.io/client-go/rest" ) @@ -98,9 +98,7 @@ func (c *TraefikV1alpha1Client) TraefikServices(namespace string) TraefikService // where httpClient was generated with rest.HTTPClientFor(c). func NewForConfig(c *rest.Config) (*TraefikV1alpha1Client, error) { config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } + setConfigDefaults(&config) httpClient, err := rest.HTTPClientFor(&config) if err != nil { return nil, err @@ -112,9 +110,7 @@ func NewForConfig(c *rest.Config) (*TraefikV1alpha1Client, error) { // Note the http client provided takes precedence over the configured transport values. func NewForConfigAndClient(c *rest.Config, h *http.Client) (*TraefikV1alpha1Client, error) { config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } + setConfigDefaults(&config) client, err := rest.RESTClientForConfigAndClient(&config, h) if err != nil { return nil, err @@ -137,17 +133,15 @@ func New(c rest.Interface) *TraefikV1alpha1Client { return &TraefikV1alpha1Client{c} } -func setConfigDefaults(config *rest.Config) error { - gv := v1alpha1.SchemeGroupVersion +func setConfigDefaults(config *rest.Config) { + gv := traefikiov1alpha1.SchemeGroupVersion config.GroupVersion = &gv config.APIPath = "/apis" - config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + config.NegotiatedSerializer = rest.CodecFactoryForGeneratedClient(scheme.Scheme, scheme.Codecs).WithoutConversion() if config.UserAgent == "" { config.UserAgent = rest.DefaultKubernetesUserAgent() } - - return nil } // RESTClient returns a RESTClient that is used to communicate diff --git a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/traefikservice.go b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/traefikservice.go index 84a56cd6b..1bc07d33e 100644 --- a/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/traefikservice.go +++ b/pkg/provider/kubernetes/crd/generated/clientset/versioned/typed/traefikio/v1alpha1/traefikservice.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,15 +27,15 @@ THE SOFTWARE. package v1alpha1 import ( - "context" - "time" + context "context" + applyconfigurationtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/applyconfiguration/traefikio/v1alpha1" scheme "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned/scheme" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" types "k8s.io/apimachinery/pkg/types" watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" + gentype "k8s.io/client-go/gentype" ) // TraefikServicesGetter has a method to return a TraefikServiceInterface. @@ -46,141 +46,33 @@ type TraefikServicesGetter interface { // TraefikServiceInterface has methods to work with TraefikService resources. type TraefikServiceInterface interface { - Create(ctx context.Context, traefikService *v1alpha1.TraefikService, opts v1.CreateOptions) (*v1alpha1.TraefikService, error) - Update(ctx context.Context, traefikService *v1alpha1.TraefikService, opts v1.UpdateOptions) (*v1alpha1.TraefikService, error) + Create(ctx context.Context, traefikService *traefikiov1alpha1.TraefikService, opts v1.CreateOptions) (*traefikiov1alpha1.TraefikService, error) + Update(ctx context.Context, traefikService *traefikiov1alpha1.TraefikService, opts v1.UpdateOptions) (*traefikiov1alpha1.TraefikService, error) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error - Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.TraefikService, error) - List(ctx context.Context, opts v1.ListOptions) (*v1alpha1.TraefikServiceList, error) + Get(ctx context.Context, name string, opts v1.GetOptions) (*traefikiov1alpha1.TraefikService, error) + List(ctx context.Context, opts v1.ListOptions) (*traefikiov1alpha1.TraefikServiceList, error) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) - Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TraefikService, err error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *traefikiov1alpha1.TraefikService, err error) + Apply(ctx context.Context, traefikService *applyconfigurationtraefikiov1alpha1.TraefikServiceApplyConfiguration, opts v1.ApplyOptions) (result *traefikiov1alpha1.TraefikService, err error) TraefikServiceExpansion } // traefikServices implements TraefikServiceInterface type traefikServices struct { - client rest.Interface - ns string + *gentype.ClientWithListAndApply[*traefikiov1alpha1.TraefikService, *traefikiov1alpha1.TraefikServiceList, *applyconfigurationtraefikiov1alpha1.TraefikServiceApplyConfiguration] } // newTraefikServices returns a TraefikServices func newTraefikServices(c *TraefikV1alpha1Client, namespace string) *traefikServices { return &traefikServices{ - client: c.RESTClient(), - ns: namespace, + gentype.NewClientWithListAndApply[*traefikiov1alpha1.TraefikService, *traefikiov1alpha1.TraefikServiceList, *applyconfigurationtraefikiov1alpha1.TraefikServiceApplyConfiguration]( + "traefikservices", + c.RESTClient(), + scheme.ParameterCodec, + namespace, + func() *traefikiov1alpha1.TraefikService { return &traefikiov1alpha1.TraefikService{} }, + func() *traefikiov1alpha1.TraefikServiceList { return &traefikiov1alpha1.TraefikServiceList{} }, + ), } } - -// Get takes name of the traefikService, and returns the corresponding traefikService object, and an error if there is any. -func (c *traefikServices) Get(ctx context.Context, name string, options v1.GetOptions) (result *v1alpha1.TraefikService, err error) { - result = &v1alpha1.TraefikService{} - err = c.client.Get(). - Namespace(c.ns). - Resource("traefikservices"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(ctx). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of TraefikServices that match those selectors. -func (c *traefikServices) List(ctx context.Context, opts v1.ListOptions) (result *v1alpha1.TraefikServiceList, err error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - result = &v1alpha1.TraefikServiceList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("traefikservices"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Do(ctx). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested traefikServices. -func (c *traefikServices) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { - var timeout time.Duration - if opts.TimeoutSeconds != nil { - timeout = time.Duration(*opts.TimeoutSeconds) * time.Second - } - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("traefikservices"). - VersionedParams(&opts, scheme.ParameterCodec). - Timeout(timeout). - Watch(ctx) -} - -// Create takes the representation of a traefikService and creates it. Returns the server's representation of the traefikService, and an error, if there is any. -func (c *traefikServices) Create(ctx context.Context, traefikService *v1alpha1.TraefikService, opts v1.CreateOptions) (result *v1alpha1.TraefikService, err error) { - result = &v1alpha1.TraefikService{} - err = c.client.Post(). - Namespace(c.ns). - Resource("traefikservices"). - VersionedParams(&opts, scheme.ParameterCodec). - Body(traefikService). - Do(ctx). - Into(result) - return -} - -// Update takes the representation of a traefikService and updates it. Returns the server's representation of the traefikService, and an error, if there is any. -func (c *traefikServices) Update(ctx context.Context, traefikService *v1alpha1.TraefikService, opts v1.UpdateOptions) (result *v1alpha1.TraefikService, err error) { - result = &v1alpha1.TraefikService{} - err = c.client.Put(). - Namespace(c.ns). - Resource("traefikservices"). - Name(traefikService.Name). - VersionedParams(&opts, scheme.ParameterCodec). - Body(traefikService). - Do(ctx). - Into(result) - return -} - -// Delete takes name of the traefikService and deletes it. Returns an error if one occurs. -func (c *traefikServices) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("traefikservices"). - Name(name). - Body(&opts). - Do(ctx). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *traefikServices) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { - var timeout time.Duration - if listOpts.TimeoutSeconds != nil { - timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second - } - return c.client.Delete(). - Namespace(c.ns). - Resource("traefikservices"). - VersionedParams(&listOpts, scheme.ParameterCodec). - Timeout(timeout). - Body(&opts). - Do(ctx). - Error() -} - -// Patch applies the patch and returns the patched traefikService. -func (c *traefikServices) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *v1alpha1.TraefikService, err error) { - result = &v1alpha1.TraefikService{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("traefikservices"). - Name(name). - SubResource(subresources...). - VersionedParams(&opts, scheme.ParameterCodec). - Body(data). - Do(ctx). - Into(result) - return -} diff --git a/pkg/provider/kubernetes/crd/generated/informers/externalversions/factory.go b/pkg/provider/kubernetes/crd/generated/informers/externalversions/factory.go index 1dff9903f..41ed66f44 100644 --- a/pkg/provider/kubernetes/crd/generated/informers/externalversions/factory.go +++ b/pkg/provider/kubernetes/crd/generated/informers/externalversions/factory.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -236,6 +236,7 @@ type SharedInformerFactory interface { // Start initializes all requested informers. They are handled in goroutines // which run until the stop channel gets closed. + // Warning: Start does not block. When run in a go-routine, it will race with a later WaitForCacheSync. Start(stopCh <-chan struct{}) // Shutdown marks a factory as shutting down. At that point no new diff --git a/pkg/provider/kubernetes/crd/generated/informers/externalversions/generic.go b/pkg/provider/kubernetes/crd/generated/informers/externalversions/generic.go index 970e2e899..f975efcc5 100644 --- a/pkg/provider/kubernetes/crd/generated/informers/externalversions/generic.go +++ b/pkg/provider/kubernetes/crd/generated/informers/externalversions/generic.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,7 +27,7 @@ THE SOFTWARE. package externalversions import ( - "fmt" + fmt "fmt" v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" schema "k8s.io/apimachinery/pkg/runtime/schema" diff --git a/pkg/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces/factory_interfaces.go index ce6b460e0..22470b163 100644 --- a/pkg/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces/factory_interfaces.go +++ b/pkg/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/interface.go b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/interface.go index 27ce04f22..686edc9a7 100644 --- a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/interface.go +++ b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/interface.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/ingressroute.go b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/ingressroute.go index b9e466333..5bfc88702 100644 --- a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/ingressroute.go +++ b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/ingressroute.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,13 +27,13 @@ THE SOFTWARE. package v1alpha1 import ( - "context" + context "context" time "time" versioned "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned" internalinterfaces "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" - traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" + crdtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -44,7 +44,7 @@ import ( // IngressRoutes. type IngressRouteInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.IngressRouteLister + Lister() traefikiov1alpha1.IngressRouteLister } type ingressRouteInformer struct { @@ -70,16 +70,28 @@ func NewFilteredIngressRouteInformer(client versioned.Interface, namespace strin if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().IngressRoutes(namespace).List(context.TODO(), options) + return client.TraefikV1alpha1().IngressRoutes(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().IngressRoutes(namespace).Watch(context.TODO(), options) + return client.TraefikV1alpha1().IngressRoutes(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().IngressRoutes(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().IngressRoutes(namespace).Watch(ctx, options) }, }, - &traefikiov1alpha1.IngressRoute{}, + &crdtraefikiov1alpha1.IngressRoute{}, resyncPeriod, indexers, ) @@ -90,9 +102,9 @@ func (f *ingressRouteInformer) defaultInformer(client versioned.Interface, resyn } func (f *ingressRouteInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&traefikiov1alpha1.IngressRoute{}, f.defaultInformer) + return f.factory.InformerFor(&crdtraefikiov1alpha1.IngressRoute{}, f.defaultInformer) } -func (f *ingressRouteInformer) Lister() v1alpha1.IngressRouteLister { - return v1alpha1.NewIngressRouteLister(f.Informer().GetIndexer()) +func (f *ingressRouteInformer) Lister() traefikiov1alpha1.IngressRouteLister { + return traefikiov1alpha1.NewIngressRouteLister(f.Informer().GetIndexer()) } diff --git a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/ingressroutetcp.go b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/ingressroutetcp.go index fd42c12c0..52405235a 100644 --- a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/ingressroutetcp.go +++ b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/ingressroutetcp.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,13 +27,13 @@ THE SOFTWARE. package v1alpha1 import ( - "context" + context "context" time "time" versioned "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned" internalinterfaces "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" - traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" + crdtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -44,7 +44,7 @@ import ( // IngressRouteTCPs. type IngressRouteTCPInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.IngressRouteTCPLister + Lister() traefikiov1alpha1.IngressRouteTCPLister } type ingressRouteTCPInformer struct { @@ -70,16 +70,28 @@ func NewFilteredIngressRouteTCPInformer(client versioned.Interface, namespace st if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().IngressRouteTCPs(namespace).List(context.TODO(), options) + return client.TraefikV1alpha1().IngressRouteTCPs(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().IngressRouteTCPs(namespace).Watch(context.TODO(), options) + return client.TraefikV1alpha1().IngressRouteTCPs(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().IngressRouteTCPs(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().IngressRouteTCPs(namespace).Watch(ctx, options) }, }, - &traefikiov1alpha1.IngressRouteTCP{}, + &crdtraefikiov1alpha1.IngressRouteTCP{}, resyncPeriod, indexers, ) @@ -90,9 +102,9 @@ func (f *ingressRouteTCPInformer) defaultInformer(client versioned.Interface, re } func (f *ingressRouteTCPInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&traefikiov1alpha1.IngressRouteTCP{}, f.defaultInformer) + return f.factory.InformerFor(&crdtraefikiov1alpha1.IngressRouteTCP{}, f.defaultInformer) } -func (f *ingressRouteTCPInformer) Lister() v1alpha1.IngressRouteTCPLister { - return v1alpha1.NewIngressRouteTCPLister(f.Informer().GetIndexer()) +func (f *ingressRouteTCPInformer) Lister() traefikiov1alpha1.IngressRouteTCPLister { + return traefikiov1alpha1.NewIngressRouteTCPLister(f.Informer().GetIndexer()) } diff --git a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/ingressrouteudp.go b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/ingressrouteudp.go index 652b35e3e..273dfe7b2 100644 --- a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/ingressrouteudp.go +++ b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/ingressrouteudp.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,13 +27,13 @@ THE SOFTWARE. package v1alpha1 import ( - "context" + context "context" time "time" versioned "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned" internalinterfaces "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" - traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" + crdtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -44,7 +44,7 @@ import ( // IngressRouteUDPs. type IngressRouteUDPInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.IngressRouteUDPLister + Lister() traefikiov1alpha1.IngressRouteUDPLister } type ingressRouteUDPInformer struct { @@ -70,16 +70,28 @@ func NewFilteredIngressRouteUDPInformer(client versioned.Interface, namespace st if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().IngressRouteUDPs(namespace).List(context.TODO(), options) + return client.TraefikV1alpha1().IngressRouteUDPs(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().IngressRouteUDPs(namespace).Watch(context.TODO(), options) + return client.TraefikV1alpha1().IngressRouteUDPs(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().IngressRouteUDPs(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().IngressRouteUDPs(namespace).Watch(ctx, options) }, }, - &traefikiov1alpha1.IngressRouteUDP{}, + &crdtraefikiov1alpha1.IngressRouteUDP{}, resyncPeriod, indexers, ) @@ -90,9 +102,9 @@ func (f *ingressRouteUDPInformer) defaultInformer(client versioned.Interface, re } func (f *ingressRouteUDPInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&traefikiov1alpha1.IngressRouteUDP{}, f.defaultInformer) + return f.factory.InformerFor(&crdtraefikiov1alpha1.IngressRouteUDP{}, f.defaultInformer) } -func (f *ingressRouteUDPInformer) Lister() v1alpha1.IngressRouteUDPLister { - return v1alpha1.NewIngressRouteUDPLister(f.Informer().GetIndexer()) +func (f *ingressRouteUDPInformer) Lister() traefikiov1alpha1.IngressRouteUDPLister { + return traefikiov1alpha1.NewIngressRouteUDPLister(f.Informer().GetIndexer()) } diff --git a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/interface.go b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/interface.go index 0bdad9ac9..48708e981 100644 --- a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/interface.go +++ b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/interface.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/middleware.go b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/middleware.go index 0d6839287..324c5219d 100644 --- a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/middleware.go +++ b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/middleware.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,13 +27,13 @@ THE SOFTWARE. package v1alpha1 import ( - "context" + context "context" time "time" versioned "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned" internalinterfaces "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" - traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" + crdtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -44,7 +44,7 @@ import ( // Middlewares. type MiddlewareInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.MiddlewareLister + Lister() traefikiov1alpha1.MiddlewareLister } type middlewareInformer struct { @@ -70,16 +70,28 @@ func NewFilteredMiddlewareInformer(client versioned.Interface, namespace string, if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().Middlewares(namespace).List(context.TODO(), options) + return client.TraefikV1alpha1().Middlewares(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().Middlewares(namespace).Watch(context.TODO(), options) + return client.TraefikV1alpha1().Middlewares(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().Middlewares(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().Middlewares(namespace).Watch(ctx, options) }, }, - &traefikiov1alpha1.Middleware{}, + &crdtraefikiov1alpha1.Middleware{}, resyncPeriod, indexers, ) @@ -90,9 +102,9 @@ func (f *middlewareInformer) defaultInformer(client versioned.Interface, resyncP } func (f *middlewareInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&traefikiov1alpha1.Middleware{}, f.defaultInformer) + return f.factory.InformerFor(&crdtraefikiov1alpha1.Middleware{}, f.defaultInformer) } -func (f *middlewareInformer) Lister() v1alpha1.MiddlewareLister { - return v1alpha1.NewMiddlewareLister(f.Informer().GetIndexer()) +func (f *middlewareInformer) Lister() traefikiov1alpha1.MiddlewareLister { + return traefikiov1alpha1.NewMiddlewareLister(f.Informer().GetIndexer()) } diff --git a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/middlewaretcp.go b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/middlewaretcp.go index ec02ec206..e8cacfd41 100644 --- a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/middlewaretcp.go +++ b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/middlewaretcp.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,13 +27,13 @@ THE SOFTWARE. package v1alpha1 import ( - "context" + context "context" time "time" versioned "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned" internalinterfaces "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" - traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" + crdtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -44,7 +44,7 @@ import ( // MiddlewareTCPs. type MiddlewareTCPInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.MiddlewareTCPLister + Lister() traefikiov1alpha1.MiddlewareTCPLister } type middlewareTCPInformer struct { @@ -70,16 +70,28 @@ func NewFilteredMiddlewareTCPInformer(client versioned.Interface, namespace stri if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().MiddlewareTCPs(namespace).List(context.TODO(), options) + return client.TraefikV1alpha1().MiddlewareTCPs(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().MiddlewareTCPs(namespace).Watch(context.TODO(), options) + return client.TraefikV1alpha1().MiddlewareTCPs(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().MiddlewareTCPs(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().MiddlewareTCPs(namespace).Watch(ctx, options) }, }, - &traefikiov1alpha1.MiddlewareTCP{}, + &crdtraefikiov1alpha1.MiddlewareTCP{}, resyncPeriod, indexers, ) @@ -90,9 +102,9 @@ func (f *middlewareTCPInformer) defaultInformer(client versioned.Interface, resy } func (f *middlewareTCPInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&traefikiov1alpha1.MiddlewareTCP{}, f.defaultInformer) + return f.factory.InformerFor(&crdtraefikiov1alpha1.MiddlewareTCP{}, f.defaultInformer) } -func (f *middlewareTCPInformer) Lister() v1alpha1.MiddlewareTCPLister { - return v1alpha1.NewMiddlewareTCPLister(f.Informer().GetIndexer()) +func (f *middlewareTCPInformer) Lister() traefikiov1alpha1.MiddlewareTCPLister { + return traefikiov1alpha1.NewMiddlewareTCPLister(f.Informer().GetIndexer()) } diff --git a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/serverstransport.go b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/serverstransport.go index ea7739ff9..8b091354d 100644 --- a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/serverstransport.go +++ b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/serverstransport.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,13 +27,13 @@ THE SOFTWARE. package v1alpha1 import ( - "context" + context "context" time "time" versioned "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned" internalinterfaces "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" - traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" + crdtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -44,7 +44,7 @@ import ( // ServersTransports. type ServersTransportInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.ServersTransportLister + Lister() traefikiov1alpha1.ServersTransportLister } type serversTransportInformer struct { @@ -70,16 +70,28 @@ func NewFilteredServersTransportInformer(client versioned.Interface, namespace s if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().ServersTransports(namespace).List(context.TODO(), options) + return client.TraefikV1alpha1().ServersTransports(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().ServersTransports(namespace).Watch(context.TODO(), options) + return client.TraefikV1alpha1().ServersTransports(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().ServersTransports(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().ServersTransports(namespace).Watch(ctx, options) }, }, - &traefikiov1alpha1.ServersTransport{}, + &crdtraefikiov1alpha1.ServersTransport{}, resyncPeriod, indexers, ) @@ -90,9 +102,9 @@ func (f *serversTransportInformer) defaultInformer(client versioned.Interface, r } func (f *serversTransportInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&traefikiov1alpha1.ServersTransport{}, f.defaultInformer) + return f.factory.InformerFor(&crdtraefikiov1alpha1.ServersTransport{}, f.defaultInformer) } -func (f *serversTransportInformer) Lister() v1alpha1.ServersTransportLister { - return v1alpha1.NewServersTransportLister(f.Informer().GetIndexer()) +func (f *serversTransportInformer) Lister() traefikiov1alpha1.ServersTransportLister { + return traefikiov1alpha1.NewServersTransportLister(f.Informer().GetIndexer()) } diff --git a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/serverstransporttcp.go b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/serverstransporttcp.go index c0c102591..824c67cae 100644 --- a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/serverstransporttcp.go +++ b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/serverstransporttcp.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,13 +27,13 @@ THE SOFTWARE. package v1alpha1 import ( - "context" + context "context" time "time" versioned "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned" internalinterfaces "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" - traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" + crdtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -44,7 +44,7 @@ import ( // ServersTransportTCPs. type ServersTransportTCPInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.ServersTransportTCPLister + Lister() traefikiov1alpha1.ServersTransportTCPLister } type serversTransportTCPInformer struct { @@ -70,16 +70,28 @@ func NewFilteredServersTransportTCPInformer(client versioned.Interface, namespac if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().ServersTransportTCPs(namespace).List(context.TODO(), options) + return client.TraefikV1alpha1().ServersTransportTCPs(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().ServersTransportTCPs(namespace).Watch(context.TODO(), options) + return client.TraefikV1alpha1().ServersTransportTCPs(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().ServersTransportTCPs(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().ServersTransportTCPs(namespace).Watch(ctx, options) }, }, - &traefikiov1alpha1.ServersTransportTCP{}, + &crdtraefikiov1alpha1.ServersTransportTCP{}, resyncPeriod, indexers, ) @@ -90,9 +102,9 @@ func (f *serversTransportTCPInformer) defaultInformer(client versioned.Interface } func (f *serversTransportTCPInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&traefikiov1alpha1.ServersTransportTCP{}, f.defaultInformer) + return f.factory.InformerFor(&crdtraefikiov1alpha1.ServersTransportTCP{}, f.defaultInformer) } -func (f *serversTransportTCPInformer) Lister() v1alpha1.ServersTransportTCPLister { - return v1alpha1.NewServersTransportTCPLister(f.Informer().GetIndexer()) +func (f *serversTransportTCPInformer) Lister() traefikiov1alpha1.ServersTransportTCPLister { + return traefikiov1alpha1.NewServersTransportTCPLister(f.Informer().GetIndexer()) } diff --git a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/tlsoption.go b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/tlsoption.go index fa65e9e63..fbc03787a 100644 --- a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/tlsoption.go +++ b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/tlsoption.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,13 +27,13 @@ THE SOFTWARE. package v1alpha1 import ( - "context" + context "context" time "time" versioned "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned" internalinterfaces "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" - traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" + crdtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -44,7 +44,7 @@ import ( // TLSOptions. type TLSOptionInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.TLSOptionLister + Lister() traefikiov1alpha1.TLSOptionLister } type tLSOptionInformer struct { @@ -70,16 +70,28 @@ func NewFilteredTLSOptionInformer(client versioned.Interface, namespace string, if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().TLSOptions(namespace).List(context.TODO(), options) + return client.TraefikV1alpha1().TLSOptions(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().TLSOptions(namespace).Watch(context.TODO(), options) + return client.TraefikV1alpha1().TLSOptions(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().TLSOptions(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().TLSOptions(namespace).Watch(ctx, options) }, }, - &traefikiov1alpha1.TLSOption{}, + &crdtraefikiov1alpha1.TLSOption{}, resyncPeriod, indexers, ) @@ -90,9 +102,9 @@ func (f *tLSOptionInformer) defaultInformer(client versioned.Interface, resyncPe } func (f *tLSOptionInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&traefikiov1alpha1.TLSOption{}, f.defaultInformer) + return f.factory.InformerFor(&crdtraefikiov1alpha1.TLSOption{}, f.defaultInformer) } -func (f *tLSOptionInformer) Lister() v1alpha1.TLSOptionLister { - return v1alpha1.NewTLSOptionLister(f.Informer().GetIndexer()) +func (f *tLSOptionInformer) Lister() traefikiov1alpha1.TLSOptionLister { + return traefikiov1alpha1.NewTLSOptionLister(f.Informer().GetIndexer()) } diff --git a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/tlsstore.go b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/tlsstore.go index 04095e338..02eea4cf8 100644 --- a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/tlsstore.go +++ b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/tlsstore.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,13 +27,13 @@ THE SOFTWARE. package v1alpha1 import ( - "context" + context "context" time "time" versioned "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned" internalinterfaces "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" - traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" + crdtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -44,7 +44,7 @@ import ( // TLSStores. type TLSStoreInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.TLSStoreLister + Lister() traefikiov1alpha1.TLSStoreLister } type tLSStoreInformer struct { @@ -70,16 +70,28 @@ func NewFilteredTLSStoreInformer(client versioned.Interface, namespace string, r if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().TLSStores(namespace).List(context.TODO(), options) + return client.TraefikV1alpha1().TLSStores(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().TLSStores(namespace).Watch(context.TODO(), options) + return client.TraefikV1alpha1().TLSStores(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().TLSStores(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().TLSStores(namespace).Watch(ctx, options) }, }, - &traefikiov1alpha1.TLSStore{}, + &crdtraefikiov1alpha1.TLSStore{}, resyncPeriod, indexers, ) @@ -90,9 +102,9 @@ func (f *tLSStoreInformer) defaultInformer(client versioned.Interface, resyncPer } func (f *tLSStoreInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&traefikiov1alpha1.TLSStore{}, f.defaultInformer) + return f.factory.InformerFor(&crdtraefikiov1alpha1.TLSStore{}, f.defaultInformer) } -func (f *tLSStoreInformer) Lister() v1alpha1.TLSStoreLister { - return v1alpha1.NewTLSStoreLister(f.Informer().GetIndexer()) +func (f *tLSStoreInformer) Lister() traefikiov1alpha1.TLSStoreLister { + return traefikiov1alpha1.NewTLSStoreLister(f.Informer().GetIndexer()) } diff --git a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/traefikservice.go b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/traefikservice.go index a1a1bbf58..272be98cb 100644 --- a/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/traefikservice.go +++ b/pkg/provider/kubernetes/crd/generated/informers/externalversions/traefikio/v1alpha1/traefikservice.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,13 +27,13 @@ THE SOFTWARE. package v1alpha1 import ( - "context" + context "context" time "time" versioned "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/clientset/versioned" internalinterfaces "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" - traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1" + crdtraefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" watch "k8s.io/apimachinery/pkg/watch" @@ -44,7 +44,7 @@ import ( // TraefikServices. type TraefikServiceInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.TraefikServiceLister + Lister() traefikiov1alpha1.TraefikServiceLister } type traefikServiceInformer struct { @@ -70,16 +70,28 @@ func NewFilteredTraefikServiceInformer(client versioned.Interface, namespace str if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().TraefikServices(namespace).List(context.TODO(), options) + return client.TraefikV1alpha1().TraefikServices(namespace).List(context.Background(), options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.TraefikV1alpha1().TraefikServices(namespace).Watch(context.TODO(), options) + return client.TraefikV1alpha1().TraefikServices(namespace).Watch(context.Background(), options) + }, + ListWithContextFunc: func(ctx context.Context, options v1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().TraefikServices(namespace).List(ctx, options) + }, + WatchFuncWithContext: func(ctx context.Context, options v1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.TraefikV1alpha1().TraefikServices(namespace).Watch(ctx, options) }, }, - &traefikiov1alpha1.TraefikService{}, + &crdtraefikiov1alpha1.TraefikService{}, resyncPeriod, indexers, ) @@ -90,9 +102,9 @@ func (f *traefikServiceInformer) defaultInformer(client versioned.Interface, res } func (f *traefikServiceInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&traefikiov1alpha1.TraefikService{}, f.defaultInformer) + return f.factory.InformerFor(&crdtraefikiov1alpha1.TraefikService{}, f.defaultInformer) } -func (f *traefikServiceInformer) Lister() v1alpha1.TraefikServiceLister { - return v1alpha1.NewTraefikServiceLister(f.Informer().GetIndexer()) +func (f *traefikServiceInformer) Lister() traefikiov1alpha1.TraefikServiceLister { + return traefikiov1alpha1.NewTraefikServiceLister(f.Informer().GetIndexer()) } diff --git a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/expansion_generated.go b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/expansion_generated.go index b61b4b735..32f3d7b4a 100644 --- a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/expansion_generated.go +++ b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/expansion_generated.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/ingressroute.go b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/ingressroute.go index a88372570..57647bde4 100644 --- a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/ingressroute.go +++ b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/ingressroute.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,10 +27,10 @@ THE SOFTWARE. package v1alpha1 import ( - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // IngressRouteLister helps list IngressRoutes. @@ -38,7 +38,7 @@ import ( type IngressRouteLister interface { // List lists all IngressRoutes in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.IngressRoute, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.IngressRoute, err error) // IngressRoutes returns an object that can list and get IngressRoutes. IngressRoutes(namespace string) IngressRouteNamespaceLister IngressRouteListerExpansion @@ -46,25 +46,17 @@ type IngressRouteLister interface { // ingressRouteLister implements the IngressRouteLister interface. type ingressRouteLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*traefikiov1alpha1.IngressRoute] } // NewIngressRouteLister returns a new IngressRouteLister. func NewIngressRouteLister(indexer cache.Indexer) IngressRouteLister { - return &ingressRouteLister{indexer: indexer} -} - -// List lists all IngressRoutes in the indexer. -func (s *ingressRouteLister) List(selector labels.Selector) (ret []*v1alpha1.IngressRoute, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.IngressRoute)) - }) - return ret, err + return &ingressRouteLister{listers.New[*traefikiov1alpha1.IngressRoute](indexer, traefikiov1alpha1.Resource("ingressroute"))} } // IngressRoutes returns an object that can list and get IngressRoutes. func (s *ingressRouteLister) IngressRoutes(namespace string) IngressRouteNamespaceLister { - return ingressRouteNamespaceLister{indexer: s.indexer, namespace: namespace} + return ingressRouteNamespaceLister{listers.NewNamespaced[*traefikiov1alpha1.IngressRoute](s.ResourceIndexer, namespace)} } // IngressRouteNamespaceLister helps list and get IngressRoutes. @@ -72,36 +64,15 @@ func (s *ingressRouteLister) IngressRoutes(namespace string) IngressRouteNamespa type IngressRouteNamespaceLister interface { // List lists all IngressRoutes in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.IngressRoute, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.IngressRoute, err error) // Get retrieves the IngressRoute from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.IngressRoute, error) + Get(name string) (*traefikiov1alpha1.IngressRoute, error) IngressRouteNamespaceListerExpansion } // ingressRouteNamespaceLister implements the IngressRouteNamespaceLister // interface. type ingressRouteNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all IngressRoutes in the indexer for a given namespace. -func (s ingressRouteNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.IngressRoute, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.IngressRoute)) - }) - return ret, err -} - -// Get retrieves the IngressRoute from the indexer for a given namespace and name. -func (s ingressRouteNamespaceLister) Get(name string) (*v1alpha1.IngressRoute, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("ingressroute"), name) - } - return obj.(*v1alpha1.IngressRoute), nil + listers.ResourceIndexer[*traefikiov1alpha1.IngressRoute] } diff --git a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/ingressroutetcp.go b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/ingressroutetcp.go index a0f2c50da..661047de6 100644 --- a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/ingressroutetcp.go +++ b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/ingressroutetcp.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,10 +27,10 @@ THE SOFTWARE. package v1alpha1 import ( - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // IngressRouteTCPLister helps list IngressRouteTCPs. @@ -38,7 +38,7 @@ import ( type IngressRouteTCPLister interface { // List lists all IngressRouteTCPs in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.IngressRouteTCP, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.IngressRouteTCP, err error) // IngressRouteTCPs returns an object that can list and get IngressRouteTCPs. IngressRouteTCPs(namespace string) IngressRouteTCPNamespaceLister IngressRouteTCPListerExpansion @@ -46,25 +46,17 @@ type IngressRouteTCPLister interface { // ingressRouteTCPLister implements the IngressRouteTCPLister interface. type ingressRouteTCPLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*traefikiov1alpha1.IngressRouteTCP] } // NewIngressRouteTCPLister returns a new IngressRouteTCPLister. func NewIngressRouteTCPLister(indexer cache.Indexer) IngressRouteTCPLister { - return &ingressRouteTCPLister{indexer: indexer} -} - -// List lists all IngressRouteTCPs in the indexer. -func (s *ingressRouteTCPLister) List(selector labels.Selector) (ret []*v1alpha1.IngressRouteTCP, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.IngressRouteTCP)) - }) - return ret, err + return &ingressRouteTCPLister{listers.New[*traefikiov1alpha1.IngressRouteTCP](indexer, traefikiov1alpha1.Resource("ingressroutetcp"))} } // IngressRouteTCPs returns an object that can list and get IngressRouteTCPs. func (s *ingressRouteTCPLister) IngressRouteTCPs(namespace string) IngressRouteTCPNamespaceLister { - return ingressRouteTCPNamespaceLister{indexer: s.indexer, namespace: namespace} + return ingressRouteTCPNamespaceLister{listers.NewNamespaced[*traefikiov1alpha1.IngressRouteTCP](s.ResourceIndexer, namespace)} } // IngressRouteTCPNamespaceLister helps list and get IngressRouteTCPs. @@ -72,36 +64,15 @@ func (s *ingressRouteTCPLister) IngressRouteTCPs(namespace string) IngressRouteT type IngressRouteTCPNamespaceLister interface { // List lists all IngressRouteTCPs in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.IngressRouteTCP, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.IngressRouteTCP, err error) // Get retrieves the IngressRouteTCP from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.IngressRouteTCP, error) + Get(name string) (*traefikiov1alpha1.IngressRouteTCP, error) IngressRouteTCPNamespaceListerExpansion } // ingressRouteTCPNamespaceLister implements the IngressRouteTCPNamespaceLister // interface. type ingressRouteTCPNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all IngressRouteTCPs in the indexer for a given namespace. -func (s ingressRouteTCPNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.IngressRouteTCP, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.IngressRouteTCP)) - }) - return ret, err -} - -// Get retrieves the IngressRouteTCP from the indexer for a given namespace and name. -func (s ingressRouteTCPNamespaceLister) Get(name string) (*v1alpha1.IngressRouteTCP, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("ingressroutetcp"), name) - } - return obj.(*v1alpha1.IngressRouteTCP), nil + listers.ResourceIndexer[*traefikiov1alpha1.IngressRouteTCP] } diff --git a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/ingressrouteudp.go b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/ingressrouteudp.go index 429ae71d8..ae03f3299 100644 --- a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/ingressrouteudp.go +++ b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/ingressrouteudp.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,10 +27,10 @@ THE SOFTWARE. package v1alpha1 import ( - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // IngressRouteUDPLister helps list IngressRouteUDPs. @@ -38,7 +38,7 @@ import ( type IngressRouteUDPLister interface { // List lists all IngressRouteUDPs in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.IngressRouteUDP, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.IngressRouteUDP, err error) // IngressRouteUDPs returns an object that can list and get IngressRouteUDPs. IngressRouteUDPs(namespace string) IngressRouteUDPNamespaceLister IngressRouteUDPListerExpansion @@ -46,25 +46,17 @@ type IngressRouteUDPLister interface { // ingressRouteUDPLister implements the IngressRouteUDPLister interface. type ingressRouteUDPLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*traefikiov1alpha1.IngressRouteUDP] } // NewIngressRouteUDPLister returns a new IngressRouteUDPLister. func NewIngressRouteUDPLister(indexer cache.Indexer) IngressRouteUDPLister { - return &ingressRouteUDPLister{indexer: indexer} -} - -// List lists all IngressRouteUDPs in the indexer. -func (s *ingressRouteUDPLister) List(selector labels.Selector) (ret []*v1alpha1.IngressRouteUDP, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.IngressRouteUDP)) - }) - return ret, err + return &ingressRouteUDPLister{listers.New[*traefikiov1alpha1.IngressRouteUDP](indexer, traefikiov1alpha1.Resource("ingressrouteudp"))} } // IngressRouteUDPs returns an object that can list and get IngressRouteUDPs. func (s *ingressRouteUDPLister) IngressRouteUDPs(namespace string) IngressRouteUDPNamespaceLister { - return ingressRouteUDPNamespaceLister{indexer: s.indexer, namespace: namespace} + return ingressRouteUDPNamespaceLister{listers.NewNamespaced[*traefikiov1alpha1.IngressRouteUDP](s.ResourceIndexer, namespace)} } // IngressRouteUDPNamespaceLister helps list and get IngressRouteUDPs. @@ -72,36 +64,15 @@ func (s *ingressRouteUDPLister) IngressRouteUDPs(namespace string) IngressRouteU type IngressRouteUDPNamespaceLister interface { // List lists all IngressRouteUDPs in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.IngressRouteUDP, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.IngressRouteUDP, err error) // Get retrieves the IngressRouteUDP from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.IngressRouteUDP, error) + Get(name string) (*traefikiov1alpha1.IngressRouteUDP, error) IngressRouteUDPNamespaceListerExpansion } // ingressRouteUDPNamespaceLister implements the IngressRouteUDPNamespaceLister // interface. type ingressRouteUDPNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all IngressRouteUDPs in the indexer for a given namespace. -func (s ingressRouteUDPNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.IngressRouteUDP, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.IngressRouteUDP)) - }) - return ret, err -} - -// Get retrieves the IngressRouteUDP from the indexer for a given namespace and name. -func (s ingressRouteUDPNamespaceLister) Get(name string) (*v1alpha1.IngressRouteUDP, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("ingressrouteudp"), name) - } - return obj.(*v1alpha1.IngressRouteUDP), nil + listers.ResourceIndexer[*traefikiov1alpha1.IngressRouteUDP] } diff --git a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/middleware.go b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/middleware.go index 9d9c1c468..f87306ef2 100644 --- a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/middleware.go +++ b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/middleware.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,10 +27,10 @@ THE SOFTWARE. package v1alpha1 import ( - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // MiddlewareLister helps list Middlewares. @@ -38,7 +38,7 @@ import ( type MiddlewareLister interface { // List lists all Middlewares in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.Middleware, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.Middleware, err error) // Middlewares returns an object that can list and get Middlewares. Middlewares(namespace string) MiddlewareNamespaceLister MiddlewareListerExpansion @@ -46,25 +46,17 @@ type MiddlewareLister interface { // middlewareLister implements the MiddlewareLister interface. type middlewareLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*traefikiov1alpha1.Middleware] } // NewMiddlewareLister returns a new MiddlewareLister. func NewMiddlewareLister(indexer cache.Indexer) MiddlewareLister { - return &middlewareLister{indexer: indexer} -} - -// List lists all Middlewares in the indexer. -func (s *middlewareLister) List(selector labels.Selector) (ret []*v1alpha1.Middleware, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.Middleware)) - }) - return ret, err + return &middlewareLister{listers.New[*traefikiov1alpha1.Middleware](indexer, traefikiov1alpha1.Resource("middleware"))} } // Middlewares returns an object that can list and get Middlewares. func (s *middlewareLister) Middlewares(namespace string) MiddlewareNamespaceLister { - return middlewareNamespaceLister{indexer: s.indexer, namespace: namespace} + return middlewareNamespaceLister{listers.NewNamespaced[*traefikiov1alpha1.Middleware](s.ResourceIndexer, namespace)} } // MiddlewareNamespaceLister helps list and get Middlewares. @@ -72,36 +64,15 @@ func (s *middlewareLister) Middlewares(namespace string) MiddlewareNamespaceList type MiddlewareNamespaceLister interface { // List lists all Middlewares in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.Middleware, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.Middleware, err error) // Get retrieves the Middleware from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.Middleware, error) + Get(name string) (*traefikiov1alpha1.Middleware, error) MiddlewareNamespaceListerExpansion } // middlewareNamespaceLister implements the MiddlewareNamespaceLister // interface. type middlewareNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all Middlewares in the indexer for a given namespace. -func (s middlewareNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Middleware, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.Middleware)) - }) - return ret, err -} - -// Get retrieves the Middleware from the indexer for a given namespace and name. -func (s middlewareNamespaceLister) Get(name string) (*v1alpha1.Middleware, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("middleware"), name) - } - return obj.(*v1alpha1.Middleware), nil + listers.ResourceIndexer[*traefikiov1alpha1.Middleware] } diff --git a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/middlewaretcp.go b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/middlewaretcp.go index 547800f78..8eca35e2d 100644 --- a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/middlewaretcp.go +++ b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/middlewaretcp.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,10 +27,10 @@ THE SOFTWARE. package v1alpha1 import ( - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // MiddlewareTCPLister helps list MiddlewareTCPs. @@ -38,7 +38,7 @@ import ( type MiddlewareTCPLister interface { // List lists all MiddlewareTCPs in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.MiddlewareTCP, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.MiddlewareTCP, err error) // MiddlewareTCPs returns an object that can list and get MiddlewareTCPs. MiddlewareTCPs(namespace string) MiddlewareTCPNamespaceLister MiddlewareTCPListerExpansion @@ -46,25 +46,17 @@ type MiddlewareTCPLister interface { // middlewareTCPLister implements the MiddlewareTCPLister interface. type middlewareTCPLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*traefikiov1alpha1.MiddlewareTCP] } // NewMiddlewareTCPLister returns a new MiddlewareTCPLister. func NewMiddlewareTCPLister(indexer cache.Indexer) MiddlewareTCPLister { - return &middlewareTCPLister{indexer: indexer} -} - -// List lists all MiddlewareTCPs in the indexer. -func (s *middlewareTCPLister) List(selector labels.Selector) (ret []*v1alpha1.MiddlewareTCP, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.MiddlewareTCP)) - }) - return ret, err + return &middlewareTCPLister{listers.New[*traefikiov1alpha1.MiddlewareTCP](indexer, traefikiov1alpha1.Resource("middlewaretcp"))} } // MiddlewareTCPs returns an object that can list and get MiddlewareTCPs. func (s *middlewareTCPLister) MiddlewareTCPs(namespace string) MiddlewareTCPNamespaceLister { - return middlewareTCPNamespaceLister{indexer: s.indexer, namespace: namespace} + return middlewareTCPNamespaceLister{listers.NewNamespaced[*traefikiov1alpha1.MiddlewareTCP](s.ResourceIndexer, namespace)} } // MiddlewareTCPNamespaceLister helps list and get MiddlewareTCPs. @@ -72,36 +64,15 @@ func (s *middlewareTCPLister) MiddlewareTCPs(namespace string) MiddlewareTCPName type MiddlewareTCPNamespaceLister interface { // List lists all MiddlewareTCPs in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.MiddlewareTCP, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.MiddlewareTCP, err error) // Get retrieves the MiddlewareTCP from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.MiddlewareTCP, error) + Get(name string) (*traefikiov1alpha1.MiddlewareTCP, error) MiddlewareTCPNamespaceListerExpansion } // middlewareTCPNamespaceLister implements the MiddlewareTCPNamespaceLister // interface. type middlewareTCPNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all MiddlewareTCPs in the indexer for a given namespace. -func (s middlewareTCPNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.MiddlewareTCP, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.MiddlewareTCP)) - }) - return ret, err -} - -// Get retrieves the MiddlewareTCP from the indexer for a given namespace and name. -func (s middlewareTCPNamespaceLister) Get(name string) (*v1alpha1.MiddlewareTCP, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("middlewaretcp"), name) - } - return obj.(*v1alpha1.MiddlewareTCP), nil + listers.ResourceIndexer[*traefikiov1alpha1.MiddlewareTCP] } diff --git a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/serverstransport.go b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/serverstransport.go index f95ea600f..3b9374428 100644 --- a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/serverstransport.go +++ b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/serverstransport.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,10 +27,10 @@ THE SOFTWARE. package v1alpha1 import ( - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // ServersTransportLister helps list ServersTransports. @@ -38,7 +38,7 @@ import ( type ServersTransportLister interface { // List lists all ServersTransports in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.ServersTransport, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.ServersTransport, err error) // ServersTransports returns an object that can list and get ServersTransports. ServersTransports(namespace string) ServersTransportNamespaceLister ServersTransportListerExpansion @@ -46,25 +46,17 @@ type ServersTransportLister interface { // serversTransportLister implements the ServersTransportLister interface. type serversTransportLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*traefikiov1alpha1.ServersTransport] } // NewServersTransportLister returns a new ServersTransportLister. func NewServersTransportLister(indexer cache.Indexer) ServersTransportLister { - return &serversTransportLister{indexer: indexer} -} - -// List lists all ServersTransports in the indexer. -func (s *serversTransportLister) List(selector labels.Selector) (ret []*v1alpha1.ServersTransport, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.ServersTransport)) - }) - return ret, err + return &serversTransportLister{listers.New[*traefikiov1alpha1.ServersTransport](indexer, traefikiov1alpha1.Resource("serverstransport"))} } // ServersTransports returns an object that can list and get ServersTransports. func (s *serversTransportLister) ServersTransports(namespace string) ServersTransportNamespaceLister { - return serversTransportNamespaceLister{indexer: s.indexer, namespace: namespace} + return serversTransportNamespaceLister{listers.NewNamespaced[*traefikiov1alpha1.ServersTransport](s.ResourceIndexer, namespace)} } // ServersTransportNamespaceLister helps list and get ServersTransports. @@ -72,36 +64,15 @@ func (s *serversTransportLister) ServersTransports(namespace string) ServersTran type ServersTransportNamespaceLister interface { // List lists all ServersTransports in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.ServersTransport, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.ServersTransport, err error) // Get retrieves the ServersTransport from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.ServersTransport, error) + Get(name string) (*traefikiov1alpha1.ServersTransport, error) ServersTransportNamespaceListerExpansion } // serversTransportNamespaceLister implements the ServersTransportNamespaceLister // interface. type serversTransportNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all ServersTransports in the indexer for a given namespace. -func (s serversTransportNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.ServersTransport, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.ServersTransport)) - }) - return ret, err -} - -// Get retrieves the ServersTransport from the indexer for a given namespace and name. -func (s serversTransportNamespaceLister) Get(name string) (*v1alpha1.ServersTransport, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("serverstransport"), name) - } - return obj.(*v1alpha1.ServersTransport), nil + listers.ResourceIndexer[*traefikiov1alpha1.ServersTransport] } diff --git a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/serverstransporttcp.go b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/serverstransporttcp.go index 4f43abf78..8b4bba0a6 100644 --- a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/serverstransporttcp.go +++ b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/serverstransporttcp.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,10 +27,10 @@ THE SOFTWARE. package v1alpha1 import ( - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // ServersTransportTCPLister helps list ServersTransportTCPs. @@ -38,7 +38,7 @@ import ( type ServersTransportTCPLister interface { // List lists all ServersTransportTCPs in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.ServersTransportTCP, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.ServersTransportTCP, err error) // ServersTransportTCPs returns an object that can list and get ServersTransportTCPs. ServersTransportTCPs(namespace string) ServersTransportTCPNamespaceLister ServersTransportTCPListerExpansion @@ -46,25 +46,17 @@ type ServersTransportTCPLister interface { // serversTransportTCPLister implements the ServersTransportTCPLister interface. type serversTransportTCPLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*traefikiov1alpha1.ServersTransportTCP] } // NewServersTransportTCPLister returns a new ServersTransportTCPLister. func NewServersTransportTCPLister(indexer cache.Indexer) ServersTransportTCPLister { - return &serversTransportTCPLister{indexer: indexer} -} - -// List lists all ServersTransportTCPs in the indexer. -func (s *serversTransportTCPLister) List(selector labels.Selector) (ret []*v1alpha1.ServersTransportTCP, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.ServersTransportTCP)) - }) - return ret, err + return &serversTransportTCPLister{listers.New[*traefikiov1alpha1.ServersTransportTCP](indexer, traefikiov1alpha1.Resource("serverstransporttcp"))} } // ServersTransportTCPs returns an object that can list and get ServersTransportTCPs. func (s *serversTransportTCPLister) ServersTransportTCPs(namespace string) ServersTransportTCPNamespaceLister { - return serversTransportTCPNamespaceLister{indexer: s.indexer, namespace: namespace} + return serversTransportTCPNamespaceLister{listers.NewNamespaced[*traefikiov1alpha1.ServersTransportTCP](s.ResourceIndexer, namespace)} } // ServersTransportTCPNamespaceLister helps list and get ServersTransportTCPs. @@ -72,36 +64,15 @@ func (s *serversTransportTCPLister) ServersTransportTCPs(namespace string) Serve type ServersTransportTCPNamespaceLister interface { // List lists all ServersTransportTCPs in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.ServersTransportTCP, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.ServersTransportTCP, err error) // Get retrieves the ServersTransportTCP from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.ServersTransportTCP, error) + Get(name string) (*traefikiov1alpha1.ServersTransportTCP, error) ServersTransportTCPNamespaceListerExpansion } // serversTransportTCPNamespaceLister implements the ServersTransportTCPNamespaceLister // interface. type serversTransportTCPNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all ServersTransportTCPs in the indexer for a given namespace. -func (s serversTransportTCPNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.ServersTransportTCP, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.ServersTransportTCP)) - }) - return ret, err -} - -// Get retrieves the ServersTransportTCP from the indexer for a given namespace and name. -func (s serversTransportTCPNamespaceLister) Get(name string) (*v1alpha1.ServersTransportTCP, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("serverstransporttcp"), name) - } - return obj.(*v1alpha1.ServersTransportTCP), nil + listers.ResourceIndexer[*traefikiov1alpha1.ServersTransportTCP] } diff --git a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/tlsoption.go b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/tlsoption.go index f5c00ad7d..495b134e8 100644 --- a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/tlsoption.go +++ b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/tlsoption.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,10 +27,10 @@ THE SOFTWARE. package v1alpha1 import ( - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // TLSOptionLister helps list TLSOptions. @@ -38,7 +38,7 @@ import ( type TLSOptionLister interface { // List lists all TLSOptions in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.TLSOption, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.TLSOption, err error) // TLSOptions returns an object that can list and get TLSOptions. TLSOptions(namespace string) TLSOptionNamespaceLister TLSOptionListerExpansion @@ -46,25 +46,17 @@ type TLSOptionLister interface { // tLSOptionLister implements the TLSOptionLister interface. type tLSOptionLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*traefikiov1alpha1.TLSOption] } // NewTLSOptionLister returns a new TLSOptionLister. func NewTLSOptionLister(indexer cache.Indexer) TLSOptionLister { - return &tLSOptionLister{indexer: indexer} -} - -// List lists all TLSOptions in the indexer. -func (s *tLSOptionLister) List(selector labels.Selector) (ret []*v1alpha1.TLSOption, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.TLSOption)) - }) - return ret, err + return &tLSOptionLister{listers.New[*traefikiov1alpha1.TLSOption](indexer, traefikiov1alpha1.Resource("tlsoption"))} } // TLSOptions returns an object that can list and get TLSOptions. func (s *tLSOptionLister) TLSOptions(namespace string) TLSOptionNamespaceLister { - return tLSOptionNamespaceLister{indexer: s.indexer, namespace: namespace} + return tLSOptionNamespaceLister{listers.NewNamespaced[*traefikiov1alpha1.TLSOption](s.ResourceIndexer, namespace)} } // TLSOptionNamespaceLister helps list and get TLSOptions. @@ -72,36 +64,15 @@ func (s *tLSOptionLister) TLSOptions(namespace string) TLSOptionNamespaceLister type TLSOptionNamespaceLister interface { // List lists all TLSOptions in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.TLSOption, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.TLSOption, err error) // Get retrieves the TLSOption from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.TLSOption, error) + Get(name string) (*traefikiov1alpha1.TLSOption, error) TLSOptionNamespaceListerExpansion } // tLSOptionNamespaceLister implements the TLSOptionNamespaceLister // interface. type tLSOptionNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all TLSOptions in the indexer for a given namespace. -func (s tLSOptionNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.TLSOption, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.TLSOption)) - }) - return ret, err -} - -// Get retrieves the TLSOption from the indexer for a given namespace and name. -func (s tLSOptionNamespaceLister) Get(name string) (*v1alpha1.TLSOption, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("tlsoption"), name) - } - return obj.(*v1alpha1.TLSOption), nil + listers.ResourceIndexer[*traefikiov1alpha1.TLSOption] } diff --git a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/tlsstore.go b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/tlsstore.go index a5529027c..787246420 100644 --- a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/tlsstore.go +++ b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/tlsstore.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,10 +27,10 @@ THE SOFTWARE. package v1alpha1 import ( - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // TLSStoreLister helps list TLSStores. @@ -38,7 +38,7 @@ import ( type TLSStoreLister interface { // List lists all TLSStores in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.TLSStore, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.TLSStore, err error) // TLSStores returns an object that can list and get TLSStores. TLSStores(namespace string) TLSStoreNamespaceLister TLSStoreListerExpansion @@ -46,25 +46,17 @@ type TLSStoreLister interface { // tLSStoreLister implements the TLSStoreLister interface. type tLSStoreLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*traefikiov1alpha1.TLSStore] } // NewTLSStoreLister returns a new TLSStoreLister. func NewTLSStoreLister(indexer cache.Indexer) TLSStoreLister { - return &tLSStoreLister{indexer: indexer} -} - -// List lists all TLSStores in the indexer. -func (s *tLSStoreLister) List(selector labels.Selector) (ret []*v1alpha1.TLSStore, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.TLSStore)) - }) - return ret, err + return &tLSStoreLister{listers.New[*traefikiov1alpha1.TLSStore](indexer, traefikiov1alpha1.Resource("tlsstore"))} } // TLSStores returns an object that can list and get TLSStores. func (s *tLSStoreLister) TLSStores(namespace string) TLSStoreNamespaceLister { - return tLSStoreNamespaceLister{indexer: s.indexer, namespace: namespace} + return tLSStoreNamespaceLister{listers.NewNamespaced[*traefikiov1alpha1.TLSStore](s.ResourceIndexer, namespace)} } // TLSStoreNamespaceLister helps list and get TLSStores. @@ -72,36 +64,15 @@ func (s *tLSStoreLister) TLSStores(namespace string) TLSStoreNamespaceLister { type TLSStoreNamespaceLister interface { // List lists all TLSStores in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.TLSStore, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.TLSStore, err error) // Get retrieves the TLSStore from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.TLSStore, error) + Get(name string) (*traefikiov1alpha1.TLSStore, error) TLSStoreNamespaceListerExpansion } // tLSStoreNamespaceLister implements the TLSStoreNamespaceLister // interface. type tLSStoreNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all TLSStores in the indexer for a given namespace. -func (s tLSStoreNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.TLSStore, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.TLSStore)) - }) - return ret, err -} - -// Get retrieves the TLSStore from the indexer for a given namespace and name. -func (s tLSStoreNamespaceLister) Get(name string) (*v1alpha1.TLSStore, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("tlsstore"), name) - } - return obj.(*v1alpha1.TLSStore), nil + listers.ResourceIndexer[*traefikiov1alpha1.TLSStore] } diff --git a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/traefikservice.go b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/traefikservice.go index 229e6fe67..5712a2628 100644 --- a/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/traefikservice.go +++ b/pkg/provider/kubernetes/crd/generated/listers/traefikio/v1alpha1/traefikservice.go @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -27,10 +27,10 @@ THE SOFTWARE. package v1alpha1 import ( - v1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" + traefikiov1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" + labels "k8s.io/apimachinery/pkg/labels" + listers "k8s.io/client-go/listers" + cache "k8s.io/client-go/tools/cache" ) // TraefikServiceLister helps list TraefikServices. @@ -38,7 +38,7 @@ import ( type TraefikServiceLister interface { // List lists all TraefikServices in the indexer. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.TraefikService, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.TraefikService, err error) // TraefikServices returns an object that can list and get TraefikServices. TraefikServices(namespace string) TraefikServiceNamespaceLister TraefikServiceListerExpansion @@ -46,25 +46,17 @@ type TraefikServiceLister interface { // traefikServiceLister implements the TraefikServiceLister interface. type traefikServiceLister struct { - indexer cache.Indexer + listers.ResourceIndexer[*traefikiov1alpha1.TraefikService] } // NewTraefikServiceLister returns a new TraefikServiceLister. func NewTraefikServiceLister(indexer cache.Indexer) TraefikServiceLister { - return &traefikServiceLister{indexer: indexer} -} - -// List lists all TraefikServices in the indexer. -func (s *traefikServiceLister) List(selector labels.Selector) (ret []*v1alpha1.TraefikService, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.TraefikService)) - }) - return ret, err + return &traefikServiceLister{listers.New[*traefikiov1alpha1.TraefikService](indexer, traefikiov1alpha1.Resource("traefikservice"))} } // TraefikServices returns an object that can list and get TraefikServices. func (s *traefikServiceLister) TraefikServices(namespace string) TraefikServiceNamespaceLister { - return traefikServiceNamespaceLister{indexer: s.indexer, namespace: namespace} + return traefikServiceNamespaceLister{listers.NewNamespaced[*traefikiov1alpha1.TraefikService](s.ResourceIndexer, namespace)} } // TraefikServiceNamespaceLister helps list and get TraefikServices. @@ -72,36 +64,15 @@ func (s *traefikServiceLister) TraefikServices(namespace string) TraefikServiceN type TraefikServiceNamespaceLister interface { // List lists all TraefikServices in the indexer for a given namespace. // Objects returned here must be treated as read-only. - List(selector labels.Selector) (ret []*v1alpha1.TraefikService, err error) + List(selector labels.Selector) (ret []*traefikiov1alpha1.TraefikService, err error) // Get retrieves the TraefikService from the indexer for a given namespace and name. // Objects returned here must be treated as read-only. - Get(name string) (*v1alpha1.TraefikService, error) + Get(name string) (*traefikiov1alpha1.TraefikService, error) TraefikServiceNamespaceListerExpansion } // traefikServiceNamespaceLister implements the TraefikServiceNamespaceLister // interface. type traefikServiceNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all TraefikServices in the indexer for a given namespace. -func (s traefikServiceNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.TraefikService, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.TraefikService)) - }) - return ret, err -} - -// Get retrieves the TraefikService from the indexer for a given namespace and name. -func (s traefikServiceNamespaceLister) Get(name string) (*v1alpha1.TraefikService, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("traefikservice"), name) - } - return obj.(*v1alpha1.TraefikService), nil + listers.ResourceIndexer[*traefikiov1alpha1.TraefikService] } diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go index b9f5c0aa5..92b45a124 100644 --- a/pkg/provider/kubernetes/crd/kubernetes.go +++ b/pkg/provider/kubernetes/crd/kubernetes.go @@ -1379,15 +1379,18 @@ func buildCertificates(client Client, tlsStore, namespace string, certificates [ return nil } -func makeServiceKey(rule, ingressName string) (string, error) { +func makeServiceKey(rule, ingressName string) string { h := sha256.New() + + // As explained in https://pkg.go.dev/hash#Hash, + // Write never returns an error. if _, err := h.Write([]byte(rule)); err != nil { - return "", err + return "" } key := fmt.Sprintf("%s-%.10x", ingressName, h.Sum(nil)) - return key, nil + return key } func makeID(namespace, name string) string { diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go index 2e907d6c3..be9490ad6 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_http.go +++ b/pkg/provider/kubernetes/crd/kubernetes_http.go @@ -14,7 +14,6 @@ import ( "github.com/traefik/traefik/v3/pkg/observability/logs" "github.com/traefik/traefik/v3/pkg/provider" traefikv1alpha1 "github.com/traefik/traefik/v3/pkg/provider/kubernetes/crd/traefikio/v1alpha1" - "github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s" "github.com/traefik/traefik/v3/pkg/tls" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/intstr" @@ -61,6 +60,12 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli disableClusterScopeResources: p.DisableClusterScopeResources, } + parentRouterNames, err := resolveParentRouterNames(client, ingressRoute, p.AllowCrossNamespace) + if err != nil { + logger.Error().Err(err).Msg("Error resolving parent routers") + continue + } + for _, route := range ingressRoute.Spec.Routes { if len(route.Kind) > 0 && route.Kind != "Rule" { logger.Error().Msgf("Unsupported match kind: %s. Only \"Rule\" is supported for now.", route.Kind) @@ -72,11 +77,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli continue } - serviceKey, err := makeServiceKey(route.Match, ingressName) - if err != nil { - logger.Error().Err(err).Send() - continue - } + serviceKey := makeServiceKey(route.Match, ingressName) mds, err := p.makeMiddlewareKeys(ctx, ingressRoute.Namespace, route.Middlewares) if err != nil { @@ -87,7 +88,8 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli normalized := provider.Normalize(makeID(ingressRoute.Namespace, serviceKey)) serviceName := normalized - if len(route.Services) > 1 { + switch { + case len(route.Services) > 1: spec := traefikv1alpha1.TraefikServiceSpec{ Weighted: &traefikv1alpha1.WeightedRoundRobin{ Services: route.Services, @@ -99,7 +101,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli logger.Error().Err(errBuild).Send() continue } - } else if len(route.Services) == 1 { + case len(route.Services) == 1: fullName, serversLB, err := cb.nameAndService(ctx, ingressRoute.Namespace, route.Services[0].LoadBalancerSpec) if err != nil { logger.Error().Err(err).Send() @@ -111,6 +113,9 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli } else { serviceName = fullName } + default: + // Routes without services leave serviceName empty. + serviceName = "" } r := &dynamic.Router{ @@ -121,6 +126,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli Rule: route.Match, Service: serviceName, Observability: route.Observability, + ParentRefs: parentRouterNames, } if ingressRoute.Spec.TLS != nil { @@ -202,6 +208,50 @@ func (p *Provider) makeMiddlewareKeys(ctx context.Context, ingRouteNamespace str return mds, nil } +// resolveParentRouterNames resolves parent IngressRoute references to router names. +// It returns the list of parent router names and an error if one occurred during processing. +func resolveParentRouterNames(client Client, ingressRoute *traefikv1alpha1.IngressRoute, allowCrossNamespace bool) ([]string, error) { + // If no parent refs, return empty list (not an error). + if len(ingressRoute.Spec.ParentRefs) == 0 { + return nil, nil + } + + var parentRouterNames []string + for _, parentRef := range ingressRoute.Spec.ParentRefs { + // Determine parent namespace (default to child namespace if not specified). + parentNamespace := parentRef.Namespace + if parentNamespace == "" { + parentNamespace = ingressRoute.Namespace + } + + // Validate cross-namespace access. + if !isNamespaceAllowed(allowCrossNamespace, ingressRoute.Namespace, parentNamespace) { + return nil, fmt.Errorf("cross-namespace reference to parent IngressRoute %s/%s not allowed", parentNamespace, parentRef.Name) + } + + var parentIngressRoute *traefikv1alpha1.IngressRoute + for _, ir := range client.GetIngressRoutes() { + if ir.Name == parentRef.Name && ir.Namespace == parentNamespace { + parentIngressRoute = ir + break + } + } + + if parentIngressRoute == nil { + return nil, fmt.Errorf("parent IngressRoute %s/%s does not exist", parentNamespace, parentRef.Name) + } + + // Compute router names for all routes in parent IngressRoute. + for _, route := range parentIngressRoute.Spec.Routes { + serviceKey := makeServiceKey(route.Match, parentIngressRoute.Name) + routerName := provider.Normalize(makeID(parentIngressRoute.Namespace, serviceKey)) + parentRouterNames = append(parentRouterNames, routerName) + } + } + + return parentRouterNames, nil +} + type configBuilder struct { client Client allowCrossNamespace bool @@ -216,13 +266,17 @@ type configBuilder struct { func (c configBuilder) buildTraefikService(ctx context.Context, tService *traefikv1alpha1.TraefikService, conf map[string]*dynamic.Service) error { id := provider.Normalize(makeID(tService.Namespace, tService.Name)) - if tService.Spec.Weighted != nil { + switch { + case tService.Spec.Weighted != nil: return c.buildServicesLB(ctx, tService.Namespace, tService.Spec, id, conf) - } else if tService.Spec.Mirroring != nil { + case tService.Spec.Mirroring != nil: return c.buildMirroring(ctx, tService, id, conf) - } + case tService.Spec.HighestRandomWeight != nil: + return c.buildHRW(ctx, tService, id, conf) + default: - return errors.New("unspecified service type") + return errors.New("unspecified service type") + } } // buildServicesLB creates the configuration for the load-balancer of services named id, and defined in tService. @@ -329,7 +383,7 @@ func (c configBuilder) buildServersLB(namespace string, svc traefikv1alpha1.Load // TODO: remove this when the fake client apply default values. if svc.Strategy != "" { switch svc.Strategy { - case dynamic.BalancerStrategyWRR, dynamic.BalancerStrategyP2C: + case dynamic.BalancerStrategyWRR, dynamic.BalancerStrategyP2C, dynamic.BalancerStrategyHRW, dynamic.BalancerStrategyLeastTime: lb.Strategy = svc.Strategy // Here we are just logging a warning as the default value is already applied. @@ -392,6 +446,21 @@ func (c configBuilder) buildServersLB(namespace string, svc traefikv1alpha1.Load } } + if svc.PassiveHealthCheck != nil { + lb.PassiveHealthCheck = &dynamic.PassiveServerHealthCheck{} + lb.PassiveHealthCheck.SetDefaults() + + if svc.PassiveHealthCheck.MaxFailedAttempts != nil { + lb.PassiveHealthCheck.MaxFailedAttempts = *svc.PassiveHealthCheck.MaxFailedAttempts + } + + if svc.PassiveHealthCheck.FailureWindow != nil { + if err := lb.PassiveHealthCheck.FailureWindow.Set(svc.PassiveHealthCheck.FailureWindow.String()); err != nil { + return nil, err + } + } + } + conf := svc lb.PassHostHeader = conf.PassHostHeader if lb.PassHostHeader == nil { @@ -572,7 +641,10 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L } for _, endpoint := range endpointSlice.Endpoints { - if !k8s.EndpointServing(endpoint) { + // The Serving condition allows to track if the Pod can receive traffic. + // It is set to true when the Pod is Ready or Terminating. + // From the go documentation, a nil value should be interpreted as "true". + if !ptr.Deref(endpoint.Conditions.Serving, true) { continue } @@ -584,7 +656,7 @@ func (c configBuilder) loadServers(parentNamespace string, svc traefikv1alpha1.L addresses[address] = struct{}{} servers = append(servers, dynamic.Server{ URL: fmt.Sprintf("%s://%s", protocol, net.JoinHostPort(address, strconv.Itoa(int(port)))), - Fenced: ptr.Deref(endpoint.Conditions.Terminating, false) && ptr.Deref(endpoint.Conditions.Serving, false), + Fenced: ptr.Deref(endpoint.Conditions.Terminating, false), }) } } @@ -629,6 +701,38 @@ func (c configBuilder) nameAndService(ctx context.Context, parentNamespace strin } } +func (c configBuilder) buildHRW(ctx context.Context, tService *traefikv1alpha1.TraefikService, id string, conf map[string]*dynamic.Service) error { + var hrwServices []dynamic.HRWService + for _, hrwService := range tService.Spec.HighestRandomWeight.Services { + hrwServiceName, k8sService, err := c.nameAndService(ctx, tService.Namespace, hrwService.LoadBalancerSpec) + if err != nil { + return err + } + + if k8sService != nil { + conf[hrwServiceName] = k8sService + } + + weight := hrwService.Weight + if weight == nil { + weight = func(i int) *int { return &i }(1) + } + + hrwServices = append(hrwServices, dynamic.HRWService{ + Name: hrwServiceName, + Weight: weight, + }) + } + + conf[id] = &dynamic.Service{ + HighestRandomWeight: &dynamic.HighestRandomWeight{ + Services: hrwServices, + }, + } + + return nil +} + func splitSvcNameProvider(name string) (string, string) { parts := strings.Split(name, providerNamespaceSeparator) diff --git a/pkg/provider/kubernetes/crd/kubernetes_tcp.go b/pkg/provider/kubernetes/crd/kubernetes_tcp.go index c4c3fa6d4..bbe48f638 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_tcp.go +++ b/pkg/provider/kubernetes/crd/kubernetes_tcp.go @@ -50,11 +50,7 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client continue } - key, err := makeServiceKey(route.Match, ingressName) - if err != nil { - logger.Error().Err(err).Send() - continue - } + key := makeServiceKey(route.Match, ingressName) mds, err := p.makeMiddlewareTCPKeys(ctx, ingressRouteTCP.Namespace, route.Middlewares) if err != nil { diff --git a/pkg/provider/kubernetes/crd/kubernetes_test.go b/pkg/provider/kubernetes/crd/kubernetes_test.go index ec6651091..018a3dd43 100644 --- a/pkg/provider/kubernetes/crd/kubernetes_test.go +++ b/pkg/provider/kubernetes/crd/kubernetes_test.go @@ -1655,7 +1655,7 @@ func TestLoadIngressRouteTCPs(t *testing.T) { k8sObjects, crdObjects := readResources(t, test.paths) kubeClient := kubefake.NewClientset(k8sObjects...) - crdClient := traefikcrdfake.NewSimpleClientset(crdObjects...) + crdClient := traefikcrdfake.NewClientset(crdObjects...) client := newClientImpl(kubeClient, crdClient) @@ -3150,6 +3150,79 @@ func TestLoadIngressRoutes(t *testing.T) { }, }, }, + { + desc: "one kube services in a highest random weight", + paths: []string{"with_highest_random_weight.yml"}, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TLS: &dynamic.TLSConfiguration{}, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "default-test-route-77c62dfe9517144aeeaa": { + EntryPoints: []string{"web"}, + Service: "default-hrw1", + Rule: "Host(`foo.com`) && PathPrefix(`/foo`)", + Priority: 12, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "default-hrw1": { + HighestRandomWeight: &dynamic.HighestRandomWeight{ + Services: []dynamic.HRWService{ + {Name: "default-whoami1-8080", Weight: pointer(10)}, + {Name: "default-whoami2-8080", Weight: pointer(20)}, + }, + }, + }, + "default-whoami1-8080": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, + Servers: []dynamic.Server{ + { + URL: "http://10.10.0.1:8080", + }, + { + URL: "http://10.10.0.2:8080", + }, + }, + PassHostHeader: pointer(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: ptypes.Duration(100 * time.Millisecond), + }, + }, + }, + "default-whoami2-8080": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, + Servers: []dynamic.Server{ + { + URL: "http://10.10.0.3:8080", + }, + { + URL: "http://10.10.0.4:8080", + }, + }, + PassHostHeader: pointer(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: ptypes.Duration(100 * time.Millisecond), + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + }, + }, { desc: "One ingress Route with two different services, with weights", paths: []string{"services.yml", "with_two_services_weight.yml"}, @@ -5278,6 +5351,370 @@ func TestLoadIngressRoutes(t *testing.T) { TLS: &dynamic.TLSConfiguration{}, }, }, + { + desc: "IngressRoute with single parent (single route)", + paths: []string{"parent_refs_services.yml", "parent_refs_single_parent_single_route.yml"}, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "default-parent-single-3c07cfffe8e5f876a01e": { + EntryPoints: []string{"web"}, + Rule: "Host(`parent.example.com`)", + }, + "default-child-single-2bba0a3de1b50b70a519": { + Service: "default-child-single-2bba0a3de1b50b70a519", + Rule: "Path(`/api`)", + ParentRefs: []string{"default-parent-single-3c07cfffe8e5f876a01e"}, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "default-child-single-2bba0a3de1b50b70a519": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, + Servers: []dynamic.Server{ + { + URL: "http://10.10.2.1:9000", + }, + { + URL: "http://10.10.2.2:9000", + }, + }, + PassHostHeader: pointer(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: ptypes.Duration(100 * time.Millisecond), + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "IngressRoute with single parent (multiple routes) - all parent routers in ParentRefs", + paths: []string{"parent_refs_services.yml", "parent_refs_single_parent_multiple_routes.yml"}, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "default-parent-multi-4aac0d541c2b669a2d5d": { + EntryPoints: []string{"web"}, + Rule: "Host(`api.example.com`) && PathPrefix(`/v1`)", + }, + "default-parent-multi-0af1ca0a94f5b87a125e": { + EntryPoints: []string{"web"}, + Rule: "Host(`api.example.com`) && PathPrefix(`/v2`)", + }, + "default-child-multi-routes-b0479051e6a353d66211": { + Service: "default-child-multi-routes-b0479051e6a353d66211", + Rule: "Path(`/users`)", + ParentRefs: []string{"default-parent-multi-4aac0d541c2b669a2d5d", "default-parent-multi-0af1ca0a94f5b87a125e"}, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "default-child-multi-routes-b0479051e6a353d66211": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, + Servers: []dynamic.Server{ + { + URL: "http://10.10.5.1:9000", + }, + { + URL: "http://10.10.5.2:9000", + }, + }, + PassHostHeader: pointer(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: ptypes.Duration(100 * time.Millisecond), + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "IngressRoute with multiple parents", + paths: []string{"parent_refs_services.yml", "parent_refs_multiple_parents.yml"}, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "default-parent-a-629990b524bf9a1a8d27": { + EntryPoints: []string{"web"}, + Rule: "Host(`a.example.com`)", + }, + "default-parent-b-add617f9b95cff009054": { + EntryPoints: []string{"web"}, + Rule: "Host(`b.example.com`)", + }, + "default-child-multi-parents-8013b5025acddd1761d1": { + Service: "default-child-multi-parents-8013b5025acddd1761d1", + Rule: "Path(`/shared`)", + ParentRefs: []string{"default-parent-a-629990b524bf9a1a8d27", "default-parent-b-add617f9b95cff009054"}, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "default-child-multi-parents-8013b5025acddd1761d1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, + Servers: []dynamic.Server{ + { + URL: "http://10.10.8.1:9000", + }, + { + URL: "http://10.10.8.2:9000", + }, + }, + PassHostHeader: pointer(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: ptypes.Duration(100 * time.Millisecond), + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "IngressRoute with missing parent - routers skipped", + paths: []string{"parent_refs_services.yml", "parent_refs_missing_parent.yml"}, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "IngressRoute with cross-namespace parent allowed", + allowCrossNamespace: true, + paths: []string{"parent_refs_services.yml", "parent_refs_cross_namespace_allowed.yml"}, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "ns-a-parent-cross-74575ab54671a3ede28c": { + EntryPoints: []string{"web"}, + Rule: "Host(`cross.example.com`)", + }, + "ns-b-child-cross-allowed-0bad04de665623bf2362": { + Service: "ns-b-child-cross-allowed-0bad04de665623bf2362", + Rule: "Path(`/cross`)", + ParentRefs: []string{"ns-a-parent-cross-74575ab54671a3ede28c"}, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "ns-b-child-cross-allowed-0bad04de665623bf2362": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, + Servers: []dynamic.Server{ + { + URL: "http://10.10.11.1:9000", + }, + { + URL: "http://10.10.11.2:9000", + }, + }, + PassHostHeader: pointer(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: ptypes.Duration(100 * time.Millisecond), + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "IngressRoute with cross-namespace parent denied", + allowCrossNamespace: false, + paths: []string{"parent_refs_services.yml", "parent_refs_cross_namespace_denied.yml"}, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "ns-a-parent-cross-74575ab54671a3ede28c": { + EntryPoints: []string{"web"}, + Rule: "Host(`cross.example.com`)", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "IngressRoute with parent namespace defaulting to child namespace", + paths: []string{"parent_refs_services.yml", "parent_refs_default_namespace.yml"}, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "default-parent-default-9b8ab283eeed3eb66561": { + EntryPoints: []string{"web"}, + Rule: "Host(`default.example.com`)", + }, + "default-child-same-9234eba1edcfbd8a7723": { + Service: "default-child-same-9234eba1edcfbd8a7723", + Rule: "Path(`/same`)", + ParentRefs: []string{"default-parent-default-9b8ab283eeed3eb66561"}, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "default-child-same-9234eba1edcfbd8a7723": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, + Servers: []dynamic.Server{ + { + URL: "http://10.10.14.1:9000", + }, + { + URL: "http://10.10.14.2:9000", + }, + }, + PassHostHeader: pointer(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: ptypes.Duration(100 * time.Millisecond), + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, + { + desc: "Simple Ingress Route with leasttime strategy", + paths: []string{"services.yml", "with_leasttime_strategy.yml"}, + expected: &dynamic.Configuration{ + UDP: &dynamic.UDPConfiguration{ + Routers: map[string]*dynamic.UDPRouter{}, + Services: map[string]*dynamic.UDPService{}, + }, + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Middlewares: map[string]*dynamic.TCPMiddleware{}, + Services: map[string]*dynamic.TCPService{}, + ServersTransports: map[string]*dynamic.TCPServersTransport{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "default-test-route-55869f6407935ccfa805": { + EntryPoints: []string{"web"}, + Service: "default-test-route-55869f6407935ccfa805", + Rule: "Host(`foo.com`) && PathPrefix(`/leasttime`)", + Priority: 12, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "default-test-route-55869f6407935ccfa805": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyLeastTime, + Servers: []dynamic.Server{ + { + URL: "http://10.10.0.3:8080", + }, + { + URL: "http://10.10.0.4:8080", + }, + }, + PassHostHeader: pointer(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: ptypes.Duration(100 * time.Millisecond), + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, } for _, test := range testCases { @@ -5291,7 +5728,7 @@ func TestLoadIngressRoutes(t *testing.T) { k8sObjects, crdObjects := readResources(t, test.paths) kubeClient := kubefake.NewClientset(k8sObjects...) - crdClient := traefikcrdfake.NewSimpleClientset(crdObjects...) + crdClient := traefikcrdfake.NewClientset(crdObjects...) client := newClientImpl(kubeClient, crdClient) @@ -5373,7 +5810,7 @@ func TestLoadIngressRoutes_multipleEndpointAddresses(t *testing.T) { k8sObjects, crdObjects := readResources(t, []string{"services.yml", "with_multiple_endpointslices.yml"}) kubeClient := kubefake.NewClientset(k8sObjects...) - crdClient := traefikcrdfake.NewSimpleClientset(crdObjects...) + crdClient := traefikcrdfake.NewClientset(crdObjects...) client := newClientImpl(kubeClient, crdClient) @@ -5882,7 +6319,7 @@ func TestLoadIngressRouteUDPs(t *testing.T) { k8sObjects, crdObjects := readResources(t, test.paths) kubeClient := kubefake.NewClientset(k8sObjects...) - crdClient := traefikcrdfake.NewSimpleClientset(crdObjects...) + crdClient := traefikcrdfake.NewClientset(crdObjects...) client := newClientImpl(kubeClient, crdClient) @@ -7385,7 +7822,7 @@ func TestCrossNamespace(t *testing.T) { k8sObjects, crdObjects := readResources(t, test.paths) kubeClient := kubefake.NewClientset(k8sObjects...) - crdClient := traefikcrdfake.NewSimpleClientset(crdObjects...) + crdClient := traefikcrdfake.NewClientset(crdObjects...) client := newClientImpl(kubeClient, crdClient) @@ -7655,7 +8092,7 @@ func TestExternalNameService(t *testing.T) { k8sObjects, crdObjects := readResources(t, test.paths) kubeClient := kubefake.NewClientset(k8sObjects...) - crdClient := traefikcrdfake.NewSimpleClientset(crdObjects...) + crdClient := traefikcrdfake.NewClientset(crdObjects...) client := newClientImpl(kubeClient, crdClient) @@ -7837,7 +8274,7 @@ func TestNativeLB(t *testing.T) { k8sObjects, crdObjects := readResources(t, test.paths) kubeClient := kubefake.NewClientset(k8sObjects...) - crdClient := traefikcrdfake.NewSimpleClientset(crdObjects...) + crdClient := traefikcrdfake.NewClientset(crdObjects...) client := newClientImpl(kubeClient, crdClient) @@ -8103,7 +8540,7 @@ func TestNodePortLB(t *testing.T) { k8sObjects, crdObjects := readResources(t, test.paths) kubeClient := kubefake.NewClientset(k8sObjects...) - crdClient := traefikcrdfake.NewSimpleClientset(crdObjects...) + crdClient := traefikcrdfake.NewClientset(crdObjects...) client := newClientImpl(kubeClient, crdClient) @@ -8144,7 +8581,7 @@ func TestCreateBasicAuthCredentials(t *testing.T) { } kubeClient := kubefake.NewClientset(k8sObjects...) - crdClient := traefikcrdfake.NewSimpleClientset() + crdClient := traefikcrdfake.NewClientset() client := newClientImpl(kubeClient, crdClient) @@ -8749,7 +9186,7 @@ func TestGlobalNativeLB(t *testing.T) { } kubeClient := kubefake.NewClientset(k8sObjects...) - crdClient := traefikcrdfake.NewSimpleClientset(crdObjects...) + crdClient := traefikcrdfake.NewClientset(crdObjects...) client := newClientImpl(kubeClient, crdClient) diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go index 6fda0077d..36c50286b 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroute.go @@ -13,18 +13,22 @@ type IngressRouteSpec struct { Routes []Route `json:"routes"` // EntryPoints defines the list of entry point names to bind to. // Entry points have to be configured in the static configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + // More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/entrypoints/ // Default: all. EntryPoints []string `json:"entryPoints,omitempty"` // TLS defines the TLS configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/router/#tls + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/router/#tls TLS *TLS `json:"tls,omitempty"` + // ParentRefs defines references to parent IngressRoute resources for multi-layer routing. + // When set, this IngressRoute's routers will be children of the referenced parent IngressRoute's routers. + // More info: https://doc.traefik.io/traefik/v3.6/routing/routers/#parentrefs + ParentRefs []IngressRouteRef `json:"parentRefs,omitempty"` } // Route holds the HTTP route configuration. type Route struct { // Match defines the router's rule. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/ + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/ Match string `json:"match"` // Kind defines the kind of the route. // Rule is the only supported kind. @@ -32,62 +36,62 @@ type Route struct { // +kubebuilder:validation:Enum=Rule Kind string `json:"kind,omitempty"` // Priority defines the router's priority. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/#priority + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/#priority // +kubebuilder:validation:Maximum=9223372036854774807 Priority int `json:"priority,omitempty"` // Syntax defines the router's rule syntax. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/rules-and-priority/#rulesyntax // Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. Syntax string `json:"syntax,omitempty"` // Services defines the list of Service. // It can contain any combination of TraefikService and/or reference to a Kubernetes Service. Services []Service `json:"services,omitempty"` // Middlewares defines the list of references to Middleware resources. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/middleware/ + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/middleware/ Middlewares []MiddlewareRef `json:"middlewares,omitempty"` // Observability defines the observability configuration for a router. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/routing/observability/ + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/routing/observability/ Observability *dynamic.RouterObservabilityConfig `json:"observability,omitempty"` } // TLS holds the TLS configuration. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/overview/ +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/overview/ type TLS struct { // SecretName is the name of the referenced Kubernetes Secret to specify the certificate details. SecretName string `json:"secretName,omitempty"` // Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. // If not defined, the `default` TLSOption is used. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-options/ + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-options/ Options *TLSOptionRef `json:"options,omitempty"` // Store defines the reference to the TLSStore, that will be used to store certificates. // Please note that only `default` TLSStore can be used. Store *TLSStoreRef `json:"store,omitempty"` // CertResolver defines the name of the certificate resolver to use. // Cert resolvers have to be configured in the static configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/tls/certificate-resolvers/acme/ + // More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/tls/certificate-resolvers/acme/ CertResolver string `json:"certResolver,omitempty"` // Domains defines the list of domains that will be used to issue certificates. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#domains + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#domains Domains []types.Domain `json:"domains,omitempty"` } // TLSOptionRef is a reference to a TLSOption resource. type TLSOptionRef struct { // Name defines the name of the referenced TLSOption. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsoption/ + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/tlsoption/ Name string `json:"name"` // Namespace defines the namespace of the referenced TLSOption. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsoption/ + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/tlsoption/ Namespace string `json:"namespace,omitempty"` } // TLSStoreRef is a reference to a TLSStore resource. type TLSStoreRef struct { // Name defines the name of the referenced TLSStore. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsstore/ + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/tlsstore/ Name string `json:"name"` // Namespace defines the namespace of the referenced TLSStore. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/tlsstore/ + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/tlsstore/ Namespace string `json:"namespace,omitempty"` } @@ -104,7 +108,7 @@ type LoadBalancerSpec struct { // Namespace defines the namespace of the referenced Kubernetes Service or TraefikService. Namespace string `json:"namespace,omitempty"` // Sticky defines the sticky sessions configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#sticky-sessions + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#sticky-sessions Sticky *dynamic.Sticky `json:"sticky,omitempty"` // Port defines the port of a Kubernetes Service. // This can be a reference to a named port. @@ -114,10 +118,10 @@ type LoadBalancerSpec struct { // It defaults to https when Kubernetes Service port is 443, http otherwise. Scheme string `json:"scheme,omitempty"` // Strategy defines the load balancing strategy between the servers. - // Supported values are: wrr (Weighed round-robin) and p2c (Power of two choices). + // Supported values are: wrr (Weighed round-robin), p2c (Power of two choices), hrw (Highest Random Weight), and leasttime (Least-Time). // RoundRobin value is deprecated and supported for backward compatibility. - // TODO: when the deprecated RoundRobin value will be removed, set the default value to wrr. - // +kubebuilder:validation:Enum=wrr;p2c;RoundRobin + // TODO: when the deprecated RoundRobin value will be removed, set the default kubebuilder value to wrr. + // +kubebuilder:validation:Enum=wrr;p2c;hrw;leasttime;RoundRobin Strategy dynamic.BalancerStrategy `json:"strategy,omitempty"` // PassHostHeader defines whether the client Host header is forwarded to the upstream Kubernetes Service. // By default, passHostHeader is true. @@ -144,6 +148,8 @@ type LoadBalancerSpec struct { NodePortLB bool `json:"nodePortLB,omitempty"` // Healthcheck defines health checks for ExternalName services. HealthCheck *ServerHealthCheck `json:"healthCheck,omitempty"` + // PassiveHealthCheck defines passive health checks for ExternalName services. + PassiveHealthCheck *PassiveServerHealthCheck `json:"passiveHealthCheck,omitempty"` } type ResponseForwarding struct { @@ -189,6 +195,13 @@ type ServerHealthCheck struct { Headers map[string]string `json:"headers,omitempty"` } +type PassiveServerHealthCheck struct { + // FailureWindow defines the time window during which the failed attempts must occur for the server to be marked as unhealthy. It also defines for how long the server will be considered unhealthy. + FailureWindow *intstr.IntOrString `json:"failureWindow,omitempty"` + // MaxFailedAttempts is the number of consecutive failed attempts allowed within the failure window before marking the server as unhealthy. + MaxFailedAttempts *int `json:"maxFailedAttempts,omitempty"` +} + // Service defines an upstream HTTP service to proxy traffic to. type Service struct { LoadBalancerSpec `json:",inline"` @@ -202,6 +215,14 @@ type MiddlewareRef struct { Namespace string `json:"namespace,omitempty"` } +// IngressRouteRef is a reference to an IngressRoute resource. +type IngressRouteRef struct { + // Name defines the name of the referenced IngressRoute resource. + Name string `json:"name"` + // Namespace defines the namespace of the referenced IngressRoute resource. + Namespace string `json:"namespace,omitempty"` +} + // +genclient // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:storageversion diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroutetcp.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroutetcp.go index 5ef08dc02..1d719ee9a 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroutetcp.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressroutetcp.go @@ -13,25 +13,25 @@ type IngressRouteTCPSpec struct { Routes []RouteTCP `json:"routes"` // EntryPoints defines the list of entry point names to bind to. // Entry points have to be configured in the static configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + // More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/entrypoints/ // Default: all. EntryPoints []string `json:"entryPoints,omitempty"` // TLS defines the TLS configuration on a layer 4 / TCP Route. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/router/#tls + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/router/#tls TLS *TLSTCP `json:"tls,omitempty"` } // RouteTCP holds the TCP route configuration. type RouteTCP struct { // Match defines the router's rule. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/ + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/ Match string `json:"match"` // Priority defines the router's priority. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/#priority + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/#priority // +kubebuilder:validation:Maximum=9223372036854774807 Priority int `json:"priority,omitempty"` // Syntax defines the router's rule syntax. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/routing/rules-and-priority/#rulesyntax // +kubebuilder:validation:Enum=v3;v2 // Deprecated: Please do not use this field and rewrite the router rules to use the v3 syntax. Syntax string `json:"syntax,omitempty"` @@ -42,7 +42,7 @@ type RouteTCP struct { } // TLSTCP holds the TLS configuration for an IngressRouteTCP. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/ +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/tls/ type TLSTCP struct { // SecretName is the name of the referenced Kubernetes Secret to specify the certificate details. SecretName string `json:"secretName,omitempty"` @@ -50,17 +50,17 @@ type TLSTCP struct { Passthrough bool `json:"passthrough,omitempty"` // Options defines the reference to a TLSOption, that specifies the parameters of the TLS connection. // If not defined, the `default` TLSOption is used. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/#tls-options + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/tls/#tls-options Options *ObjectReference `json:"options,omitempty"` // Store defines the reference to the TLSStore, that will be used to store certificates. // Please note that only `default` TLSStore can be used. Store *ObjectReference `json:"store,omitempty"` // CertResolver defines the name of the certificate resolver to use. // Cert resolvers have to be configured in the static configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/tls/certificate-resolvers/acme/ + // More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/tls/certificate-resolvers/acme/ CertResolver string `json:"certResolver,omitempty"` // Domains defines the list of domains that will be used to issue certificates. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/tls/#domains + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/tls/#domains Domains []types.Domain `json:"domains,omitempty"` } @@ -85,7 +85,7 @@ type ServiceTCP struct { // Deprecated: TerminationDelay will not be supported in future APIVersions, please use ServersTransport to configure the TerminationDelay instead. TerminationDelay *int `json:"terminationDelay,omitempty"` // ProxyProtocol defines the PROXY protocol configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/service/#proxy-protocol + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/service/#proxy-protocol // Deprecated: ProxyProtocol will not be supported in future APIVersions, please use ServersTransport to configure ProxyProtocol instead. ProxyProtocol *dynamic.ProxyProtocol `json:"proxyProtocol,omitempty"` // ServersTransport defines the name of ServersTransportTCP resource to use. diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressrouteudp.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressrouteudp.go index 6e0038843..e7c8d1c6d 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressrouteudp.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/ingressrouteudp.go @@ -11,7 +11,7 @@ type IngressRouteUDPSpec struct { Routes []RouteUDP `json:"routes"` // EntryPoints defines the list of entry point names to bind to. // Entry points have to be configured in the static configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/install-configuration/entrypoints/ + // More info: https://doc.traefik.io/traefik/v3.6/reference/install-configuration/entrypoints/ // Default: all. EntryPoints []string `json:"entryPoints,omitempty"` } diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middleware.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middleware.go index 32354e2b5..d00e839d1 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middleware.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middleware.go @@ -12,7 +12,7 @@ import ( // +kubebuilder:storageversion // Middleware is the CRD implementation of a Traefik Middleware. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/overview/ +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/overview/ type Middleware struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. @@ -52,7 +52,7 @@ type MiddlewareSpec struct { ContentType *dynamic.ContentType `json:"contentType,omitempty"` GrpcWeb *dynamic.GrpcWeb `json:"grpcWeb,omitempty"` // Plugin defines the middleware plugin configuration. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/overview/#community-middlewares + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/overview/#community-middlewares Plugin map[string]apiextensionv1.JSON `json:"plugin,omitempty"` } @@ -60,7 +60,7 @@ type MiddlewareSpec struct { // ErrorPage holds the custom error middleware configuration. // This middleware returns a custom page in lieu of the default, according to configured ranges of HTTP Status codes. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/errorpages/ +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/errorpages/ type ErrorPage struct { // Status defines which status or range of statuses should result in an error page. // It can be either a status code as a number (500), @@ -73,7 +73,7 @@ type ErrorPage struct { // For example: "418": 404 or "410-418": 404 StatusRewrites map[string]int `json:"statusRewrites,omitempty"` // Service defines the reference to a Kubernetes Service that will serve the error page. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/errorpages/#service + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/errorpages/#service Service Service `json:"service,omitempty"` // Query defines the URL for the error page (hosted by service). // The {status} variable can be used in order to insert the status code in the URL. @@ -108,7 +108,7 @@ type CircuitBreaker struct { // Chain holds the configuration of the chain middleware. // This middleware enables to define reusable combinations of other pieces of middleware. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/chain/ +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/chain/ type Chain struct { // Middlewares is the list of MiddlewareRef which composes the chain. Middlewares []MiddlewareRef `json:"middlewares,omitempty"` @@ -118,7 +118,7 @@ type Chain struct { // BasicAuth holds the basic auth middleware configuration. // This middleware restricts access to your services to known users. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/basicauth/ +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/basicauth/ type BasicAuth struct { // Secret is the name of the referenced Kubernetes Secret containing user credentials. Secret string `json:"secret,omitempty"` @@ -129,7 +129,7 @@ type BasicAuth struct { // Default: false. RemoveHeader bool `json:"removeHeader,omitempty"` // HeaderField defines a header field to store the authenticated user. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/basicauth/#headerfield + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/basicauth/#headerfield HeaderField string `json:"headerField,omitempty"` } @@ -137,7 +137,7 @@ type BasicAuth struct { // DigestAuth holds the digest auth middleware configuration. // This middleware restricts access to your services to known users. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/digestauth/ +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/digestauth/ type DigestAuth struct { // Secret is the name of the referenced Kubernetes Secret containing user credentials. Secret string `json:"secret,omitempty"` @@ -147,7 +147,7 @@ type DigestAuth struct { // Default: traefik. Realm string `json:"realm,omitempty"` // HeaderField defines a header field to store the authenticated user. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/digestauth/#headerfield + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/digestauth/#headerfield HeaderField string `json:"headerField,omitempty"` } @@ -155,7 +155,7 @@ type DigestAuth struct { // ForwardAuth holds the forward auth middleware configuration. // This middleware delegates the request authentication to a Service. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/ +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/forwardauth/ type ForwardAuth struct { // Address defines the authentication server address. Address string `json:"address,omitempty"` @@ -164,7 +164,7 @@ type ForwardAuth struct { // AuthResponseHeaders defines the list of headers to copy from the authentication server response and set on forwarded request, replacing any existing conflicting headers. AuthResponseHeaders []string `json:"authResponseHeaders,omitempty"` // AuthResponseHeadersRegex defines the regex to match headers to copy from the authentication server response and set on forwarded request, after stripping all headers that match the regex. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/#authresponseheadersregex + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/forwardauth/#authresponseheadersregex AuthResponseHeadersRegex string `json:"authResponseHeadersRegex,omitempty"` // AuthRequestHeaders defines the list of the headers to copy from the request to the authentication server. // If not set or empty then all request headers are passed. @@ -174,7 +174,7 @@ type ForwardAuth struct { // AddAuthCookiesToResponse defines the list of cookies to copy from the authentication server response to the response. AddAuthCookiesToResponse []string `json:"addAuthCookiesToResponse,omitempty"` // HeaderField defines a header field to store the authenticated user. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/forwardauth/#headerfield + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/forwardauth/#headerfield HeaderField string `json:"headerField,omitempty"` // ForwardBody defines whether to send the request body to the authentication server. ForwardBody bool `json:"forwardBody,omitempty"` @@ -201,7 +201,7 @@ type ClientTLSWithCAOptional struct { // RateLimit holds the rate limit configuration. // This middleware ensures that services will receive a fair amount of requests, and allows one to define what fair is. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/ratelimit/ +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/ratelimit/ type RateLimit struct { // Average is the maximum rate, by default in requests/s, allowed for the given source. // It defaults to 0, which means no rate limiting. @@ -286,7 +286,7 @@ type ClientTLS struct { // Compress holds the compress middleware configuration. // This middleware compresses responses before sending them to the client, using gzip, brotli, or zstd compression. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/compress/ +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/compress/ type Compress struct { // ExcludedContentTypes defines the list of content types to compare the Content-Type header of the incoming requests and responses before compressing. // `application/grpc` is always excluded. @@ -308,7 +308,7 @@ type Compress struct { // Retry holds the retry middleware configuration. // This middleware reissues requests a given number of times to a backend server if that server does not reply. // As soon as the server answers, the middleware stops retrying, regardless of the response status. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/middlewares/retry/ +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/middlewares/retry/ type Retry struct { // Attempts defines how many times the request should be retried. // +kubebuilder:validation:Minimum=0 diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middlewaretcp.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middlewaretcp.go index 49309b02c..cf0e2a648 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middlewaretcp.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/middlewaretcp.go @@ -9,7 +9,7 @@ import ( // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // MiddlewareTCP is the CRD implementation of a Traefik TCP middleware. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/overview/ +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/overview/ type MiddlewareTCP struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. @@ -28,11 +28,11 @@ type MiddlewareTCPSpec struct { // IPWhiteList defines the IPWhiteList middleware configuration. // This middleware accepts/refuses connections based on the client IP. // Deprecated: please use IPAllowList instead. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/ipwhitelist/ + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/ipwhitelist/ IPWhiteList *dynamic.TCPIPWhiteList `json:"ipWhiteList,omitempty"` // IPAllowList defines the IPAllowList middleware configuration. // This middleware accepts/refuses connections based on the client IP. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/middlewares/ipallowlist/ + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/middlewares/ipallowlist/ IPAllowList *dynamic.TCPIPAllowList `json:"ipAllowList,omitempty"` } diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransport.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransport.go index f797671fc..8cfbb92be 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransport.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransport.go @@ -13,7 +13,7 @@ import ( // ServersTransport is the CRD implementation of a ServersTransport. // If no serversTransport is specified, the default@internal will be used. // The default@internal serversTransport is created from the static configuration. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/serverstransport/ +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/serverstransport/ type ServersTransport struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransporttcp.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransporttcp.go index dfb2299be..374108d10 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransporttcp.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/serverstransporttcp.go @@ -13,7 +13,7 @@ import ( // ServersTransportTCP is the CRD implementation of a TCPServersTransport. // If no tcpServersTransport is specified, a default one named default@internal will be used. // The default@internal tcpServersTransport can be configured in the static configuration. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/tcp/serverstransport/ +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/tcp/serverstransport/ type ServersTransportTCP struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/service.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/service.go index 8adce08ef..35d45bcfc 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/service.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/service.go @@ -13,7 +13,7 @@ import ( // TraefikService object allows to: // - Apply weight to Services on load-balancing // - Mirror traffic on services -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/traefikservice/ +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/traefikservice/ type TraefikService struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. @@ -44,12 +44,14 @@ type TraefikServiceSpec struct { Weighted *WeightedRoundRobin `json:"weighted,omitempty"` // Mirroring defines the Mirroring service configuration. Mirroring *Mirroring `json:"mirroring,omitempty"` + // HighestRandomWeight defines the highest random weight service configuration. + HighestRandomWeight *HighestRandomWeight `json:"highestRandomWeight,omitempty"` } // +k8s:deepcopy-gen=true // Mirroring holds the mirroring service configuration. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#mirroring +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#mirroring type Mirroring struct { LoadBalancerSpec `json:",inline"` @@ -78,11 +80,20 @@ type MirrorService struct { // +k8s:deepcopy-gen=true // WeightedRoundRobin holds the weighted round-robin configuration. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/load-balancing/service/#weighted-round-robin-wrr +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/load-balancing/service/#weighted-round-robin-wrr type WeightedRoundRobin struct { // Services defines the list of Kubernetes Service and/or TraefikService to load-balance, with weight. Services []Service `json:"services,omitempty"` // Sticky defines whether sticky sessions are enabled. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/kubernetes/crd/http/traefikservice/#stickiness-and-load-balancing + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/kubernetes/crd/http/traefikservice/#stickiness-and-load-balancing Sticky *dynamic.Sticky `json:"sticky,omitempty"` } + +// +k8s:deepcopy-gen=true + +// HighestRandomWeight holds the highest random weight configuration. +// More info: https://doc.traefik.io/traefik/v3.6/routing/services/#highest-random-configuration +type HighestRandomWeight struct { + // Services defines the list of Kubernetes Service and/or TraefikService to load-balance, with weight. + Services []Service `json:"services,omitempty"` +} diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/tlsoption.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/tlsoption.go index 07ab9954d..b4eebf6b7 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/tlsoption.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/tlsoption.go @@ -9,7 +9,7 @@ import ( // +kubebuilder:storageversion // TLSOption is the CRD implementation of a Traefik TLS Option, allowing to configure some parameters of the TLS connection. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#tls-options +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#tls-options type TLSOption struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. @@ -32,17 +32,17 @@ type TLSOptionSpec struct { // Default: None. MaxVersion string `json:"maxVersion,omitempty"` // CipherSuites defines the list of supported cipher suites for TLS versions up to TLS 1.2. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#cipher-suites + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#cipher-suites CipherSuites []string `json:"cipherSuites,omitempty"` // CurvePreferences defines the preferred elliptic curves. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#curve-preferences + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#curve-preferences CurvePreferences []string `json:"curvePreferences,omitempty"` // ClientAuth defines the server's policy for TLS Client Authentication. ClientAuth ClientAuth `json:"clientAuth,omitempty"` // SniStrict defines whether Traefik allows connections from clients connections that do not specify a server_name extension. SniStrict bool `json:"sniStrict,omitempty"` // ALPNProtocols defines the list of supported application level protocols for the TLS handshake, in order of preference. - // More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#alpn-protocols + // More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#alpn-protocols ALPNProtocols []string `json:"alpnProtocols,omitempty"` // DisableSessionTickets disables TLS session resumption via session tickets. DisableSessionTickets bool `json:"disableSessionTickets,omitempty"` diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/tlsstore.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/tlsstore.go index 23a5d1a9a..36a31ca65 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/tlsstore.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/tlsstore.go @@ -12,7 +12,7 @@ import ( // TLSStore is the CRD implementation of a Traefik TLS Store. // For the time being, only the TLSStore named default is supported. // This means that you cannot have two stores that are named default in different Kubernetes namespaces. -// More info: https://doc.traefik.io/traefik/v3.5/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#certificates-stores +// More info: https://doc.traefik.io/traefik/v3.6/reference/routing-configuration/http/tls/tls-certificates/#certificates-stores#certificates-stores type TLSStore struct { metav1.TypeMeta `json:",inline"` // Standard object's metadata. diff --git a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go index c4f898635..3137d46ec 100644 --- a/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/provider/kubernetes/crd/traefikio/v1alpha1/zz_generated.deepcopy.go @@ -4,7 +4,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -349,6 +349,29 @@ func (in *ForwardingTimeouts) DeepCopy() *ForwardingTimeouts { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HighestRandomWeight) DeepCopyInto(out *HighestRandomWeight) { + *out = *in + if in.Services != nil { + in, out := &in.Services, &out.Services + *out = make([]Service, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HighestRandomWeight. +func (in *HighestRandomWeight) DeepCopy() *HighestRandomWeight { + if in == nil { + return nil + } + out := new(HighestRandomWeight) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IngressRoute) DeepCopyInto(out *IngressRoute) { *out = *in @@ -409,6 +432,22 @@ func (in *IngressRouteList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IngressRouteRef) DeepCopyInto(out *IngressRouteRef) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressRouteRef. +func (in *IngressRouteRef) DeepCopy() *IngressRouteRef { + if in == nil { + return nil + } + out := new(IngressRouteRef) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *IngressRouteSpec) DeepCopyInto(out *IngressRouteSpec) { *out = *in @@ -429,6 +468,11 @@ func (in *IngressRouteSpec) DeepCopyInto(out *IngressRouteSpec) { *out = new(TLS) (*in).DeepCopyInto(*out) } + if in.ParentRefs != nil { + in, out := &in.ParentRefs, &out.ParentRefs + *out = make([]IngressRouteRef, len(*in)) + copy(*out, *in) + } return } @@ -657,6 +701,11 @@ func (in *LoadBalancerSpec) DeepCopyInto(out *LoadBalancerSpec) { *out = new(ServerHealthCheck) (*in).DeepCopyInto(*out) } + if in.PassiveHealthCheck != nil { + in, out := &in.PassiveHealthCheck, &out.PassiveHealthCheck + *out = new(PassiveServerHealthCheck) + (*in).DeepCopyInto(*out) + } return } @@ -1047,6 +1096,32 @@ func (in *ObjectReference) DeepCopy() *ObjectReference { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PassiveServerHealthCheck) DeepCopyInto(out *PassiveServerHealthCheck) { + *out = *in + if in.FailureWindow != nil { + in, out := &in.FailureWindow, &out.FailureWindow + *out = new(intstr.IntOrString) + **out = **in + } + if in.MaxFailedAttempts != nil { + in, out := &in.MaxFailedAttempts, &out.MaxFailedAttempts + *out = new(int) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PassiveServerHealthCheck. +func (in *PassiveServerHealthCheck) DeepCopy() *PassiveServerHealthCheck { + if in == nil { + return nil + } + out := new(PassiveServerHealthCheck) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RateLimit) DeepCopyInto(out *RateLimit) { *out = *in @@ -1997,6 +2072,11 @@ func (in *TraefikServiceSpec) DeepCopyInto(out *TraefikServiceSpec) { *out = new(Mirroring) (*in).DeepCopyInto(*out) } + if in.HighestRandomWeight != nil { + in, out := &in.HighestRandomWeight, &out.HighestRandomWeight + *out = new(HighestRandomWeight) + (*in).DeepCopyInto(*out) + } return } diff --git a/pkg/provider/kubernetes/gateway/client.go b/pkg/provider/kubernetes/gateway/client.go index 109a0f9d7..50cf789d3 100644 --- a/pkg/provider/kubernetes/gateway/client.go +++ b/pkg/provider/kubernetes/gateway/client.go @@ -14,7 +14,6 @@ import ( "github.com/traefik/traefik/v3/pkg/types" corev1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" - kerror "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/selection" @@ -26,7 +25,6 @@ import ( "k8s.io/client-go/util/retry" gatev1 "sigs.k8s.io/gateway-api/apis/v1" gatev1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gatev1alpha3 "sigs.k8s.io/gateway-api/apis/v1alpha3" gatev1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1" gateclientset "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned" gateinformers "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions" @@ -187,6 +185,14 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< if err != nil { return nil, err } + _, err = factoryGateway.Gateway().V1().BackendTLSPolicies().Informer().AddEventHandler(eventHandler) + if err != nil { + return nil, err + } + _, err = factoryKube.Core().V1().ConfigMaps().Informer().AddEventHandler(eventHandler) + if err != nil { + return nil, err + } if c.experimentalChannel { _, err = factoryGateway.Gateway().V1alpha2().TCPRoutes().Informer().AddEventHandler(eventHandler) @@ -197,14 +203,6 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< if err != nil { return nil, err } - _, err = factoryGateway.Gateway().V1alpha3().BackendTLSPolicies().Informer().AddEventHandler(eventHandler) - if err != nil { - return nil, err - } - _, err = factoryKube.Core().V1().ConfigMaps().Informer().AddEventHandler(eventHandler) - if err != nil { - return nil, err - } } factorySecret := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm)) @@ -367,6 +365,72 @@ func (c *clientWrapper) ListGatewayClasses() ([]*gatev1.GatewayClass, error) { return c.factoryGatewayClass.Gateway().V1().GatewayClasses().Lister().List(labels.Everything()) } +// ListEndpointSlicesForService returns the EndpointSlices for the given service name in the given namespace. +func (c *clientWrapper) ListEndpointSlicesForService(namespace, serviceName string) ([]*discoveryv1.EndpointSlice, error) { + if !c.isWatchedNamespace(namespace) { + return nil, fmt.Errorf("failed to get endpointslices for service %s/%s: namespace is not within watched namespaces", namespace, serviceName) + } + + serviceLabelRequirement, err := labels.NewRequirement(discoveryv1.LabelServiceName, selection.Equals, []string{serviceName}) + if err != nil { + return nil, fmt.Errorf("failed to create service label selector requirement: %w", err) + } + serviceSelector := labels.NewSelector() + serviceSelector = serviceSelector.Add(*serviceLabelRequirement) + + return c.factoriesKube[c.lookupNamespace(namespace)].Discovery().V1().EndpointSlices().Lister().EndpointSlices(namespace).List(serviceSelector) +} + +// ListBackendTLSPoliciesForService returns the BackendTLSPolicy for the given service name in the given namespace. +func (c *clientWrapper) ListBackendTLSPoliciesForService(namespace, serviceName string) ([]*gatev1.BackendTLSPolicy, error) { + if !c.isWatchedNamespace(namespace) { + return nil, fmt.Errorf("failed to get BackendTLSPolicies for service %s/%s: namespace is not within watched namespaces", namespace, serviceName) + } + + policies, err := c.factoriesGateway[c.lookupNamespace(namespace)].Gateway().V1().BackendTLSPolicies().Lister().BackendTLSPolicies(namespace).List(labels.Everything()) + if err != nil { + return nil, fmt.Errorf("failed to list BackendTLSPolicies in namespace %s", namespace) + } + + var servicePolicies []*gatev1.BackendTLSPolicy + for _, policy := range policies { + for _, ref := range policy.Spec.TargetRefs { + // The policy does not target the service. + if (ref.Group != "" && ref.Group != groupCore) || ref.Kind != kindService || string(ref.Name) != serviceName { + continue + } + + servicePolicies = append(servicePolicies, policy) + } + } + + return servicePolicies, nil +} + +// GetService returns the named service from the given namespace. +func (c *clientWrapper) GetService(namespace, name string) (*corev1.Service, error) { + if !c.isWatchedNamespace(namespace) { + return nil, fmt.Errorf("failed to get service %s/%s: namespace is not within watched namespaces", namespace, name) + } + return c.factoriesKube[c.lookupNamespace(namespace)].Core().V1().Services().Lister().Services(namespace).Get(name) +} + +// GetSecret returns the named secret from the given namespace. +func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, error) { + if !c.isWatchedNamespace(namespace) { + return nil, fmt.Errorf("failed to get secret %s/%s: namespace is not within watched namespaces", namespace, name) + } + return c.factoriesSecret[c.lookupNamespace(namespace)].Core().V1().Secrets().Lister().Secrets(namespace).Get(name) +} + +// GetConfigMap returns the named configMap from the given namespace. +func (c *clientWrapper) GetConfigMap(namespace, name string) (*corev1.ConfigMap, error) { + if !c.isWatchedNamespace(namespace) { + return nil, fmt.Errorf("failed to get configMap %s/%s: namespace is not within watched namespaces", namespace, name) + } + return c.factoriesKube[c.lookupNamespace(namespace)].Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).Get(name) +} + func (c *clientWrapper) UpdateGatewayClassStatus(ctx context.Context, name string, status gatev1.GatewayClassStatus) error { err := retry.RetryOnConflict(retry.DefaultRetry, func() error { currentGatewayClass, err := c.factoryGatewayClass.Gateway().V1().GatewayClasses().Lister().Get(name) @@ -637,20 +701,20 @@ func (c *clientWrapper) UpdateTLSRouteStatus(ctx context.Context, route ktypes.N return nil } -func (c *clientWrapper) UpdateBackendTLSPolicyStatus(ctx context.Context, policy ktypes.NamespacedName, status gatev1alpha2.PolicyStatus) error { +func (c *clientWrapper) UpdateBackendTLSPolicyStatus(ctx context.Context, policy ktypes.NamespacedName, status gatev1.PolicyStatus) error { if !c.isWatchedNamespace(policy.Namespace) { return fmt.Errorf("updating BackendTLSPolicy status %s/%s: namespace is not within watched namespaces", policy.Namespace, policy.Name) } err := retry.RetryOnConflict(retry.DefaultRetry, func() error { - currentPolicy, err := c.factoriesGateway[c.lookupNamespace(policy.Namespace)].Gateway().V1alpha3().BackendTLSPolicies().Lister().BackendTLSPolicies(policy.Namespace).Get(policy.Name) + currentPolicy, err := c.factoriesGateway[c.lookupNamespace(policy.Namespace)].Gateway().V1().BackendTLSPolicies().Lister().BackendTLSPolicies(policy.Namespace).Get(policy.Name) if err != nil { // We have to return err itself here (not wrapped inside another error) // so that RetryOnConflict can identify it correctly. return err } - ancestorStatuses := make([]gatev1alpha2.PolicyAncestorStatus, len(status.Ancestors)) + ancestorStatuses := make([]gatev1.PolicyAncestorStatus, len(status.Ancestors)) copy(ancestorStatuses, status.Ancestors) // keep statuses added by other gateway controllers, @@ -660,14 +724,6 @@ func (c *clientWrapper) UpdateBackendTLSPolicyStatus(ctx context.Context, policy ancestorStatuses = append(ancestorStatuses, ancestorStatus) continue } - - if slices.ContainsFunc(status.Ancestors, func(status gatev1alpha2.PolicyAncestorStatus) bool { - return reflect.DeepEqual(ancestorStatus.AncestorRef, status.AncestorRef) - }) { - continue - } - - ancestorStatuses = append(ancestorStatuses, ancestorStatus) } if len(ancestorStatuses) > 16 { @@ -680,11 +736,11 @@ func (c *clientWrapper) UpdateBackendTLSPolicyStatus(ctx context.Context, policy } currentPolicy = currentPolicy.DeepCopy() - currentPolicy.Status = gatev1alpha2.PolicyStatus{ + currentPolicy.Status = gatev1.PolicyStatus{ Ancestors: ancestorStatuses, } - if _, err = c.csGateway.GatewayV1alpha3().BackendTLSPolicies(policy.Namespace).UpdateStatus(ctx, currentPolicy, metav1.UpdateOptions{}); err != nil { + if _, err = c.csGateway.GatewayV1().BackendTLSPolicies(policy.Namespace).UpdateStatus(ctx, currentPolicy, metav1.UpdateOptions{}); err != nil { // We have to return err itself here (not wrapped inside another error) // so that RetryOnConflict can identify it correctly. return err @@ -699,85 +755,7 @@ func (c *clientWrapper) UpdateBackendTLSPolicyStatus(ctx context.Context, policy return nil } -// GetService returns the named service from the given namespace. -func (c *clientWrapper) GetService(namespace, name string) (*corev1.Service, bool, error) { - if !c.isWatchedNamespace(namespace) { - return nil, false, fmt.Errorf("failed to get service %s/%s: namespace is not within watched namespaces", namespace, name) - } - - service, err := c.factoriesKube[c.lookupNamespace(namespace)].Core().V1().Services().Lister().Services(namespace).Get(name) - exist, err := translateNotFoundError(err) - - return service, exist, err -} - -// ListEndpointSlicesForService returns the EndpointSlices for the given service name in the given namespace. -func (c *clientWrapper) ListEndpointSlicesForService(namespace, serviceName string) ([]*discoveryv1.EndpointSlice, error) { - if !c.isWatchedNamespace(namespace) { - return nil, fmt.Errorf("failed to get endpointslices for service %s/%s: namespace is not within watched namespaces", namespace, serviceName) - } - - serviceLabelRequirement, err := labels.NewRequirement(discoveryv1.LabelServiceName, selection.Equals, []string{serviceName}) - if err != nil { - return nil, fmt.Errorf("failed to create service label selector requirement: %w", err) - } - serviceSelector := labels.NewSelector() - serviceSelector = serviceSelector.Add(*serviceLabelRequirement) - - return c.factoriesKube[c.lookupNamespace(namespace)].Discovery().V1().EndpointSlices().Lister().EndpointSlices(namespace).List(serviceSelector) -} - -// ListBackendTLSPoliciesForService returns the BackendTLSPolicy for the given service name in the given namespace. -func (c *clientWrapper) ListBackendTLSPoliciesForService(namespace, serviceName string) ([]*gatev1alpha3.BackendTLSPolicy, error) { - if !c.isWatchedNamespace(namespace) { - return nil, fmt.Errorf("failed to get BackendTLSPolicies for service %s/%s: namespace is not within watched namespaces", namespace, serviceName) - } - - policies, err := c.factoriesGateway[c.lookupNamespace(namespace)].Gateway().V1alpha3().BackendTLSPolicies().Lister().BackendTLSPolicies(namespace).List(labels.Everything()) - if err != nil { - return nil, fmt.Errorf("failed to list BackendTLSPolicies in namespace %s", namespace) - } - - var servicePolicies []*gatev1alpha3.BackendTLSPolicy - for _, policy := range policies { - for _, ref := range policy.Spec.TargetRefs { - // The policy does not target the service. - if (ref.Group != "" && ref.Group != groupCore) || ref.Kind != kindService || string(ref.Name) != serviceName { - continue - } - - servicePolicies = append(servicePolicies, policy) - } - } - - return servicePolicies, nil -} - -// GetSecret returns the named secret from the given namespace. -func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, bool, error) { - if !c.isWatchedNamespace(namespace) { - return nil, false, fmt.Errorf("failed to get secret %s/%s: namespace is not within watched namespaces", namespace, name) - } - - secret, err := c.factoriesSecret[c.lookupNamespace(namespace)].Core().V1().Secrets().Lister().Secrets(namespace).Get(name) - exist, err := translateNotFoundError(err) - - return secret, exist, err -} - -// GetConfigMap returns the named configMap from the given namespace. -func (c *clientWrapper) GetConfigMap(namespace, name string) (*corev1.ConfigMap, bool, error) { - if !c.isWatchedNamespace(namespace) { - return nil, false, fmt.Errorf("failed to get configMap %s/%s: namespace is not within watched namespaces", namespace, name) - } - - configMap, err := c.factoriesKube[c.lookupNamespace(namespace)].Core().V1().ConfigMaps().Lister().ConfigMaps(namespace).Get(name) - exist, err := translateNotFoundError(err) - - return configMap, exist, err -} - -// lookupNamespace returns the lookup namespace key for the given namespace. +// lookupNamespace returns the lookup namespace listenerKey for the given namespace. // When listening on all namespaces, it returns the client-go identifier ("") // for all-namespaces. Otherwise, it returns the given namespace. // The distinction is necessary because we index all informers on the special @@ -800,28 +778,19 @@ func (c *clientWrapper) isWatchedNamespace(namespace string) bool { return slices.Contains(c.watchedNamespaces, namespace) } -// translateNotFoundError will translate a "not found" error to a boolean return -// value which indicates if the resource exists and a nil error. -func translateNotFoundError(err error) (bool, error) { - if kerror.IsNotFound(err) { - return false, nil - } - return err == nil, err -} - func gatewayStatusEqual(statusA, statusB gatev1.GatewayStatus) bool { return reflect.DeepEqual(statusA.Addresses, statusB.Addresses) && listenersStatusEqual(statusA.Listeners, statusB.Listeners) && conditionsEqual(statusA.Conditions, statusB.Conditions) } -func policyAncestorStatusesEqual(policyAncestorStatusesA, policyAncestorStatusesB []gatev1alpha2.PolicyAncestorStatus) bool { +func policyAncestorStatusesEqual(policyAncestorStatusesA, policyAncestorStatusesB []gatev1.PolicyAncestorStatus) bool { if len(policyAncestorStatusesA) != len(policyAncestorStatusesB) { return false } for _, sA := range policyAncestorStatusesA { - if !slices.ContainsFunc(policyAncestorStatusesB, func(sB gatev1alpha2.PolicyAncestorStatus) bool { + if !slices.ContainsFunc(policyAncestorStatusesB, func(sB gatev1.PolicyAncestorStatus) bool { return policyAncestorStatusEqual(sB, sA) }) { return false @@ -829,7 +798,7 @@ func policyAncestorStatusesEqual(policyAncestorStatusesA, policyAncestorStatuses } for _, sB := range policyAncestorStatusesB { - if !slices.ContainsFunc(policyAncestorStatusesA, func(sA gatev1alpha2.PolicyAncestorStatus) bool { + if !slices.ContainsFunc(policyAncestorStatusesA, func(sA gatev1.PolicyAncestorStatus) bool { return policyAncestorStatusEqual(sA, sB) }) { return false @@ -839,7 +808,7 @@ func policyAncestorStatusesEqual(policyAncestorStatusesA, policyAncestorStatuses return true } -func policyAncestorStatusEqual(sA, sB gatev1alpha2.PolicyAncestorStatus) bool { +func policyAncestorStatusEqual(sA, sB gatev1.PolicyAncestorStatus) bool { return sA.ControllerName == sB.ControllerName && reflect.DeepEqual(sA.AncestorRef, sB.AncestorRef) && conditionsEqual(sA.Conditions, sB.Conditions) diff --git a/pkg/provider/kubernetes/gateway/features.go b/pkg/provider/kubernetes/gateway/features.go index eb3a433d8..88dedd573 100644 --- a/pkg/provider/kubernetes/gateway/features.go +++ b/pkg/provider/kubernetes/gateway/features.go @@ -1,24 +1,48 @@ package gateway -import "sigs.k8s.io/gateway-api/pkg/features" +import ( + "sync" -func SupportedFeatures() []features.FeatureName { - return []features.FeatureName{ - features.GatewayFeature.Name, - features.GatewayPort8080Feature.Name, - features.GRPCRouteFeature.Name, - features.HTTPRouteFeature.Name, - features.HTTPRouteQueryParamMatchingFeature.Name, - features.HTTPRouteMethodMatchingFeature.Name, - features.HTTPRoutePortRedirectFeature.Name, - features.HTTPRouteSchemeRedirectFeature.Name, - features.HTTPRouteHostRewriteFeature.Name, - features.HTTPRoutePathRewriteFeature.Name, - features.HTTPRoutePathRedirectFeature.Name, - features.HTTPRouteResponseHeaderModificationFeature.Name, - features.HTTPRouteBackendProtocolH2CFeature.Name, - features.HTTPRouteBackendProtocolWebSocketFeature.Name, - features.HTTPRouteDestinationPortMatchingFeature.Name, - features.TLSRouteFeature.Name, + "k8s.io/apimachinery/pkg/util/sets" + "sigs.k8s.io/gateway-api/pkg/features" +) + +var SupportedFeatures = sync.OnceValue(func() []features.FeatureName { + featureSet := sets.New[features.Feature](). + Insert(features.GatewayCoreFeatures.UnsortedList()...). + Insert(features.GatewayExtendedFeatures.Intersection(extendedGatewayFeatures()).UnsortedList()...). + Insert(features.HTTPRouteCoreFeatures.UnsortedList()...). + Insert(features.HTTPRouteExtendedFeatures.Intersection(extendedHTTPRouteFeatures()).UnsortedList()...). + Insert(features.ReferenceGrantCoreFeatures.UnsortedList()...). + Insert(features.BackendTLSPolicyCoreFeatures.UnsortedList()...). + Insert(features.GRPCRouteCoreFeatures.UnsortedList()...). + Insert(features.TLSRouteCoreFeatures.UnsortedList()...) + + featureNames := make([]features.FeatureName, 0, featureSet.Len()) + for f := range featureSet { + featureNames = append(featureNames, f.Name) } + return featureNames +}) + +// extendedGatewayFeatures returns the supported extended Gateway features. +func extendedGatewayFeatures() sets.Set[features.Feature] { + return sets.New(features.GatewayPort8080Feature) +} + +// extendedHTTPRouteFeatures returns the supported extended HTTP Route features. +func extendedHTTPRouteFeatures() sets.Set[features.Feature] { + return sets.New( + features.HTTPRouteQueryParamMatchingFeature, + features.HTTPRouteMethodMatchingFeature, + features.HTTPRoutePortRedirectFeature, + features.HTTPRouteSchemeRedirectFeature, + features.HTTPRouteHostRewriteFeature, + features.HTTPRoutePathRewriteFeature, + features.HTTPRoutePathRedirectFeature, + features.HTTPRouteResponseHeaderModificationFeature, + features.HTTPRouteBackendProtocolH2CFeature, + features.HTTPRouteBackendProtocolWebSocketFeature, + features.HTTPRouteDestinationPortMatchingFeature, + ) } diff --git a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_backend_tls_policy.yml b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_backend_tls_policy.yml index 7748aee47..f183c92d8 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_backend_tls_policy.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_backend_tls_policy.yml @@ -52,7 +52,7 @@ spec: --- kind: BackendTLSPolicy -apiVersion: gateway.networking.k8s.io/v1alpha3 +apiVersion: gateway.networking.k8s.io/v1 metadata: name: policy-1 namespace: default @@ -78,7 +78,7 @@ metadata: name: ca-file namespace: default data: - ca.crt: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=" + ca.crt: "CA1" --- apiVersion: v1 @@ -87,4 +87,4 @@ metadata: name: ca-file-2 namespace: default data: - ca.crt: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=" + ca.crt: "CA2" diff --git a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_backend_tls_policy_system.yml b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_backend_tls_policy_system.yml index cc36c0468..05b28bd9c 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_backend_tls_policy_system.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_backend_tls_policy_system.yml @@ -52,7 +52,7 @@ spec: --- kind: BackendTLSPolicy -apiVersion: gateway.networking.k8s.io/v1alpha3 +apiVersion: gateway.networking.k8s.io/v1 metadata: name: policy-1 namespace: default diff --git a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_protocol_https.yml b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_protocol_https.yml index cd4abdec1..bb160e469 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_protocol_https.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_protocol_https.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_protocol_https_with_tls_passthrough.yml b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_protocol_https_with_tls_passthrough.yml index 9804bc80d..73cbc8e82 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_protocol_https_with_tls_passthrough.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_protocol_https_with_tls_passthrough.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_protocol_tls.yml b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_protocol_tls.yml index bace669a0..552e32c95 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_protocol_tls.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_protocol_tls.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_two_gateways_one_httproute.yml b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_two_gateways_one_httproute.yml index 934b67f82..b5c5c832f 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_two_gateways_one_httproute.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_two_gateways_one_httproute.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_two_listeners_one_httproute.yml b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_two_listeners_one_httproute.yml index ca1c1a90e..f86f94346 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/httproute/with_two_listeners_one_httproute.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/httproute/with_two_listeners_one_httproute.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/mixed/simple.yml b/pkg/provider/kubernetes/gateway/fixtures/mixed/simple.yml index 0fd6ee36e..35bd833e4 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/mixed/simple.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/mixed/simple.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/mixed/with_core_group.yml b/pkg/provider/kubernetes/gateway/fixtures/mixed/with_core_group.yml index dcef7966d..3294e3a73 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/mixed/with_core_group.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/mixed/with_core_group.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/mixed/with_multiple_listeners_using_same_hostname_port_protocol.yml b/pkg/provider/kubernetes/gateway/fixtures/mixed/with_multiple_listeners_using_same_hostname_port_protocol.yml index 272e363d7..cb0e19a60 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/mixed/with_multiple_listeners_using_same_hostname_port_protocol.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/mixed/with_multiple_listeners_using_same_hostname_port_protocol.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/mixed/with_namespace_all.yml b/pkg/provider/kubernetes/gateway/fixtures/mixed/with_namespace_all.yml index c2b525044..263302204 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/mixed/with_namespace_all.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/mixed/with_namespace_all.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/mixed/with_namespace_same.yml b/pkg/provider/kubernetes/gateway/fixtures/mixed/with_namespace_same.yml index 8d470e749..6bf1df73b 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/mixed/with_namespace_same.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/mixed/with_namespace_same.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/mixed/with_namespace_selector.yml b/pkg/provider/kubernetes/gateway/fixtures/mixed/with_namespace_selector.yml index a46e3dab6..7fa26a504 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/mixed/with_namespace_selector.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/mixed/with_namespace_selector.yml @@ -14,8 +14,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret.yml b/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret.yml index ba5f1a9fb..d76fb2065 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret.yml @@ -20,8 +20,8 @@ metadata: namespace: secret-namespace data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_missing.yml b/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_missing.yml index 660ff18b6..e73e55a24 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_missing.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_missing.yml @@ -6,8 +6,8 @@ metadata: namespace: secret-namespace data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_not_matching_from.yml b/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_not_matching_from.yml index 47aba19d8..e3662e9f2 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_not_matching_from.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_not_matching_from.yml @@ -20,8 +20,8 @@ metadata: namespace: secret-namespace data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_not_matching_to.yml b/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_not_matching_to.yml index e08fa7a42..2b336f6ea 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_not_matching_to.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/referencegrant/for_secret_not_matching_to.yml @@ -21,8 +21,8 @@ metadata: namespace: secret-namespace data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/tcproute/with_protocol_https.yml b/pkg/provider/kubernetes/gateway/fixtures/tcproute/with_protocol_https.yml index ceb2f0bb0..5bbebbfca 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/tcproute/with_protocol_https.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/tcproute/with_protocol_https.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/tcproute/with_protocol_tls.yml b/pkg/provider/kubernetes/gateway/fixtures/tcproute/with_protocol_tls.yml index 05e3fc5ae..ac6b60bee 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/tcproute/with_protocol_tls.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/tcproute/with_protocol_tls.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/tcproute/without_tcproute_tls_protocol.yml b/pkg/provider/kubernetes/gateway/fixtures/tcproute/without_tcproute_tls_protocol.yml index 67e8f584d..23a1860b4 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/tcproute/without_tcproute_tls_protocol.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/tcproute/without_tcproute_tls_protocol.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/gatewayclass_with_unknown_controller.yml b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/gatewayclass_with_unknown_controller.yml index 0e9c8f448..b5ce33ede 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/gatewayclass_with_unknown_controller.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/gatewayclass_with_unknown_controller.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/simple_TLS_to_TCPRoute.yml b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/simple_TLS_to_TCPRoute.yml index 4a2dc107f..7f30bf92d 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/simple_TLS_to_TCPRoute.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/simple_TLS_to_TCPRoute.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/simple_TLS_to_TLSRoute.yml b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/simple_TLS_to_TLSRoute.yml index e41a60aa7..b908be3f0 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/simple_TLS_to_TLSRoute.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/simple_TLS_to_TLSRoute.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/simple_cross_provider.yml b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/simple_cross_provider.yml index 46c588590..636673c68 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/simple_cross_provider.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/simple_cross_provider.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/simple_nativelb.yml b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/simple_nativelb.yml index 253e59b9b..fde70d51e 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/simple_nativelb.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/simple_nativelb.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_multiple_routes_kind.yml b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_multiple_routes_kind.yml index c88672329..3c6afe86e 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_multiple_routes_kind.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_multiple_routes_kind.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_passthrough_tls.yml b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_passthrough_tls.yml index e04d4e362..9ffc3a200 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_passthrough_tls.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_passthrough_tls.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_protocol_https.yml b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_protocol_https.yml index c5d904baa..2b424d065 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_protocol_https.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_protocol_https.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_wrong_service_port.yml b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_wrong_service_port.yml index 5d296c248..37fafa2ad 100644 --- a/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_wrong_service_port.yml +++ b/pkg/provider/kubernetes/gateway/fixtures/tlsroute/with_wrong_service_port.yml @@ -6,8 +6,8 @@ metadata: namespace: default data: - tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0= - tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCi0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0= + tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJxRENDQVU2Z0F3SUJBZ0lVWU9zcjBRZ0hPQnE0a1lSQ0w1K1REZFZ0NmJRd0NnWUlLb1pJemowRUF3SXcKRmpFVU1CSUdBMVVFQXd3TFpYaGhiWEJzWlM1amIyMHdIaGNOTWpVeE1ERXdNRGN4TnpNd1doY05NelV4TURBNApNRGN4TnpNd1dqQVdNUlF3RWdZRFZRUUREQXRsZUdGdGNHeGxMbU52YlRCWk1CTUdCeXFHU000OUFnRUdDQ3FHClNNNDlBd0VIQTBJQUJET3JpdzNaUTd3SWhXcmJQUzZKRlFUM2JUb05DRjAwdlNWNWZhYjZUYlh5TDh0bHNHcmUKVFJJRjJFd2dzdGVNT2t4R0tLU2xEdnVhRHdxOHAvcVYrMHVqZWpCNE1CMEdBMVVkRGdRV0JCUk1Fa3VleFhRaApVdERnUmcxS0J2NzJDRHErRXpBZkJnTlZIU01FR0RBV2dCUk1Fa3VleFhRaFV0RGdSZzFLQnY3MkNEcStFekFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUNVR0ExVWRFUVFlTUJ5Q0MyVjRZVzF3YkdVdVkyOXRnZzBxTG1WNFlXMXcKYkdVdVkyOXRNQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURzODdWazBzd0E2SGdPSmpST3llMW14RDgzcWNHeQpwZUZnb3hWOTNEeStjd0lnVjBNTUVKSmJWc1R5WkszRVErK1hjNXJFTDc4bnJKK1lJRVYrckNVV2o1VT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ== + tls.key: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JR0hBZ0VBTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEJHMHdhd0lCQVFRZ253Z0w1RFk0VUIxNHNNNmYKRGlrUWR0cWgyUVcxQXJmRjRmYzFVRnppZmRHaFJBTkNBQVF6cTRzTjJVTzhDSVZxMnowdWlSVUU5MjA2RFFoZApOTDBsZVgybStrMjE4aS9MWmJCcTNrMFNCZGhNSUxMWGpEcE1SaWlrcFE3N21nOEt2S2Y2bGZ0TAotLS0tLUVORCBQUklWQVRFIEtFWS0tLS0t --- kind: GatewayClass diff --git a/pkg/provider/kubernetes/gateway/httproute.go b/pkg/provider/kubernetes/gateway/httproute.go index ebee5fd1f..863c2f093 100644 --- a/pkg/provider/kubernetes/gateway/httproute.go +++ b/pkg/provider/kubernetes/gateway/httproute.go @@ -7,6 +7,7 @@ import ( "net" "net/http" "regexp" + "slices" "strconv" "strings" @@ -19,8 +20,6 @@ import ( ktypes "k8s.io/apimachinery/pkg/types" "k8s.io/utils/ptr" gatev1 "sigs.k8s.io/gateway-api/apis/v1" - gatev1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" - gatev1alpha3 "sigs.k8s.io/gateway-api/apis/v1alpha3" ) func (p *Provider) loadHTTPRoutes(ctx context.Context, gatewayListeners []gatewayListener, conf *dynamic.Configuration) { @@ -412,85 +411,125 @@ func (p *Provider) loadHTTPServers(ctx context.Context, namespace string, route } } - var st *dynamic.ServersTransport - var protocol string - if p.ExperimentalChannel { - servicePolicies, err := p.client.ListBackendTLSPoliciesForService(namespace, string(backendRef.Name)) - if err != nil { - return nil, nil, &metav1.Condition{ - Type: string(gatev1.RouteConditionResolvedRefs), - Status: metav1.ConditionFalse, - ObservedGeneration: route.Generation, - LastTransitionTime: metav1.Now(), - Reason: string(gatev1.RouteReasonRefNotPermitted), - Message: fmt.Sprintf("Cannot list BackendTLSPolicies for Service %s/%s: %s", namespace, string(backendRef.Name), err), - } + backendTLSPolicies, err := p.client.ListBackendTLSPoliciesForService(namespace, string(backendRef.Name)) + if err != nil { + return nil, nil, &metav1.Condition{ + Type: string(gatev1.RouteConditionResolvedRefs), + Status: metav1.ConditionFalse, + ObservedGeneration: route.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.RouteReasonRefNotPermitted), + Message: fmt.Sprintf("Cannot list BackendTLSPolicies for Service %s/%s: %s", namespace, string(backendRef.Name), err), } + } - var matchedPolicy *gatev1alpha3.BackendTLSPolicy - for _, policy := range servicePolicies { - matched := false - for _, targetRef := range policy.Spec.TargetRefs { - if targetRef.SectionName == nil || svcPort.Name == string(*targetRef.SectionName) { - matchedPolicy = policy - matched = true - break - } + // Sort BackendTLSPolicies by creation timestamp, then by name to match the BackendTLSPolicy requirements. + slices.SortStableFunc(backendTLSPolicies, func(a, b *gatev1.BackendTLSPolicy) int { + cmpTime := a.CreationTimestamp.Time.Compare(b.CreationTimestamp.Time) + if cmpTime == 0 { + return strings.Compare(a.Name, b.Name) + } + return cmpTime + }) + + var serversTransport *dynamic.ServersTransport + for _, policy := range backendTLSPolicies { + for _, targetRef := range policy.Spec.TargetRefs { + if targetRef.SectionName != nil && svcPort.Name != string(*targetRef.SectionName) { + continue } - // If the policy targets the service, but doesn't match any port. - if !matched { - // update policy status - status := gatev1alpha2.PolicyStatus{ - Ancestors: []gatev1alpha2.PolicyAncestorStatus{{ - AncestorRef: gatev1alpha2.ParentReference{ - Group: ptr.To(gatev1.Group(groupGateway)), - Kind: ptr.To(gatev1.Kind(kindGateway)), - Namespace: ptr.To(gatev1.Namespace(namespace)), - Name: gatev1.ObjectName(listener.GWName), - SectionName: ptr.To(gatev1.SectionName(listener.Name)), - }, - ControllerName: controllerName, - Conditions: []metav1.Condition{{ - Type: string(gatev1.RouteConditionResolvedRefs), - Status: metav1.ConditionFalse, - ObservedGeneration: route.Generation, - LastTransitionTime: metav1.Now(), - Reason: string(gatev1.RouteReasonBackendNotFound), - Message: fmt.Sprintf("BackendTLSPolicy has no valid TargetRef for Service %s/%s", namespace, string(backendRef.Name)), - }}, - }}, - } + policyAncestorStatus := gatev1.PolicyAncestorStatus{ + AncestorRef: gatev1.ParentReference{ + Group: ptr.To(gatev1.Group(groupGateway)), + Kind: ptr.To(gatev1.Kind(kindGateway)), + Namespace: ptr.To(gatev1.Namespace(namespace)), + Name: gatev1.ObjectName(listener.GWName), + SectionName: ptr.To(gatev1.SectionName(listener.Name)), + }, + ControllerName: controllerName, + } + // Multiple BackendTLSPolicies can match the same service port, meaning that there is a conflict. + if serversTransport != nil { + policyAncestorStatus.Conditions = append(policyAncestorStatus.Conditions, + metav1.Condition{ + Type: string(gatev1.BackendTLSPolicyConditionResolvedRefs), + Status: metav1.ConditionFalse, + ObservedGeneration: policy.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.BackendTLSPolicyReasonResolvedRefs), + }, + metav1.Condition{ + Type: string(gatev1.PolicyConditionAccepted), + Status: metav1.ConditionFalse, + ObservedGeneration: policy.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.PolicyReasonConflicted), + }, + ) + + status := gatev1.PolicyStatus{ + Ancestors: []gatev1.PolicyAncestorStatus{policyAncestorStatus}, + } if err := p.client.UpdateBackendTLSPolicyStatus(ctx, ktypes.NamespacedName{Namespace: policy.Namespace, Name: policy.Name}, status); err != nil { - log.Ctx(ctx).Warn().Err(err). - Msg("Unable to update BackendTLSPolicy status") + log.Ctx(ctx).Warn().Err(err).Msg("Unable to update conflicting BackendTLSPolicy status") } - } - } - if matchedPolicy != nil { - st, err = p.loadServersTransport(namespace, *matchedPolicy) - if err != nil { + continue + } + + var resolvedRefCondition metav1.Condition + serversTransport, resolvedRefCondition = p.loadServersTransport(namespace, policy) + + policyAncestorStatus.Conditions = append(policyAncestorStatus.Conditions, resolvedRefCondition) + if resolvedRefCondition.Status == metav1.ConditionFalse { + policyAncestorStatus.Conditions = append(policyAncestorStatus.Conditions, metav1.Condition{ + Type: string(gatev1.PolicyConditionAccepted), + Status: metav1.ConditionFalse, + ObservedGeneration: policy.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.BackendTLSPolicyReasonNoValidCACertificate), + }) + } else { + policyAncestorStatus.Conditions = append(policyAncestorStatus.Conditions, metav1.Condition{ + Type: string(gatev1.PolicyConditionAccepted), + Status: metav1.ConditionTrue, + ObservedGeneration: policy.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.PolicyReasonAccepted), + }) + } + + status := gatev1.PolicyStatus{ + Ancestors: []gatev1.PolicyAncestorStatus{policyAncestorStatus}, + } + if err := p.client.UpdateBackendTLSPolicyStatus(ctx, ktypes.NamespacedName{Namespace: policy.Namespace, Name: policy.Name}, status); err != nil { + log.Ctx(ctx).Warn().Err(err).Msg("Unable to update BackendTLSPolicy status") + } + + // When something wen wrong during the loading of a ServersTransport, + // we stop here and return a route condition error. + if resolvedRefCondition.Status == metav1.ConditionFalse { return nil, nil, &metav1.Condition{ Type: string(gatev1.RouteConditionResolvedRefs), Status: metav1.ConditionFalse, ObservedGeneration: route.Generation, LastTransitionTime: metav1.Now(), Reason: string(gatev1.RouteReasonRefNotPermitted), - Message: fmt.Sprintf("Cannot apply BackendTLSPolicy for Service %s/%s: %s", namespace, string(backendRef.Name), err), + Message: fmt.Sprintf("Cannot apply BackendTLSPolicy for Service %s/%s: %s", namespace, string(backendRef.Name), resolvedRefCondition.Message), } } - // A backend TLS policy has been found for the service, a serversTransport configuration has been created, use/force HTTPS. - protocol = "https" } } lb := &dynamic.ServersLoadBalancer{} lb.SetDefaults() - // Guess the protocol from the service port if not set by the backend TLS policy - if protocol == "" { + // If a ServersTransport is set, it means a BackendTLSPolicy matched the service port, and we can safely assume the protocol is HTTPS. + // When no ServersTransport is set, we need to determine the protocol based on the service port. + protocol := "https" + if serversTransport == nil { protocol, err = getHTTPServiceProtocol(svcPort) if err != nil { return nil, nil, &metav1.Condition{ @@ -509,40 +548,70 @@ func (p *Provider) loadHTTPServers(ctx context.Context, namespace string, route URL: fmt.Sprintf("%s://%s", protocol, net.JoinHostPort(ba.IP, strconv.Itoa(int(ba.Port)))), }) } - return lb, st, nil + return lb, serversTransport, nil } -func (p *Provider) loadServersTransport(namespace string, policy gatev1alpha3.BackendTLSPolicy) (*dynamic.ServersTransport, error) { +func (p *Provider) loadServersTransport(namespace string, policy *gatev1.BackendTLSPolicy) (*dynamic.ServersTransport, metav1.Condition) { st := &dynamic.ServersTransport{ ServerName: string(policy.Spec.Validation.Hostname), } if policy.Spec.Validation.WellKnownCACertificates != nil { - return st, nil + return st, metav1.Condition{ + Type: string(gatev1.BackendTLSPolicyConditionResolvedRefs), + Status: metav1.ConditionTrue, + ObservedGeneration: policy.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.BackendTLSPolicyReasonResolvedRefs), + } } for _, caCertRef := range policy.Spec.Validation.CACertificateRefs { if (caCertRef.Group != "" && caCertRef.Group != groupCore) || caCertRef.Kind != "ConfigMap" { - continue + return nil, metav1.Condition{ + Type: string(gatev1.BackendTLSPolicyConditionResolvedRefs), + Status: metav1.ConditionFalse, + ObservedGeneration: policy.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.BackendTLSPolicyReasonInvalidKind), + Message: "Only ConfigMaps are supported", + } } - configMap, exists, err := p.client.GetConfigMap(namespace, string(caCertRef.Name)) + configMap, err := p.client.GetConfigMap(namespace, string(caCertRef.Name)) if err != nil { - return nil, fmt.Errorf("getting configmap: %w", err) - } - if !exists { - return nil, fmt.Errorf("configmap %s/%s not found", namespace, string(caCertRef.Name)) + return nil, metav1.Condition{ + Type: string(gatev1.BackendTLSPolicyConditionResolvedRefs), + Status: metav1.ConditionFalse, + ObservedGeneration: policy.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.BackendTLSPolicyReasonInvalidCACertificateRef), + Message: fmt.Sprintf("getting configmap %s/%s: %s", namespace, string(caCertRef.Name), err), + } } caCRT, ok := configMap.Data["ca.crt"] if !ok { - return nil, fmt.Errorf("configmap %s/%s does not have ca.crt", namespace, string(caCertRef.Name)) + return nil, metav1.Condition{ + Type: string(gatev1.BackendTLSPolicyConditionResolvedRefs), + Status: metav1.ConditionFalse, + ObservedGeneration: policy.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.BackendTLSPolicyReasonInvalidCACertificateRef), + Message: fmt.Sprintf("configmap %s/%s does not have a ca.crt", namespace, string(caCertRef.Name)), + } } st.RootCAs = append(st.RootCAs, types.FileOrContent(caCRT)) } - return st, nil + return st, metav1.Condition{ + Type: string(gatev1.BackendTLSPolicyConditionResolvedRefs), + Status: metav1.ConditionTrue, + ObservedGeneration: policy.Generation, + LastTransitionTime: metav1.Now(), + Reason: string(gatev1.BackendTLSPolicyReasonResolvedRefs), + } } func buildHostRule(hostnames []gatev1.Hostname) (string, int) { @@ -746,7 +815,7 @@ func createRequestRedirect(filter *gatev1.HTTPRequestRedirectFilter, pathMatch g port = ptr.To("") } if filter.Port != nil { - port = ptr.To(fmt.Sprintf("%d", *filter.Port)) + port = ptr.To(strconv.Itoa(int(*filter.Port))) } var path *string diff --git a/pkg/provider/kubernetes/gateway/kubernetes.go b/pkg/provider/kubernetes/gateway/kubernetes.go index 3010f6bc0..0c50b76a8 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes.go +++ b/pkg/provider/kubernetes/gateway/kubernetes.go @@ -121,7 +121,7 @@ type gatewayListener struct { Port gatev1.PortNumber Protocol gatev1.ProtocolType - TLS *gatev1.GatewayTLSConfig + TLS *gatev1.ListenerTLSConfig Hostname *gatev1.Hostname Status *gatev1.ListenerStatus AllowedNamespaces []string @@ -325,14 +325,12 @@ func (p *Provider) loadConfigurationFromGateways(ctx context.Context) *dynamic.C } var supportedFeatures []gatev1.SupportedFeature - if p.ExperimentalChannel { - for _, feature := range SupportedFeatures() { - supportedFeatures = append(supportedFeatures, gatev1.SupportedFeature{Name: gatev1.FeatureName(feature)}) - } - slices.SortFunc(supportedFeatures, func(a, b gatev1.SupportedFeature) int { - return strings.Compare(string(a.Name), string(b.Name)) - }) + for _, feature := range SupportedFeatures() { + supportedFeatures = append(supportedFeatures, gatev1.SupportedFeature{Name: gatev1.FeatureName(feature)}) } + slices.SortFunc(supportedFeatures, func(a, b gatev1.SupportedFeature) int { + return strings.Compare(string(a.Name), string(b.Name)) + }) gatewayClassNames := map[string]struct{}{} for _, gatewayClass := range gatewayClasses { @@ -768,12 +766,9 @@ func (p *Provider) gatewayAddresses() ([]gatev1.GatewayStatusAddress, error) { svcRef := p.StatusAddress.Service if svcRef.Name != "" && svcRef.Namespace != "" { - svc, exists, err := p.client.GetService(svcRef.Namespace, svcRef.Name) + svc, err := p.client.GetService(svcRef.Namespace, svcRef.Name) if err != nil { - return nil, fmt.Errorf("unable to get service: %w", err) - } - if !exists { - return nil, fmt.Errorf("could not find a service with name %s in namespace %s", svcRef.Name, svcRef.Namespace) + return nil, fmt.Errorf("getting service: %w", err) } var addresses []gatev1.GatewayStatusAddress @@ -836,25 +831,27 @@ func (p *Provider) isReferenceGranted(fromKind, fromNamespace, toGroup, toKind, } func (p *Provider) getTLS(secretName gatev1.ObjectName, namespace string) (*tls.CertAndStores, error) { - secret, exists, err := p.client.GetSecret(namespace, string(secretName)) + secret, err := p.client.GetSecret(namespace, string(secretName)) if err != nil { - return nil, fmt.Errorf("failed to fetch secret %s/%s: %w", namespace, secretName, err) - } - if !exists { - return nil, fmt.Errorf("secret %s/%s does not exist", namespace, secretName) + return nil, fmt.Errorf("getting secret: %w", err) } cert, key, err := getCertificateBlocks(secret, namespace, string(secretName)) if err != nil { - return nil, err + return nil, fmt.Errorf("getting certificate blocks: %w", err) } - return &tls.CertAndStores{ + certAndStore := &tls.CertAndStores{ Certificate: tls.Certificate{ CertFile: types.FileOrContent(cert), KeyFile: types.FileOrContent(key), }, - }, nil + } + if _, err := certAndStore.GetCertificate(); err != nil { + return nil, fmt.Errorf("validating certificate: %w", err) + } + + return certAndStore, nil } func (p *Provider) allowedNamespaces(gatewayNamespace string, routeNamespaces *gatev1.RouteNamespaces) ([]string, error) { @@ -891,20 +888,17 @@ func (p *Provider) getBackendAddresses(namespace string, ref gatev1.BackendRef) return nil, corev1.ServicePort{}, errors.New("port is required for Kubernetes Service reference") } - service, exists, err := p.client.GetService(namespace, string(ref.Name)) + service, err := p.client.GetService(namespace, string(ref.Name)) if err != nil { return nil, corev1.ServicePort{}, fmt.Errorf("getting service: %w", err) } - if !exists { - return nil, corev1.ServicePort{}, errors.New("service not found") - } if service.Spec.Type == corev1.ServiceTypeExternalName { return nil, corev1.ServicePort{}, errors.New("type ExternalName is not supported for Kubernetes Service reference") } var svcPort *corev1.ServicePort for _, p := range service.Spec.Ports { - if p.Port == int32(*ref.Port) { + if p.Port == *ref.Port { svcPort = &p break } diff --git a/pkg/provider/kubernetes/gateway/kubernetes_test.go b/pkg/provider/kubernetes/gateway/kubernetes_test.go index 90e93f64f..1f5c1df8f 100644 --- a/pkg/provider/kubernetes/gateway/kubernetes_test.go +++ b/pkg/provider/kubernetes/gateway/kubernetes_test.go @@ -49,10 +49,29 @@ func init() { } } +const ( + listenerCert string = `-----BEGIN CERTIFICATE----- +MIIBqDCCAU6gAwIBAgIUYOsr0QgHOBq4kYRCL5+TDdVt6bQwCgYIKoZIzj0EAwIw +FjEUMBIGA1UEAwwLZXhhbXBsZS5jb20wHhcNMjUxMDEwMDcxNzMwWhcNMzUxMDA4 +MDcxNzMwWjAWMRQwEgYDVQQDDAtleGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqG +SM49AwEHA0IABDOriw3ZQ7wIhWrbPS6JFQT3bToNCF00vSV5fab6TbXyL8tlsGre +TRIF2EwgsteMOkxGKKSlDvuaDwq8p/qV+0ujejB4MB0GA1UdDgQWBBRMEkuexXQh +UtDgRg1KBv72CDq+EzAfBgNVHSMEGDAWgBRMEkuexXQhUtDgRg1KBv72CDq+EzAP +BgNVHRMBAf8EBTADAQH/MCUGA1UdEQQeMByCC2V4YW1wbGUuY29tgg0qLmV4YW1w +bGUuY29tMAoGCCqGSM49BAMCA0gAMEUCIQDs87Vk0swA6HgOJjROye1mxD83qcGy +peFgoxV93Dy+cwIgV0MMEJJbVsTyZK3EQ++Xc5rEL78nrJ+YIEV+rCUWj5U= +-----END CERTIFICATE-----` + listenerKey string = `-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgnwgL5DY4UB14sM6f +DikQdtqh2QW1ArfF4fc1UFzifdGhRANCAAQzq4sN2UO8CIVq2z0uiRUE9206DQhd +NL0leX2m+k218i/LZbBq3k0SBdhMILLXjDpMRiikpQ77mg8KvKf6lftL +-----END PRIVATE KEY-----` +) + func TestGatewayClassLabelSelector(t *testing.T) { k8sObjects, gwObjects := readResources(t, []string{"gatewayclass_labelselector.yaml"}) - kubeClient := kubefake.NewSimpleClientset(k8sObjects...) + kubeClient := kubefake.NewClientset(k8sObjects...) gwClient := newGatewaySimpleClientSet(t, gwObjects...) client := newClientImpl(kubeClient, gwClient) @@ -561,8 +580,8 @@ func TestLoadHTTPRoutes(t *testing.T) { Certificates: []*tls.CertAndStores{ { Certificate: tls.Certificate{ - CertFile: types.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: types.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + CertFile: types.FileOrContent(listenerCert), + KeyFile: types.FileOrContent(listenerKey), }, }, }, @@ -755,8 +774,8 @@ func TestLoadHTTPRoutes(t *testing.T) { Certificates: []*tls.CertAndStores{ { Certificate: tls.Certificate{ - CertFile: types.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: types.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + CertFile: types.FileOrContent(listenerCert), + KeyFile: types.FileOrContent(listenerKey), }, }, }, @@ -1214,8 +1233,8 @@ func TestLoadHTTPRoutes(t *testing.T) { Certificates: []*tls.CertAndStores{ { Certificate: tls.Certificate{ - CertFile: types.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: types.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + CertFile: types.FileOrContent(listenerCert), + KeyFile: types.FileOrContent(listenerKey), }, }, }, @@ -1308,8 +1327,8 @@ func TestLoadHTTPRoutes(t *testing.T) { Certificates: []*tls.CertAndStores{ { Certificate: tls.Certificate{ - CertFile: types.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: types.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + CertFile: types.FileOrContent(listenerCert), + KeyFile: types.FileOrContent(listenerKey), }, }, }, @@ -2202,74 +2221,11 @@ func TestLoadHTTPRoutes(t *testing.T) { }, }, { - desc: "Simple HTTPRoute and BackendTLSPolicy, experimental channel disabled", + desc: "Simple HTTPRoute and BackendTLSPolicy with CA certificate", paths: []string{"services.yml", "httproute/with_backend_tls_policy.yml"}, entryPoints: map[string]Entrypoint{"web": { Address: ":80", }}, - expected: &dynamic.Configuration{ - UDP: &dynamic.UDPConfiguration{ - Routers: map[string]*dynamic.UDPRouter{}, - Services: map[string]*dynamic.UDPService{}, - }, - TCP: &dynamic.TCPConfiguration{ - Routers: map[string]*dynamic.TCPRouter{}, - Middlewares: map[string]*dynamic.TCPMiddleware{}, - Services: map[string]*dynamic.TCPService{}, - ServersTransports: map[string]*dynamic.TCPServersTransport{}, - }, - HTTP: &dynamic.HTTPConfiguration{ - Routers: map[string]*dynamic.Router{ - "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-1c0cf64bde37d9d0df06": { - EntryPoints: []string{"web"}, - Service: "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-1c0cf64bde37d9d0df06-wrr", - Rule: "Host(`foo.com`) && Path(`/bar`)", - Priority: 100008, - RuleSyntax: "default", - }, - }, - Middlewares: map[string]*dynamic.Middleware{}, - Services: map[string]*dynamic.Service{ - "httproute-default-http-app-1-gw-default-my-gateway-ep-web-0-1c0cf64bde37d9d0df06-wrr": { - Weighted: &dynamic.WeightedRoundRobin{ - Services: []dynamic.WRRService{ - { - Name: "default-whoami-http-80", - Weight: ptr.To(1), - }, - }, - }, - }, - "default-whoami-http-80": { - LoadBalancer: &dynamic.ServersLoadBalancer{ - Strategy: dynamic.BalancerStrategyWRR, - Servers: []dynamic.Server{ - { - URL: "http://10.10.0.1:80", - }, - { - URL: "http://10.10.0.2:80", - }, - }, - PassHostHeader: ptr.To(true), - ResponseForwarding: &dynamic.ResponseForwarding{ - FlushInterval: ptypes.Duration(100 * time.Millisecond), - }, - }, - }, - }, - ServersTransports: map[string]*dynamic.ServersTransport{}, - }, - TLS: &dynamic.TLSConfiguration{}, - }, - }, - { - desc: "Simple HTTPRoute and BackendTLSPolicy with CA certificate, experimental channel enabled", - paths: []string{"services.yml", "httproute/with_backend_tls_policy.yml"}, - entryPoints: map[string]Entrypoint{"web": { - Address: ":80", - }}, - experimentalChannel: true, expected: &dynamic.Configuration{ UDP: &dynamic.UDPConfiguration{ Routers: map[string]*dynamic.UDPRouter{}, @@ -2326,8 +2282,8 @@ func TestLoadHTTPRoutes(t *testing.T) { "default-whoami-http-80": { ServerName: "whoami", RootCAs: []types.FileOrContent{ - "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=", - "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0=", + "CA1", + "CA2", }, }, }, @@ -2336,12 +2292,11 @@ func TestLoadHTTPRoutes(t *testing.T) { }, }, { - desc: "Simple HTTPRoute and BackendTLSPolicy with System CA, experimental channel enabled", + desc: "Simple HTTPRoute and BackendTLSPolicy with System CA", paths: []string{"services.yml", "httproute/with_backend_tls_policy_system.yml"}, entryPoints: map[string]Entrypoint{"web": { Address: ":80", }}, - experimentalChannel: true, expected: &dynamic.Configuration{ UDP: &dynamic.UDPConfiguration{ Routers: map[string]*dynamic.UDPRouter{}, @@ -2597,7 +2552,7 @@ func TestLoadHTTPRoutes(t *testing.T) { k8sObjects, gwObjects := readResources(t, test.paths) - kubeClient := kubefake.NewSimpleClientset(k8sObjects...) + kubeClient := kubefake.NewClientset(k8sObjects...) gwClient := newGatewaySimpleClientSet(t, gwObjects...) client := newClientImpl(kubeClient, gwClient) @@ -3058,7 +3013,7 @@ func TestLoadHTTPRoutes_backendExtensionRef(t *testing.T) { k8sObjects, gwObjects := readResources(t, test.paths) - kubeClient := kubefake.NewSimpleClientset(k8sObjects...) + kubeClient := kubefake.NewClientset(k8sObjects...) gwClient := newGatewaySimpleClientSet(t, gwObjects...) client := newClientImpl(kubeClient, gwClient) @@ -3344,7 +3299,7 @@ func TestLoadHTTPRoutes_filterExtensionRef(t *testing.T) { k8sObjects, gwObjects := readResources(t, []string{"services.yml", "httproute/filter_extension_ref.yml"}) - kubeClient := kubefake.NewSimpleClientset(k8sObjects...) + kubeClient := kubefake.NewClientset(k8sObjects...) gwClient := newGatewaySimpleClientSet(t, gwObjects...) client := newClientImpl(kubeClient, gwClient) @@ -3636,7 +3591,7 @@ func TestLoadGRPCRoutes_filterExtensionRef(t *testing.T) { k8sObjects, gwObjects := readResources(t, []string{"services.yml", "grpcroute/filter_extension_ref.yml"}) - kubeClient := kubefake.NewSimpleClientset(k8sObjects...) + kubeClient := kubefake.NewClientset(k8sObjects...) gwClient := newGatewaySimpleClientSet(t, gwObjects...) client := newClientImpl(kubeClient, gwClient) @@ -4217,8 +4172,8 @@ func TestLoadTCPRoutes(t *testing.T) { Certificates: []*tls.CertAndStores{ { Certificate: tls.Certificate{ - CertFile: types.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: types.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + CertFile: types.FileOrContent(listenerCert), + KeyFile: types.FileOrContent(listenerKey), }, }, }, @@ -4597,7 +4552,7 @@ func TestLoadTCPRoutes(t *testing.T) { k8sObjects, gwObjects := readResources(t, test.paths) - kubeClient := kubefake.NewSimpleClientset(k8sObjects...) + kubeClient := kubefake.NewClientset(k8sObjects...) gwClient := newGatewaySimpleClientSet(t, gwObjects...) client := newClientImpl(kubeClient, gwClient) @@ -4809,8 +4764,8 @@ func TestLoadTLSRoutes(t *testing.T) { Certificates: []*tls.CertAndStores{ { Certificate: tls.Certificate{ - CertFile: types.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: types.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + CertFile: types.FileOrContent(listenerCert), + KeyFile: types.FileOrContent(listenerKey), }, }, }, @@ -4909,8 +4864,8 @@ func TestLoadTLSRoutes(t *testing.T) { Certificates: []*tls.CertAndStores{ { Certificate: tls.Certificate{ - CertFile: types.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: types.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + CertFile: types.FileOrContent(listenerCert), + KeyFile: types.FileOrContent(listenerKey), }, }, }, @@ -5127,8 +5082,8 @@ func TestLoadTLSRoutes(t *testing.T) { Certificates: []*tls.CertAndStores{ { Certificate: tls.Certificate{ - CertFile: types.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: types.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + CertFile: types.FileOrContent(listenerCert), + KeyFile: types.FileOrContent(listenerKey), }, }, }, @@ -5197,8 +5152,8 @@ func TestLoadTLSRoutes(t *testing.T) { Certificates: []*tls.CertAndStores{ { Certificate: tls.Certificate{ - CertFile: types.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: types.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + CertFile: types.FileOrContent(listenerCert), + KeyFile: types.FileOrContent(listenerKey), }, }, }, @@ -5876,7 +5831,7 @@ func TestLoadTLSRoutes(t *testing.T) { k8sObjects, gwObjects := readResources(t, test.paths) - kubeClient := kubefake.NewSimpleClientset(k8sObjects...) + kubeClient := kubefake.NewClientset(k8sObjects...) gwClient := newGatewaySimpleClientSet(t, gwObjects...) client := newClientImpl(kubeClient, gwClient) @@ -6165,8 +6120,8 @@ func TestLoadMixedRoutes(t *testing.T) { Certificates: []*tls.CertAndStores{ { Certificate: tls.Certificate{ - CertFile: types.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: types.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + CertFile: types.FileOrContent(listenerCert), + KeyFile: types.FileOrContent(listenerKey), }, }, }, @@ -6354,8 +6309,8 @@ func TestLoadMixedRoutes(t *testing.T) { Certificates: []*tls.CertAndStores{ { Certificate: tls.Certificate{ - CertFile: types.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: types.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + CertFile: types.FileOrContent(listenerCert), + KeyFile: types.FileOrContent(listenerKey), }, }, }, @@ -6612,8 +6567,8 @@ func TestLoadMixedRoutes(t *testing.T) { Certificates: []*tls.CertAndStores{ { Certificate: tls.Certificate{ - CertFile: types.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: types.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + CertFile: types.FileOrContent(listenerCert), + KeyFile: types.FileOrContent(listenerKey), }, }, }, @@ -6773,8 +6728,8 @@ func TestLoadMixedRoutes(t *testing.T) { Certificates: []*tls.CertAndStores{ { Certificate: tls.Certificate{ - CertFile: types.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: types.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + CertFile: types.FileOrContent(listenerCert), + KeyFile: types.FileOrContent(listenerKey), }, }, }, @@ -6913,8 +6868,8 @@ func TestLoadMixedRoutes(t *testing.T) { Certificates: []*tls.CertAndStores{ { Certificate: tls.Certificate{ - CertFile: types.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: types.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + CertFile: types.FileOrContent(listenerCert), + KeyFile: types.FileOrContent(listenerKey), }, }, }, @@ -6933,7 +6888,7 @@ func TestLoadMixedRoutes(t *testing.T) { k8sObjects, gwObjects := readResources(t, test.paths) - kubeClient := kubefake.NewSimpleClientset(k8sObjects...) + kubeClient := kubefake.NewClientset(k8sObjects...) gwClient := newGatewaySimpleClientSet(t, gwObjects...) client := newClientImpl(kubeClient, gwClient) @@ -7125,8 +7080,8 @@ func TestLoadRoutesWithReferenceGrants(t *testing.T) { Certificates: []*tls.CertAndStores{ { Certificate: tls.Certificate{ - CertFile: types.FileOrContent("-----BEGIN CERTIFICATE-----\n-----END CERTIFICATE-----"), - KeyFile: types.FileOrContent("-----BEGIN PRIVATE KEY-----\n-----END PRIVATE KEY-----"), + CertFile: types.FileOrContent(listenerCert), + KeyFile: types.FileOrContent(listenerKey), }, }, }, @@ -7269,7 +7224,7 @@ func TestLoadRoutesWithReferenceGrants(t *testing.T) { k8sObjects, gwObjects := readResources(t, test.paths) - kubeClient := kubefake.NewSimpleClientset(k8sObjects...) + kubeClient := kubefake.NewClientset(k8sObjects...) gwClient := newGatewaySimpleClientSet(t, gwObjects...) client := newClientImpl(kubeClient, gwClient) @@ -8174,7 +8129,7 @@ func Test_gatewayAddresses(t *testing.T) { k8sObjects, gwObjects := readResources(t, test.paths) - kubeClient := kubefake.NewSimpleClientset(k8sObjects...) + kubeClient := kubefake.NewClientset(k8sObjects...) gwClient := newGatewaySimpleClientSet(t, gwObjects...) client := newClientImpl(kubeClient, gwClient) @@ -8330,7 +8285,7 @@ func Test_upsertRouteConditionResolvedRefs(t *testing.T) { } } -// We cannot use the gateway-api fake.NewSimpleClientset due to Gateway being pluralized as "gatewaies" instead of "gateways". +// We cannot use the gateway-api fake.NewClientset due to Gateway being pluralized as "gatewaies" instead of "gateways". func newGatewaySimpleClientSet(t *testing.T, objects ...runtime.Object) *gatefake.Clientset { t.Helper() diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/00-ingress-with-no-annotation.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/00-ingress-with-no-annotation.yml new file mode 100644 index 000000000..8b92a5958 --- /dev/null +++ b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/00-ingress-with-no-annotation.yml @@ -0,0 +1,23 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ingress-with-no-annotation + namespace: default +spec: + ingressClassName: nginx + rules: + - host: whoami.localhost + http: + paths: + - backend: + service: + name: whoami + port: + number: 80 + path: / + pathType: Prefix + tls: + - hosts: + - whoami.localhost + secretName: whoami-tls diff --git a/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/10-ingress-with-use-regex.yml b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/10-ingress-with-use-regex.yml new file mode 100644 index 000000000..4cd26cff9 --- /dev/null +++ b/pkg/provider/kubernetes/ingress-nginx/fixtures/ingresses/10-ingress-with-use-regex.yml @@ -0,0 +1,22 @@ +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: ingress-with-use-regex + namespace: default + annotations: + nginx.ingress.kubernetes.io/use-regex: "true" + +spec: + ingressClassName: nginx + rules: + - host: use-regex.localhost + http: + paths: + - path: /test(.*) + pathType: ImplementationSpecific + backend: + service: + name: whoami + port: + number: 80 diff --git a/pkg/provider/kubernetes/ingress-nginx/kubernetes.go b/pkg/provider/kubernetes/ingress-nginx/kubernetes.go index e8f583ba0..268db6ef7 100644 --- a/pkg/provider/kubernetes/ingress-nginx/kubernetes.go +++ b/pkg/provider/kubernetes/ingress-nginx/kubernetes.go @@ -22,7 +22,6 @@ import ( "github.com/traefik/traefik/v3/pkg/job" "github.com/traefik/traefik/v3/pkg/observability/logs" "github.com/traefik/traefik/v3/pkg/provider" - "github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s" "github.com/traefik/traefik/v3/pkg/safe" "github.com/traefik/traefik/v3/pkg/tls" "github.com/traefik/traefik/v3/pkg/types" @@ -80,6 +79,9 @@ type Provider struct { DefaultBackendService string `description:"Service used to serve HTTP requests not matching any known server name (catch-all). Takes the form 'namespace/name'." json:"defaultBackendService,omitempty" toml:"defaultBackendService,omitempty" yaml:"defaultBackendService,omitempty" export:"true"` DisableSvcExternalName bool `description:"Disable support for Services of type ExternalName." json:"disableSvcExternalName,omitempty" toml:"disableSvcExternalName,omitempty" yaml:"disableSvcExternalName,omitempty" export:"true"` + // NonTLSEntryPoints contains the names of entrypoints that are configured without TLS. + NonTLSEntryPoints []string `json:"-" toml:"-" yaml:"-" label:"-" file:"-"` + defaultBackendServiceNamespace string defaultBackendServiceName string @@ -453,7 +455,7 @@ func (p *Provider) loadConfiguration(ctx context.Context) *dynamic.Configuration } // TODO: if no service, do not add middlewares and 503. - serviceName := provider.Normalize(ingress.Namespace + "-" + pa.Backend.Service.Name + "-" + portString) + serviceName := provider.Normalize(ingress.Namespace + "-" + ingress.Name + "-" + pa.Backend.Service.Name + "-" + portString) service, err := p.buildService(ingress.Namespace, pa.Backend, ingressConfig) if err != nil { @@ -509,7 +511,7 @@ func (p *Provider) buildServersTransport(namespace, name string, cfg ingressConf Name: provider.Normalize(namespace + "-" + name), ServersTransport: &dynamic.ServersTransport{ ServerName: ptr.Deref(cfg.ProxySSLName, ptr.Deref(cfg.ProxySSLServerName, "")), - InsecureSkipVerify: strings.ToLower(ptr.Deref(cfg.ProxySSLVerify, "off")) == "on", + InsecureSkipVerify: strings.ToLower(ptr.Deref(cfg.ProxySSLVerify, "off")) == "off", }, } @@ -643,7 +645,10 @@ func (p *Provider) getBackendAddresses(namespace string, backend netv1.IngressBa } for _, endpoint := range endpointSlice.Endpoints { - if !k8s.EndpointServing(endpoint) { + // The Serving condition allows to track if the Pod can receive traffic. + // It is set to true when the Pod is Ready or Terminating. + // From the go documentation, a nil value should be interpreted as "true". + if !ptr.Deref(endpoint.Conditions.Serving, true) { continue } @@ -655,7 +660,7 @@ func (p *Provider) getBackendAddresses(namespace string, backend netv1.IngressBa uniqAddresses[address] = struct{}{} addresses = append(addresses, backendAddress{ Address: net.JoinHostPort(address, strconv.Itoa(int(port))), - Fenced: ptr.Deref(endpoint.Conditions.Terminating, false) && ptr.Deref(endpoint.Conditions.Serving, false), + Fenced: ptr.Deref(endpoint.Conditions.Terminating, false), }) } } @@ -798,7 +803,7 @@ func (p *Provider) applyMiddlewares(namespace, routerKey string, ingressConfig i // Apply SSL redirect is mandatory to be applied after all other middlewares. // TODO: check how to remove this, and create the HTTP router elsewhere. - applySSLRedirectConfiguration(routerKey, ingressConfig, hasTLS, rt, conf) + p.applySSLRedirectConfiguration(routerKey, ingressConfig, hasTLS, rt, conf) return nil } @@ -934,7 +939,7 @@ func applyCORSConfiguration(routerName string, ingressConfig ingressConfig, rt * rt.Middlewares = append(rt.Middlewares, corsMiddlewareName) } -func applySSLRedirectConfiguration(routerName string, ingressConfig ingressConfig, hasTLS bool, rt *dynamic.Router, conf *dynamic.Configuration) { +func (p *Provider) applySSLRedirectConfiguration(routerName string, ingressConfig ingressConfig, hasTLS bool, rt *dynamic.Router, conf *dynamic.Configuration) { var forceSSLRedirect bool if ingressConfig.ForceSSLRedirect != nil { forceSSLRedirect = *ingressConfig.ForceSSLRedirect @@ -942,39 +947,52 @@ func applySSLRedirectConfiguration(routerName string, ingressConfig ingressConfi sslRedirect := ptr.Deref(ingressConfig.SSLRedirect, hasTLS) - if !forceSSLRedirect && !sslRedirect { - if hasTLS { - httpRouter := &dynamic.Router{ - Rule: rt.Rule, - // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. - RuleSyntax: "default", - Middlewares: rt.Middlewares, - Service: rt.Service, - } + if hasTLS { + // An Ingress with TLS configuration creates only a Traefik router with a TLS configuration, + // so no Non-TLS router exists to handle HTTP traffic, and we should create it. + httpRouter := &dynamic.Router{ + // Only attach to entryPoint which do not activate TLS. + EntryPoints: p.NonTLSEntryPoints, + Rule: rt.Rule, + // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. + RuleSyntax: "default", + Middlewares: rt.Middlewares, + Service: rt.Service, + } + conf.HTTP.Routers[routerName+"-http"] = httpRouter - conf.HTTP.Routers[routerName+"-http"] = httpRouter + // If either forceSSLRedirect or sslRedirect are enabled, + // the HTTP router needs to redirect to HTTPS. + if forceSSLRedirect || sslRedirect { + redirectMiddlewareName := routerName + "-redirect-scheme" + conf.HTTP.Middlewares[redirectMiddlewareName] = &dynamic.Middleware{ + RedirectScheme: &dynamic.RedirectScheme{ + Scheme: "https", + ForcePermanentRedirect: true, + }, + } + httpRouter.Middlewares = []string{redirectMiddlewareName} + httpRouter.Service = "noop@internal" } return } - redirectRouter := &dynamic.Router{ - Rule: rt.Rule, - // "default" stands for the default rule syntax in Traefik v3, i.e. the v3 syntax. - RuleSyntax: "default", - Service: "noop@internal", + // An Ingress with no TLS configuration and forceSSLRedirect annotation should always redirect on HTTPS, + // even if no route exists for HTTPS. + if forceSSLRedirect { + redirectMiddlewareName := routerName + "-redirect-scheme" + conf.HTTP.Middlewares[redirectMiddlewareName] = &dynamic.Middleware{ + RedirectScheme: &dynamic.RedirectScheme{ + Scheme: "https", + ForcePermanentRedirect: true, + }, + } + rt.Middlewares = append([]string{redirectMiddlewareName}, rt.Middlewares...) } - redirectMiddlewareName := routerName + "-redirect-scheme" - conf.HTTP.Middlewares[redirectMiddlewareName] = &dynamic.Middleware{ - RedirectScheme: &dynamic.RedirectScheme{ - Scheme: "https", - Permanent: true, - }, - } - redirectRouter.Middlewares = append(redirectRouter.Middlewares, redirectMiddlewareName) - - conf.HTTP.Routers[routerName+"-redirect"] = redirectRouter + // An Ingress that is not forcing sslRedirect and has no TLS configuration does not redirect, + // even if sslRedirect is enabled. } func applyForwardAuthConfiguration(routerName string, ingressConfig ingressConfig, rt *dynamic.Router, conf *dynamic.Configuration) error { @@ -1049,7 +1067,7 @@ func buildRule(host string, pa netv1.HTTPIngressPath, config ingressConfig) stri rules = append(rules, fmt.Sprintf("Path(`%s`)", pa.Path)) case netv1.PathTypePrefix: if ptr.Deref(config.UseRegex, false) { - rules = append(rules, fmt.Sprintf("PathRegexp(`^%s`)", regexp.QuoteMeta(pa.Path))) + rules = append(rules, fmt.Sprintf("PathRegexp(`^%s`)", pa.Path)) } else { rules = append(rules, buildPrefixRule(pa.Path)) } diff --git a/pkg/provider/kubernetes/ingress-nginx/kubernetes_test.go b/pkg/provider/kubernetes/ingress-nginx/kubernetes_test.go index 37f819300..95781e8c8 100644 --- a/pkg/provider/kubernetes/ingress-nginx/kubernetes_test.go +++ b/pkg/provider/kubernetes/ingress-nginx/kubernetes_test.go @@ -46,6 +46,76 @@ func TestLoadIngresses(t *testing.T) { TLS: &dynamic.TLSConfiguration{}, }, }, + { + desc: "No annotation", + paths: []string{ + "ingresses/00-ingress-with-no-annotation.yml", + "ingressclasses.yml", + "services.yml", + "secrets.yml", + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "default-ingress-with-no-annotation-rule-0-path-0": { + Rule: "Host(`whoami.localhost`) && PathPrefix(`/`)", + RuleSyntax: "default", + TLS: &dynamic.RouterTLSConfig{}, + Service: "default-ingress-with-no-annotation-whoami-80", + }, + "default-ingress-with-no-annotation-rule-0-path-0-http": { + EntryPoints: []string{"web"}, + Rule: "Host(`whoami.localhost`) && PathPrefix(`/`)", + RuleSyntax: "default", + Middlewares: []string{"default-ingress-with-no-annotation-rule-0-path-0-redirect-scheme"}, + Service: "noop@internal", + }, + }, + Middlewares: map[string]*dynamic.Middleware{ + "default-ingress-with-no-annotation-rule-0-path-0-redirect-scheme": { + RedirectScheme: &dynamic.RedirectScheme{ + Scheme: "https", + ForcePermanentRedirect: true, + }, + }, + }, + Services: map[string]*dynamic.Service{ + "default-ingress-with-no-annotation-whoami-80": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://10.10.0.1:80", + }, + { + URL: "http://10.10.0.2:80", + }, + }, + Strategy: "wrr", + PassHostHeader: ptr.To(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: dynamic.DefaultFlushInterval, + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{ + Certificates: []*tls.CertAndStores{ + { + Certificate: tls.Certificate{ + CertFile: "-----BEGIN CERTIFICATE-----", + KeyFile: "-----BEGIN CERTIFICATE-----", + }, + }, + }, + }, + }, + }, { desc: "Basic Auth", paths: []string{ @@ -64,7 +134,7 @@ func TestLoadIngresses(t *testing.T) { Rule: "Host(`whoami.localhost`) && Path(`/basicauth`)", RuleSyntax: "default", Middlewares: []string{"default-ingress-with-basicauth-rule-0-path-0-basic-auth"}, - Service: "default-whoami-80", + Service: "default-ingress-with-basicauth-whoami-80", }, }, Middlewares: map[string]*dynamic.Middleware{ @@ -78,7 +148,7 @@ func TestLoadIngresses(t *testing.T) { }, }, Services: map[string]*dynamic.Service{ - "default-whoami-80": { + "default-ingress-with-basicauth-whoami-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -119,7 +189,7 @@ func TestLoadIngresses(t *testing.T) { Rule: "Host(`whoami.localhost`) && Path(`/forwardauth`)", RuleSyntax: "default", Middlewares: []string{"default-ingress-with-forwardauth-rule-0-path-0-forward-auth"}, - Service: "default-whoami-80", + Service: "default-ingress-with-forwardauth-whoami-80", }, }, Middlewares: map[string]*dynamic.Middleware{ @@ -131,7 +201,7 @@ func TestLoadIngresses(t *testing.T) { }, }, Services: map[string]*dynamic.Service{ - "default-whoami-80": { + "default-ingress-with-forwardauth-whoami-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -173,53 +243,84 @@ func TestLoadIngresses(t *testing.T) { Rule: "Host(`sslredirect.localhost`) && Path(`/`)", RuleSyntax: "default", TLS: &dynamic.RouterTLSConfig{}, - Service: "default-whoami-80", + Service: "default-ingress-with-ssl-redirect-whoami-80", }, - "default-ingress-with-ssl-redirect-rule-0-path-0-redirect": { + "default-ingress-with-ssl-redirect-rule-0-path-0-http": { + EntryPoints: []string{"web"}, Rule: "Host(`sslredirect.localhost`) && Path(`/`)", RuleSyntax: "default", Middlewares: []string{"default-ingress-with-ssl-redirect-rule-0-path-0-redirect-scheme"}, Service: "noop@internal", }, "default-ingress-without-ssl-redirect-rule-0-path-0-http": { - Rule: "Host(`withoutsslredirect.localhost`) && Path(`/`)", - RuleSyntax: "default", - Service: "default-whoami-80", + EntryPoints: []string{"web"}, + Rule: "Host(`withoutsslredirect.localhost`) && Path(`/`)", + RuleSyntax: "default", + Service: "default-ingress-without-ssl-redirect-whoami-80", }, "default-ingress-without-ssl-redirect-rule-0-path-0": { Rule: "Host(`withoutsslredirect.localhost`) && Path(`/`)", RuleSyntax: "default", TLS: &dynamic.RouterTLSConfig{}, - Service: "default-whoami-80", + Service: "default-ingress-without-ssl-redirect-whoami-80", }, "default-ingress-with-force-ssl-redirect-rule-0-path-0": { - Rule: "Host(`forcesslredirect.localhost`) && Path(`/`)", - RuleSyntax: "default", - Service: "default-whoami-80", - }, - "default-ingress-with-force-ssl-redirect-rule-0-path-0-redirect": { Rule: "Host(`forcesslredirect.localhost`) && Path(`/`)", RuleSyntax: "default", Middlewares: []string{"default-ingress-with-force-ssl-redirect-rule-0-path-0-redirect-scheme"}, - Service: "noop@internal", + Service: "default-ingress-with-force-ssl-redirect-whoami-80", }, }, Middlewares: map[string]*dynamic.Middleware{ "default-ingress-with-ssl-redirect-rule-0-path-0-redirect-scheme": { RedirectScheme: &dynamic.RedirectScheme{ - Scheme: "https", - Permanent: true, + Scheme: "https", + ForcePermanentRedirect: true, }, }, "default-ingress-with-force-ssl-redirect-rule-0-path-0-redirect-scheme": { RedirectScheme: &dynamic.RedirectScheme{ - Scheme: "https", - Permanent: true, + Scheme: "https", + ForcePermanentRedirect: true, }, }, }, Services: map[string]*dynamic.Service{ - "default-whoami-80": { + "default-ingress-with-ssl-redirect-whoami-80": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://10.10.0.1:80", + }, + { + URL: "http://10.10.0.2:80", + }, + }, + Strategy: "wrr", + PassHostHeader: ptr.To(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: dynamic.DefaultFlushInterval, + }, + }, + }, + "default-ingress-without-ssl-redirect-whoami-80": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://10.10.0.1:80", + }, + { + URL: "http://10.10.0.2:80", + }, + }, + Strategy: "wrr", + PassHostHeader: ptr.To(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: dynamic.DefaultFlushInterval, + }, + }, + }, + "default-ingress-with-force-ssl-redirect-whoami-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -313,12 +414,12 @@ func TestLoadIngresses(t *testing.T) { "default-ingress-with-sticky-rule-0-path-0": { Rule: "Host(`sticky.localhost`) && Path(`/`)", RuleSyntax: "default", - Service: "default-whoami-80", + Service: "default-ingress-with-sticky-whoami-80", }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-whoami-80": { + "default-ingress-with-sticky-whoami-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -370,12 +471,12 @@ func TestLoadIngresses(t *testing.T) { "default-ingress-with-proxy-ssl-rule-0-path-0": { Rule: "Host(`proxy-ssl.localhost`) && Path(`/`)", RuleSyntax: "default", - Service: "default-whoami-tls-443", + Service: "default-ingress-with-proxy-ssl-whoami-tls-443", }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-whoami-tls-443": { + "default-ingress-with-proxy-ssl-whoami-tls-443": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -397,7 +498,7 @@ func TestLoadIngresses(t *testing.T) { ServersTransports: map[string]*dynamic.ServersTransport{ "default-ingress-with-proxy-ssl": { ServerName: "whoami.localhost", - InsecureSkipVerify: true, + InsecureSkipVerify: false, RootCAs: []types.FileOrContent{"-----BEGIN CERTIFICATE-----"}, }, }, @@ -423,7 +524,7 @@ func TestLoadIngresses(t *testing.T) { Rule: "Host(`cors.localhost`) && Path(`/`)", RuleSyntax: "default", Middlewares: []string{"default-ingress-with-cors-rule-0-path-0-cors"}, - Service: "default-whoami-80", + Service: "default-ingress-with-cors-whoami-80", }, }, Middlewares: map[string]*dynamic.Middleware{ @@ -439,7 +540,7 @@ func TestLoadIngresses(t *testing.T) { }, }, Services: map[string]*dynamic.Service{ - "default-whoami-80": { + "default-ingress-with-cors-whoami-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -479,12 +580,12 @@ func TestLoadIngresses(t *testing.T) { "default-ingress-with-service-upstream-rule-0-path-0": { Rule: "Host(`service-upstream.localhost`) && Path(`/`)", RuleSyntax: "default", - Service: "default-whoami-80", + Service: "default-ingress-with-service-upstream-whoami-80", }, }, Middlewares: map[string]*dynamic.Middleware{}, Services: map[string]*dynamic.Service{ - "default-whoami-80": { + "default-ingress-with-service-upstream-whoami-80": { LoadBalancer: &dynamic.ServersLoadBalancer{ Servers: []dynamic.Server{ { @@ -504,6 +605,51 @@ func TestLoadIngresses(t *testing.T) { TLS: &dynamic.TLSConfiguration{}, }, }, + { + desc: "Use Regex", + paths: []string{ + "services.yml", + "ingressclasses.yml", + "ingresses/10-ingress-with-use-regex.yml", + }, + expected: &dynamic.Configuration{ + TCP: &dynamic.TCPConfiguration{ + Routers: map[string]*dynamic.TCPRouter{}, + Services: map[string]*dynamic.TCPService{}, + }, + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "default-ingress-with-use-regex-rule-0-path-0": { + Rule: "Host(`use-regex.localhost`) && PathRegexp(`^/test(.*)`)", + RuleSyntax: "default", + Service: "default-ingress-with-use-regex-whoami-80", + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{ + "default-ingress-with-use-regex-whoami-80": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{ + { + URL: "http://10.10.0.1:80", + }, + { + URL: "http://10.10.0.2:80", + }, + }, + Strategy: "wrr", + PassHostHeader: ptr.To(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: dynamic.DefaultFlushInterval, + }, + }, + }, + }, + ServersTransports: map[string]*dynamic.ServersTransport{}, + }, + TLS: &dynamic.TLSConfiguration{}, + }, + }, { desc: "Default Backend", defaultBackendServiceName: "whoami", @@ -579,6 +725,7 @@ func TestLoadIngresses(t *testing.T) { k8sClient: client, defaultBackendServiceName: test.defaultBackendServiceName, defaultBackendServiceNamespace: test.defaultBackendServiceNamespace, + NonTLSEntryPoints: []string{"web"}, } p.SetDefaults() diff --git a/pkg/provider/kubernetes/ingress/client_test.go b/pkg/provider/kubernetes/ingress/client_test.go index 9014265bd..38b7e475b 100644 --- a/pkg/provider/kubernetes/ingress/client_test.go +++ b/pkg/provider/kubernetes/ingress/client_test.go @@ -149,7 +149,7 @@ func TestClientIgnoresHelmOwnedSecrets(t *testing.T) { }, } - kubeClient := kubefake.NewSimpleClientset(helmSecret, secret) + kubeClient := kubefake.NewClientset(helmSecret, secret) discovery, _ := kubeClient.Discovery().(*discoveryfake.FakeDiscovery) discovery.FakedServerVersion = &kversion.Info{ @@ -224,7 +224,7 @@ func TestClientIgnoresEmptyEndpointSliceUpdates(t *testing.T) { }}, } - kubeClient := kubefake.NewSimpleClientset(emptyEndpointSlice, filledEndpointSlice) + kubeClient := kubefake.NewClientset(emptyEndpointSlice, filledEndpointSlice) discovery, _ := kubeClient.Discovery().(*discoveryfake.FakeDiscovery) discovery.FakedServerVersion = &kversion.Info{ diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-defaultbackend-with-resource.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-defaultbackend-with-resource.yml new file mode 100644 index 000000000..2d21ccab4 --- /dev/null +++ b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-defaultbackend-with-resource.yml @@ -0,0 +1,12 @@ +kind: Ingress +apiVersion: networking.k8s.io/v1 +metadata: + name: defaultbackend + namespace: testing + +spec: + defaultBackend: + resource: + apiGroup: example.com + kind: SomeBackend + name: foo diff --git a/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-empty-defaultbackend.yml b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-empty-defaultbackend.yml new file mode 100644 index 000000000..9eff4acd5 --- /dev/null +++ b/pkg/provider/kubernetes/ingress/fixtures/Ingress-with-empty-defaultbackend.yml @@ -0,0 +1,8 @@ +kind: Ingress +apiVersion: networking.k8s.io/v1 +metadata: + name: defaultbackend + namespace: testing + +spec: + defaultBackend: {} diff --git a/pkg/provider/kubernetes/ingress/fixtures/Published-Service-ExternalName.yml b/pkg/provider/kubernetes/ingress/fixtures/Published-Service-ExternalName.yml new file mode 100644 index 000000000..150c3b7c8 --- /dev/null +++ b/pkg/provider/kubernetes/ingress/fixtures/Published-Service-ExternalName.yml @@ -0,0 +1,83 @@ +--- +kind: Node +apiVersion: v1 +metadata: + name: node1 + +status: + addresses: + - type: ExternalIP + address: 1.2.3.4 + +--- +kind: Node +apiVersion: v1 +metadata: + name: node2 + +status: + addresses: + - type: ExternalIP + address: 5.6.7.8 + +--- +kind: Service +apiVersion: v1 +metadata: + name: published-service + namespace: default + +spec: + type: ExternalName + externalName: example.com + +--- +kind: Ingress +apiVersion: networking.k8s.io/v1 +metadata: + name: foo + namespace: default + +spec: + rules: + - host: "*.foo.com" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: service1 + port: + number: 80 + +--- +kind: Service +apiVersion: v1 +metadata: + name: service1 + namespace: default + +spec: + ports: + - port: 80 + + clusterIP: 10.0.0.1 + +--- +kind: EndpointSlice +apiVersion: discovery.k8s.io/v1 +metadata: + name: service1-abc + labels: + kubernetes.io/service-name: service1 + +addressType: IPv4 +ports: + - port: 8080 + name: "" +endpoints: + - addresses: + - 10.10.0.1 + conditions: + ready: true diff --git a/pkg/provider/kubernetes/ingress/kubernetes.go b/pkg/provider/kubernetes/ingress/kubernetes.go index 7a1919b3e..55d2ec6db 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes.go +++ b/pkg/provider/kubernetes/ingress/kubernetes.go @@ -269,6 +269,17 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl continue } + if ingress.Spec.DefaultBackend.Resource != nil { + // https://kubernetes.io/docs/concepts/services-networking/ingress/#resource-backend + logger.Error().Msg("Resource is not supported for default backend") + continue + } + + if ingress.Spec.DefaultBackend.Service == nil { + logger.Error().Msg("Default backend is missing service definition") + continue + } + service, err := p.loadService(client, ingress.Namespace, *ingress.Spec.DefaultBackend) if err != nil { logger.Error(). @@ -484,6 +495,11 @@ func (p *Provider) updateIngressStatus(ing *netv1.Ingress, k8sClient Client) err } } + case corev1.ServiceTypeExternalName: + ingressStatus = []netv1.IngressLoadBalancerIngress{{ + Hostname: service.Spec.ExternalName, + }} + default: return fmt.Errorf("unsupported service type: %s", service.Spec.Type) } @@ -646,7 +662,10 @@ func (p *Provider) loadService(client Client, namespace string, backend netv1.In protocol := getProtocol(portSpec, portName, svcConfig) for _, endpoint := range endpointSlice.Endpoints { - if !k8s.EndpointServing(endpoint) { + // The Serving condition allows to track if the Pod can receive traffic. + // It is set to true when the Pod is Ready or Terminating. + // From the go documentation, a nil value should be interpreted as "true". + if !ptr.Deref(endpoint.Conditions.Serving, true) { continue } @@ -658,7 +677,7 @@ func (p *Provider) loadService(client Client, namespace string, backend netv1.In addresses[address] = struct{}{} svc.LoadBalancer.Servers = append(svc.LoadBalancer.Servers, dynamic.Server{ URL: fmt.Sprintf("%s://%s", protocol, net.JoinHostPort(address, strconv.Itoa(int(port)))), - Fenced: ptr.Deref(endpoint.Conditions.Terminating, false) && ptr.Deref(endpoint.Conditions.Serving, false), + Fenced: ptr.Deref(endpoint.Conditions.Terminating, false), }) } } diff --git a/pkg/provider/kubernetes/ingress/kubernetes_test.go b/pkg/provider/kubernetes/ingress/kubernetes_test.go index d965e68a2..c67e84e57 100644 --- a/pkg/provider/kubernetes/ingress/kubernetes_test.go +++ b/pkg/provider/kubernetes/ingress/kubernetes_test.go @@ -550,6 +550,26 @@ func TestLoadConfigurationFromIngresses(t *testing.T) { }, }, }, + { + desc: "Ingress with defaultbackend with resource", + expected: &dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Middlewares: map[string]*dynamic.Middleware{}, + Routers: map[string]*dynamic.Router{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, + { + desc: "Ingress with empty defaultbackend", + expected: &dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Middlewares: map[string]*dynamic.Middleware{}, + Routers: map[string]*dynamic.Router{}, + Services: map[string]*dynamic.Service{}, + }, + }, + }, { desc: "Ingress with one service without endpoint", expected: &dynamic.Configuration{ @@ -2298,6 +2318,14 @@ func TestIngressEndpointPublishedService(t *testing.T) { }, }, }, + { + desc: "Published Service ExternalName", + expected: []netv1.IngressLoadBalancerIngress{ + { + Hostname: "example.com", + }, + }, + }, } for _, test := range testCases { diff --git a/pkg/provider/kubernetes/k8s/endpoint.go b/pkg/provider/kubernetes/k8s/endpoint.go deleted file mode 100644 index 415613de3..000000000 --- a/pkg/provider/kubernetes/k8s/endpoint.go +++ /dev/null @@ -1,11 +0,0 @@ -package k8s - -import ( - v1 "k8s.io/api/discovery/v1" - "k8s.io/utils/ptr" -) - -// EndpointServing returns true if the endpoint is still serving the service. -func EndpointServing(endpoint v1.Endpoint) bool { - return ptr.Deref(endpoint.Conditions.Ready, false) || ptr.Deref(endpoint.Conditions.Serving, false) -} diff --git a/pkg/provider/kubernetes/k8s/endpoint_test.go b/pkg/provider/kubernetes/k8s/endpoint_test.go deleted file mode 100644 index 9eba33f3a..000000000 --- a/pkg/provider/kubernetes/k8s/endpoint_test.go +++ /dev/null @@ -1,75 +0,0 @@ -package k8s - -import ( - "testing" - - "github.com/stretchr/testify/assert" - v1 "k8s.io/api/discovery/v1" -) - -func TestEndpointServing(t *testing.T) { - tests := []struct { - name string - endpoint v1.Endpoint - want bool - }{ - { - name: "no status", - endpoint: v1.Endpoint{ - Conditions: v1.EndpointConditions{ - Ready: nil, - Serving: nil, - }, - }, - want: false, - }, - { - name: "ready", - endpoint: v1.Endpoint{ - Conditions: v1.EndpointConditions{ - Ready: pointer(true), - Serving: nil, - }, - }, - want: true, - }, - { - name: "not ready", - endpoint: v1.Endpoint{ - Conditions: v1.EndpointConditions{ - Ready: pointer(false), - Serving: nil, - }, - }, - want: false, - }, - { - name: "not ready and serving", - endpoint: v1.Endpoint{ - Conditions: v1.EndpointConditions{ - Ready: pointer(false), - Serving: pointer(true), - }, - }, - want: true, - }, - { - name: "not ready and not serving", - endpoint: v1.Endpoint{ - Conditions: v1.EndpointConditions{ - Ready: pointer(false), - Serving: pointer(false), - }, - }, - want: false, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := EndpointServing(test.endpoint) - assert.Equal(t, test.want, got) - }) - } -} - -func pointer[T any](v T) *T { return &v } diff --git a/pkg/provider/kubernetes/knative/client.go b/pkg/provider/kubernetes/knative/client.go new file mode 100644 index 000000000..bfb87c332 --- /dev/null +++ b/pkg/provider/kubernetes/knative/client.go @@ -0,0 +1,232 @@ +package knative + +import ( + "context" + "errors" + "fmt" + "os" + "time" + + "github.com/rs/zerolog/log" + "github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + kinformers "k8s.io/client-go/informers" + kclientset "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + knativenetworkingv1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" + knativenetworkingclientset "knative.dev/networking/pkg/client/clientset/versioned" + knativenetworkinginformers "knative.dev/networking/pkg/client/informers/externalversions" +) + +const resyncPeriod = 10 * time.Minute + +type clientWrapper struct { + csKnativeNetworking knativenetworkingclientset.Interface + csKube kclientset.Interface + + factoriesKnativeNetworking map[string]knativenetworkinginformers.SharedInformerFactory + factoriesKube map[string]kinformers.SharedInformerFactory + + labelSelector string + + isNamespaceAll bool + watchedNamespaces []string +} + +func createClientFromConfig(c *rest.Config) (*clientWrapper, error) { + csKnativeNetworking, err := knativenetworkingclientset.NewForConfig(c) + if err != nil { + return nil, err + } + + csKube, err := kclientset.NewForConfig(c) + if err != nil { + return nil, err + } + + return newClientImpl(csKnativeNetworking, csKube), nil +} + +func newClientImpl(csKnativeNetworking knativenetworkingclientset.Interface, csKube kclientset.Interface) *clientWrapper { + return &clientWrapper{ + csKnativeNetworking: csKnativeNetworking, + csKube: csKube, + factoriesKnativeNetworking: make(map[string]knativenetworkinginformers.SharedInformerFactory), + factoriesKube: make(map[string]kinformers.SharedInformerFactory), + } +} + +// newInClusterClient returns a new Provider client that is expected to run +// inside the cluster. +func newInClusterClient(endpoint string) (*clientWrapper, error) { + config, err := rest.InClusterConfig() + if err != nil { + return nil, fmt.Errorf("creating in-cluster configuration: %w", err) + } + + if endpoint != "" { + config.Host = endpoint + } + + return createClientFromConfig(config) +} + +func newExternalClusterClientFromFile(file string) (*clientWrapper, error) { + configFromFlags, err := clientcmd.BuildConfigFromFlags("", file) + if err != nil { + return nil, err + } + return createClientFromConfig(configFromFlags) +} + +// newExternalClusterClient returns a new Provider client that may run outside +// of the cluster. +// The endpoint parameter must not be empty. +func newExternalClusterClient(endpoint, token, caFilePath string) (*clientWrapper, error) { + if endpoint == "" { + return nil, errors.New("endpoint missing for external cluster client") + } + + config := &rest.Config{ + Host: endpoint, + BearerToken: token, + } + + if caFilePath != "" { + caData, err := os.ReadFile(caFilePath) + if err != nil { + return nil, fmt.Errorf("reading CA file %s: %w", caFilePath, err) + } + + config.TLSClientConfig = rest.TLSClientConfig{CAData: caData} + } + + return createClientFromConfig(config) +} + +// WatchAll starts namespace-specific controllers for all relevant kinds. +func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (<-chan interface{}, error) { + eventCh := make(chan interface{}, 1) + eventHandler := &k8s.ResourceEventHandler{Ev: eventCh} + + if len(namespaces) == 0 { + namespaces = []string{metav1.NamespaceAll} + c.isNamespaceAll = true + } + c.watchedNamespaces = namespaces + + for _, ns := range namespaces { + factory := knativenetworkinginformers.NewSharedInformerFactoryWithOptions(c.csKnativeNetworking, resyncPeriod, knativenetworkinginformers.WithNamespace(ns), knativenetworkinginformers.WithTweakListOptions(func(opts *metav1.ListOptions) { + opts.LabelSelector = c.labelSelector + })) + _, err := factory.Networking().V1alpha1().Ingresses().Informer().AddEventHandler(eventHandler) + if err != nil { + return nil, err + } + + factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns)) + _, err = factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler) + if err != nil { + return nil, err + } + _, err = factoryKube.Core().V1().Secrets().Informer().AddEventHandler(eventHandler) + if err != nil { + return nil, err + } + + c.factoriesKube[ns] = factoryKube + c.factoriesKnativeNetworking[ns] = factory + } + + for _, ns := range namespaces { + c.factoriesKnativeNetworking[ns].Start(stopCh) + c.factoriesKube[ns].Start(stopCh) + } + + for _, ns := range namespaces { + for t, ok := range c.factoriesKnativeNetworking[ns].WaitForCacheSync(stopCh) { + if !ok { + return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns) + } + } + for t, ok := range c.factoriesKube[ns].WaitForCacheSync(stopCh) { + if !ok { + return nil, fmt.Errorf("timed out waiting for controller caches to sync %s in namespace %q", t.String(), ns) + } + } + } + + return eventCh, nil +} + +func (c *clientWrapper) ListIngresses() []*knativenetworkingv1alpha1.Ingress { + var result []*knativenetworkingv1alpha1.Ingress + + for ns, factory := range c.factoriesKnativeNetworking { + ings, err := factory.Networking().V1alpha1().Ingresses().Lister().List(labels.Everything()) // todo: label selector + if err != nil { + log.Error().Msgf("Failed to list ingresses in namespace %s: %s", ns, err) + } + result = append(result, ings...) + } + + return result +} + +func (c *clientWrapper) UpdateIngressStatus(ingress *knativenetworkingv1alpha1.Ingress) error { + _, err := c.csKnativeNetworking.NetworkingV1alpha1().Ingresses(ingress.Namespace).UpdateStatus(context.TODO(), ingress, metav1.UpdateOptions{}) + if err != nil { + return fmt.Errorf("updating knative ingress status %s/%s: %w", ingress.Namespace, ingress.Name, err) + } + + log.Info().Msgf("Updated status on knative ingress %s/%s", ingress.Namespace, ingress.Name) + return nil +} + +// GetService returns the named service from the given namespace. +func (c *clientWrapper) GetService(namespace, name string) (*corev1.Service, error) { + if !c.isWatchedNamespace(namespace) { + return nil, fmt.Errorf("getting service %s/%s: namespace is not within watched namespaces", namespace, name) + } + + return c.factoriesKube[c.lookupNamespace(namespace)].Core().V1().Services().Lister().Services(namespace).Get(name) +} + +// GetSecret returns the named secret from the given namespace. +func (c *clientWrapper) GetSecret(namespace, name string) (*corev1.Secret, error) { + if !c.isWatchedNamespace(namespace) { + return nil, fmt.Errorf("getting secret %s/%s: namespace is not within watched namespaces", namespace, name) + } + + return c.factoriesKube[c.lookupNamespace(namespace)].Core().V1().Secrets().Lister().Secrets(namespace).Get(name) +} + +// isWatchedNamespace checks to ensure that the namespace is being watched before we request +// it to ensure we don't panic by requesting an out-of-watch object. +func (c *clientWrapper) isWatchedNamespace(ns string) bool { + if c.isNamespaceAll { + return true + } + for _, watchedNamespace := range c.watchedNamespaces { + if watchedNamespace == ns { + return true + } + } + return false +} + +// lookupNamespace returns the lookup namespace key for the given namespace. +// When listening on all namespaces, it returns the client-go identifier ("") +// for all-namespaces. Otherwise, it returns the given namespace. +// The distinction is necessary because we index all informers on the special +// identifier iff all-namespaces are requested but receive specific namespace +// identifiers from the Kubernetes API, so we have to bridge this gap. +func (c *clientWrapper) lookupNamespace(ns string) string { + if c.isNamespaceAll { + return metav1.NamespaceAll + } + return ns +} diff --git a/pkg/provider/kubernetes/knative/fixtures/cluster_local.yaml b/pkg/provider/kubernetes/knative/fixtures/cluster_local.yaml new file mode 100644 index 000000000..0d9c0fcea --- /dev/null +++ b/pkg/provider/kubernetes/knative/fixtures/cluster_local.yaml @@ -0,0 +1,33 @@ +--- +apiVersion: networking.internal.knative.dev/v1alpha1 +kind: Ingress +metadata: + annotations: + networking.knative.dev/ingress.class: traefik.ingress.networking.knative.dev + name: helloworld-go + namespace: default +spec: + httpOption: Enabled + rules: + - hosts: + - helloworld-go.default + - helloworld-go.default.svc + - helloworld-go.default.svc.cluster.local + http: + paths: + - splits: + - appendHeaders: + Knative-Serving-Namespace: default + Knative-Serving-Revision: helloworld-go-00001 + percent: 50 + serviceName: helloworld-go-00001 + serviceNamespace: default + servicePort: 80 + - appendHeaders: + Knative-Serving-Namespace: default + Knative-Serving-Revision: helloworld-go-00002 + percent: 50 + serviceName: helloworld-go-00002 + serviceNamespace: default + servicePort: 80 + visibility: ClusterLocal diff --git a/pkg/provider/kubernetes/knative/fixtures/external_ip.yaml b/pkg/provider/kubernetes/knative/fixtures/external_ip.yaml new file mode 100644 index 000000000..0ddd4fe06 --- /dev/null +++ b/pkg/provider/kubernetes/knative/fixtures/external_ip.yaml @@ -0,0 +1,33 @@ +--- +apiVersion: networking.internal.knative.dev/v1alpha1 +kind: Ingress +metadata: + annotations: + networking.knative.dev/ingress.class: traefik.ingress.networking.knative.dev + name: helloworld-go + namespace: default +spec: + httpOption: Enabled + rules: + - hosts: + - helloworld-go.default + - helloworld-go.default.svc + - helloworld-go.default.svc.cluster.local + http: + paths: + - splits: + - appendHeaders: + Knative-Serving-Namespace: default + Knative-Serving-Revision: helloworld-go-00001 + percent: 50 + serviceName: helloworld-go-00001 + serviceNamespace: default + servicePort: 80 + - appendHeaders: + Knative-Serving-Namespace: default + Knative-Serving-Revision: helloworld-go-00002 + percent: 50 + serviceName: helloworld-go-00002 + serviceNamespace: default + servicePort: 80 + visibility: ExternalIP diff --git a/pkg/provider/kubernetes/knative/fixtures/services.yaml b/pkg/provider/kubernetes/knative/fixtures/services.yaml new file mode 100644 index 000000000..d2d69ce83 --- /dev/null +++ b/pkg/provider/kubernetes/knative/fixtures/services.yaml @@ -0,0 +1,39 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: helloworld-go-00001 + namespace: default +spec: + clusterIP: 10.43.38.208 + clusterIPs: + - 10.43.38.208 + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 8012 + - name: https + port: 443 + protocol: TCP + targetPort: 8112 + +--- +apiVersion: v1 +kind: Service +metadata: + name: helloworld-go-00002 + namespace: default +spec: + clusterIP: 10.43.44.18 + clusterIPs: + - 10.43.44.18 + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 8012 + - name: https + port: 443 + protocol: TCP + targetPort: 8112 diff --git a/pkg/provider/kubernetes/knative/fixtures/tls.yaml b/pkg/provider/kubernetes/knative/fixtures/tls.yaml new file mode 100644 index 000000000..3d85ad696 --- /dev/null +++ b/pkg/provider/kubernetes/knative/fixtures/tls.yaml @@ -0,0 +1,38 @@ +--- +apiVersion: networking.internal.knative.dev/v1alpha1 +kind: Ingress +metadata: + annotations: + networking.knative.dev/ingress.class: traefik.ingress.networking.knative.dev + name: helloworld-go + namespace: default +spec: + httpOption: Enabled + tls: + - hosts: + - helloworld-go.default.svc.cluster.local + secretName: secretName + secretNamespace: secretNamespace + rules: + - hosts: + - helloworld-go.default + - helloworld-go.default.svc + - helloworld-go.default.svc.cluster.local + http: + paths: + - splits: + - appendHeaders: + Knative-Serving-Namespace: default + Knative-Serving-Revision: helloworld-go-00001 + percent: 50 + serviceName: helloworld-go-00001 + serviceNamespace: default + servicePort: 80 + - appendHeaders: + Knative-Serving-Namespace: default + Knative-Serving-Revision: helloworld-go-00002 + percent: 50 + serviceName: helloworld-go-00002 + serviceNamespace: default + servicePort: 80 + visibility: ExternalIP diff --git a/pkg/provider/kubernetes/knative/fixtures/wrong_ingress_class.yaml b/pkg/provider/kubernetes/knative/fixtures/wrong_ingress_class.yaml new file mode 100644 index 000000000..aaaff85a0 --- /dev/null +++ b/pkg/provider/kubernetes/knative/fixtures/wrong_ingress_class.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: networking.internal.knative.dev/v1alpha1 +kind: Ingress +metadata: + annotations: + networking.knative.dev/ingress.class: foo.ingress.networking.knative.dev + name: helloworld-go + namespace: default diff --git a/pkg/provider/kubernetes/knative/kubernetes.go b/pkg/provider/kubernetes/knative/kubernetes.go new file mode 100644 index 000000000..ae9c636d3 --- /dev/null +++ b/pkg/provider/kubernetes/knative/kubernetes.go @@ -0,0 +1,531 @@ +package knative + +import ( + "context" + "errors" + "fmt" + "maps" + "net" + "os" + "slices" + "strconv" + "strings" + "time" + + "github.com/cenkalti/backoff/v4" + "github.com/mitchellh/hashstructure" + "github.com/rs/zerolog/log" + ptypes "github.com/traefik/paerser/types" + "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/job" + "github.com/traefik/traefik/v3/pkg/observability/logs" + "github.com/traefik/traefik/v3/pkg/safe" + "github.com/traefik/traefik/v3/pkg/tls" + "github.com/traefik/traefik/v3/pkg/types" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/utils/ptr" + knativenetworking "knative.dev/networking/pkg/apis/networking" + knativenetworkingv1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" + "knative.dev/pkg/network" +) + +const ( + providerName = "knative" + traefikIngressClassName = "traefik.ingress.networking.knative.dev" +) + +// ServiceRef holds a Kubernetes service reference. +type ServiceRef struct { + Name string `description:"Name of the Kubernetes service." json:"desc,omitempty" toml:"desc,omitempty" yaml:"desc,omitempty"` + Namespace string `description:"Namespace of the Kubernetes service." json:"namespace,omitempty" toml:"namespace,omitempty" yaml:"namespace,omitempty"` +} + +// Provider holds configurations of the provider. +type Provider struct { + Endpoint string `description:"Kubernetes server endpoint (required for external cluster client)." json:"endpoint,omitempty" toml:"endpoint,omitempty" yaml:"endpoint,omitempty"` + Token string `description:"Kubernetes bearer token (not needed for in-cluster client)." json:"token,omitempty" toml:"token,omitempty" yaml:"token,omitempty"` + CertAuthFilePath string `description:"Kubernetes certificate authority file path (not needed for in-cluster client)." json:"certAuthFilePath,omitempty" toml:"certAuthFilePath,omitempty" yaml:"certAuthFilePath,omitempty"` + Namespaces []string `description:"Kubernetes namespaces." json:"namespaces,omitempty" toml:"namespaces,omitempty" yaml:"namespaces,omitempty" export:"true"` + LabelSelector string `description:"Kubernetes label selector to use." json:"labelSelector,omitempty" toml:"labelSelector,omitempty" yaml:"labelSelector,omitempty" export:"true"` + PublicEntrypoints []string `description:"Entrypoint names used to expose the Ingress publicly. If empty an Ingress is exposed on all entrypoints." json:"publicEntrypoints,omitempty" toml:"publicEntrypoints,omitempty" yaml:"publicEntrypoints,omitempty" export:"true"` + PublicService ServiceRef `description:"Kubernetes service used to expose the networking controller publicly." json:"publicService,omitempty" toml:"publicService,omitempty" yaml:"publicService,omitempty" export:"true"` + PrivateEntrypoints []string `description:"Entrypoint names used to expose the Ingress privately. If empty local Ingresses are skipped." json:"privateEntrypoints,omitempty" toml:"privateEntrypoints,omitempty" yaml:"privateEntrypoints,omitempty" export:"true"` + PrivateService ServiceRef `description:"Kubernetes service used to expose the networking controller privately." json:"privateService,omitempty" toml:"privateService,omitempty" yaml:"privateService,omitempty" export:"true"` + ThrottleDuration ptypes.Duration `description:"Ingress refresh throttle duration" json:"throttleDuration,omitempty" toml:"throttleDuration,omitempty" yaml:"throttleDuration,omitempty"` + + client *clientWrapper + lastConfiguration safe.Safe +} + +// Init the provider. +func (p *Provider) Init() error { + logger := log.With().Str(logs.ProviderName, providerName).Logger() + + // Initializes Kubernetes client. + var err error + p.client, err = p.newK8sClient(logger.WithContext(context.Background())) + if err != nil { + return fmt.Errorf("creating kubernetes client: %w", err) + } + + return nil +} + +// Provide allows the knative provider to provide configurations to traefik using the given configuration channel. +func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error { + logger := log.With().Str(logs.ProviderName, providerName).Logger() + ctxLog := logger.WithContext(context.Background()) + + pool.GoCtx(func(ctxPool context.Context) { + operation := func() error { + eventsChan, err := p.client.WatchAll(p.Namespaces, ctxPool.Done()) + if err != nil { + logger.Error().Msgf("Error watching kubernetes events: %v", err) + timer := time.NewTimer(1 * time.Second) + select { + case <-timer.C: + return err + case <-ctxPool.Done(): + return nil + } + } + + throttleDuration := time.Duration(p.ThrottleDuration) + throttledChan := throttleEvents(ctxLog, throttleDuration, pool, eventsChan) + if throttledChan != nil { + eventsChan = throttledChan + } + + for { + select { + case <-ctxPool.Done(): + return nil + case event := <-eventsChan: + // Note that event is the *first* event that came in during this throttling interval -- if we're hitting our throttle, we may have dropped events. + // This is fine, because we don't treat different event types differently. + // But if we do in the future, we'll need to track more information about the dropped events. + conf, ingressStatuses := p.loadConfiguration(ctxLog) + + confHash, err := hashstructure.Hash(conf, nil) + switch { + case err != nil: + logger.Error().Msg("Unable to hash the configuration") + case p.lastConfiguration.Get() == confHash: + logger.Debug().Msgf("Skipping Kubernetes event kind %T", event) + default: + p.lastConfiguration.Set(confHash) + configurationChan <- dynamic.Message{ + ProviderName: providerName, + Configuration: conf, + } + } + + // If we're throttling, + // we sleep here for the throttle duration to enforce that we don't refresh faster than our throttle. + // time.Sleep returns immediately if p.ThrottleDuration is 0 (no throttle). + time.Sleep(throttleDuration) + + // Updating the ingress status after the throttleDuration allows to wait to make sure that the dynamic conf is updated before updating the status. + // This is needed for the conformance tests to pass, for example. + for _, ingress := range ingressStatuses { + if err := p.updateKnativeIngressStatus(ctxLog, ingress); err != nil { + logger.Error().Err(err).Msgf("Error updating status for Ingress %s/%s", ingress.Namespace, ingress.Name) + } + } + } + } + } + + notify := func(err error, time time.Duration) { + logger.Error().Msgf("Provider connection error: %v; retrying in %s", err, time) + } + err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxPool), notify) + if err != nil { + logger.Error().Msgf("Cannot connect to Provider: %v", err) + } + }) + + return nil +} + +func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) { + logger := log.Ctx(ctx).With().Logger() + + _, err := labels.Parse(p.LabelSelector) + if err != nil { + return nil, fmt.Errorf("parsing label selector: %q", p.LabelSelector) + } + logger.Info().Msgf("Label selector is: %q", p.LabelSelector) + + withEndpoint := "" + if p.Endpoint != "" { + withEndpoint = fmt.Sprintf(" with endpoint %s", p.Endpoint) + } + + var client *clientWrapper + switch { + case os.Getenv("KUBERNETES_SERVICE_HOST") != "" && os.Getenv("KUBERNETES_SERVICE_PORT") != "": + logger.Info().Msgf("Creating in-cluster Provider client%s", withEndpoint) + client, err = newInClusterClient(p.Endpoint) + case os.Getenv("KUBECONFIG") != "": + logger.Info().Msgf("Creating cluster-external Provider client from KUBECONFIG %s", os.Getenv("KUBECONFIG")) + client, err = newExternalClusterClientFromFile(os.Getenv("KUBECONFIG")) + default: + logger.Info().Msgf("Creating cluster-external Provider client%s", withEndpoint) + client, err = newExternalClusterClient(p.Endpoint, p.Token, p.CertAuthFilePath) + } + if err != nil { + return nil, err + } + + client.labelSelector = p.LabelSelector + return client, nil +} + +func (p *Provider) loadConfiguration(ctx context.Context) (*dynamic.Configuration, []*knativenetworkingv1alpha1.Ingress) { + conf := &dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: make(map[string]*dynamic.Router), + Middlewares: make(map[string]*dynamic.Middleware), + Services: make(map[string]*dynamic.Service), + }, + } + + var ingressStatuses []*knativenetworkingv1alpha1.Ingress + + uniqCerts := make(map[string]*tls.CertAndStores) + for _, ingress := range p.client.ListIngresses() { + logger := log.Ctx(ctx).With(). + Str("ingress", ingress.Name). + Str("namespace", ingress.Namespace). + Logger() + + if ingress.Annotations[knativenetworking.IngressClassAnnotationKey] != traefikIngressClassName { + logger.Debug().Msgf("Skipping Ingress %s/%s", ingress.Namespace, ingress.Name) + continue + } + + if err := p.loadCertificates(ctx, ingress, uniqCerts); err != nil { + logger.Error().Err(err).Msg("Error loading TLS certificates") + continue + } + + conf.HTTP = mergeHTTPConfigs(conf.HTTP, p.buildRouters(ctx, ingress)) + + // TODO: should we handle configuration errors? + ingressStatuses = append(ingressStatuses, ingress) + } + + if len(uniqCerts) > 0 { + conf.TLS = &dynamic.TLSConfiguration{ + Certificates: slices.Collect(maps.Values(uniqCerts)), + } + } + + return conf, ingressStatuses +} + +// loadCertificates loads the TLS certificates for the given Knative Ingress. +// This method mutates the uniqCerts map to add the loaded certificates. +func (p *Provider) loadCertificates(ctx context.Context, ingress *knativenetworkingv1alpha1.Ingress, uniqCerts map[string]*tls.CertAndStores) error { + for _, t := range ingress.Spec.TLS { + // TODO: maybe this could be allowed with an allowCrossNamespace option in the future. + if t.SecretNamespace != ingress.Namespace { + log.Ctx(ctx).Debug().Msg("TLS secret namespace has to be the same as the Ingress one") + continue + } + + key := ingress.Namespace + "-" + t.SecretName + + // TODO: as specified in the GoDoc we should validate that the certificates contain the configured Hosts. + if _, exists := uniqCerts[key]; !exists { + cert, err := p.loadCertificate(ingress.Namespace, t.SecretName) + if err != nil { + return fmt.Errorf("getting certificate: %w", err) + } + uniqCerts[key] = &tls.CertAndStores{Certificate: cert} + } + } + + return nil +} + +func (p *Provider) loadCertificate(namespace, secretName string) (tls.Certificate, error) { + secret, err := p.client.GetSecret(namespace, secretName) + if err != nil { + return tls.Certificate{}, fmt.Errorf("getting secret %s/%s: %w", namespace, secretName, err) + } + + certBytes, hasCert := secret.Data[corev1.TLSCertKey] + keyBytes, hasKey := secret.Data[corev1.TLSPrivateKeyKey] + + if (!hasCert || len(certBytes) == 0) || (!hasKey || len(keyBytes) == 0) { + return tls.Certificate{}, errors.New("secret does not contain a keypair") + } + + return tls.Certificate{ + CertFile: types.FileOrContent(certBytes), + KeyFile: types.FileOrContent(keyBytes), + }, nil +} + +func (p *Provider) buildRouters(ctx context.Context, ingress *knativenetworkingv1alpha1.Ingress) *dynamic.HTTPConfiguration { + logger := log.Ctx(ctx).With().Logger() + + conf := &dynamic.HTTPConfiguration{ + Routers: make(map[string]*dynamic.Router), + Middlewares: make(map[string]*dynamic.Middleware), + Services: make(map[string]*dynamic.Service), + } + + for ri, rule := range ingress.Spec.Rules { + if rule.HTTP == nil { + logger.Debug().Msgf("No HTTP rule defined for rule %d in Ingress %s", ri, ingress.Name) + continue + } + + entrypoints := p.PublicEntrypoints + if rule.Visibility == knativenetworkingv1alpha1.IngressVisibilityClusterLocal { + if p.PrivateEntrypoints == nil { + // Skip route creation as no internal entrypoints are defined for cluster local visibility. + continue + } + entrypoints = p.PrivateEntrypoints + } + + // TODO: support rewrite host + for pi, path := range rule.HTTP.Paths { + routerKey := fmt.Sprintf("%s-%s-rule-%d-path-%d", ingress.Namespace, ingress.Name, ri, pi) + router := &dynamic.Router{ + EntryPoints: entrypoints, + Rule: buildRule(rule.Hosts, path.Headers, path.Path), + Middlewares: make([]string, 0), + Service: routerKey + "-wrr", + } + + if len(path.AppendHeaders) > 0 { + midKey := fmt.Sprintf("%s-append-headers", routerKey) + + router.Middlewares = append(router.Middlewares, midKey) + conf.Middlewares[midKey] = &dynamic.Middleware{ + Headers: &dynamic.Headers{ + CustomRequestHeaders: path.AppendHeaders, + }, + } + } + + wrr, services, err := p.buildWeightedRoundRobin(routerKey, path.Splits) + if err != nil { + logger.Error().Err(err).Msg("Error building weighted round robin") + continue + } + + // TODO: support Ingress#HTTPOption to check if HTTP router should redirect to the HTTPS one. + conf.Routers[routerKey] = router + + // TODO: at some point we should allow to define a default TLS secret at the provider level to enable TLS with a custom cert when external-domain-tls is disabled. + // see https://knative.dev/docs/serving/encryption/external-domain-tls/#manually-obtain-and-renew-certificates + if len(ingress.Spec.TLS) > 0 { + conf.Routers[routerKey+"-tls"] = &dynamic.Router{ + EntryPoints: router.EntryPoints, + Rule: router.Rule, // TODO: maybe the rule should be a new one containing the TLS hosts injected by Knative. + Middlewares: router.Middlewares, + Service: router.Service, + TLS: &dynamic.RouterTLSConfig{}, + } + } + + conf.Services[routerKey+"-wrr"] = &dynamic.Service{Weighted: wrr} + for k, v := range services { + conf.Services[k] = v + } + } + } + + return conf +} + +func (p *Provider) buildWeightedRoundRobin(routerKey string, splits []knativenetworkingv1alpha1.IngressBackendSplit) (*dynamic.WeightedRoundRobin, map[string]*dynamic.Service, error) { + wrr := &dynamic.WeightedRoundRobin{ + Services: make([]dynamic.WRRService, 0), + } + + services := make(map[string]*dynamic.Service) + for si, split := range splits { + serviceKey := fmt.Sprintf("%s-split-%d", routerKey, si) + + var err error + services[serviceKey], err = p.buildService(split.ServiceNamespace, split.ServiceName, split.ServicePort) + if err != nil { + return nil, nil, fmt.Errorf("building service: %w", err) + } + + // As described in the spec if there is only one split it defaults to 100. + percent := split.Percent + if len(splits) == 1 { + percent = 100 + } + + wrr.Services = append(wrr.Services, dynamic.WRRService{ + Name: serviceKey, + Weight: ptr.To(percent), + Headers: split.AppendHeaders, + }) + } + + return wrr, services, nil +} + +func (p *Provider) buildService(namespace, serviceName string, port intstr.IntOrString) (*dynamic.Service, error) { + servers, err := p.buildServers(namespace, serviceName, port) + if err != nil { + return nil, fmt.Errorf("building servers: %w", err) + } + + var lb dynamic.ServersLoadBalancer + lb.SetDefaults() + lb.Servers = servers + + return &dynamic.Service{LoadBalancer: &lb}, nil +} + +func (p *Provider) buildServers(namespace, serviceName string, port intstr.IntOrString) ([]dynamic.Server, error) { + service, err := p.client.GetService(namespace, serviceName) + if err != nil { + return nil, fmt.Errorf("getting service %s/%s: %w", namespace, serviceName, err) + } + + var svcPort *corev1.ServicePort + for _, p := range service.Spec.Ports { + if p.Name == port.String() || strconv.Itoa(int(p.Port)) == port.String() { + svcPort = &p + break + } + } + if svcPort == nil { + return nil, errors.New("service port not found") + } + + if service.Spec.ClusterIP == "" { + return nil, errors.New("service does not have a ClusterIP") + } + + scheme := "http" + if svcPort.AppProtocol != nil && *svcPort.AppProtocol == knativenetworking.AppProtocolH2C { + scheme = "h2c" + } + + hostPort := net.JoinHostPort(service.Spec.ClusterIP, strconv.Itoa(int(svcPort.Port))) + return []dynamic.Server{{URL: fmt.Sprintf("%s://%s", scheme, hostPort)}}, nil +} + +func (p *Provider) updateKnativeIngressStatus(ctx context.Context, ingress *knativenetworkingv1alpha1.Ingress) error { + log.Ctx(ctx).Debug().Msgf("Updating status for Ingress %s/%s", ingress.Namespace, ingress.Name) + + var publicLbs []knativenetworkingv1alpha1.LoadBalancerIngressStatus + if p.PublicService.Name != "" && p.PublicService.Namespace != "" { + publicLbs = append(publicLbs, knativenetworkingv1alpha1.LoadBalancerIngressStatus{ + DomainInternal: network.GetServiceHostname(p.PublicService.Name, p.PublicService.Namespace), + }) + } + + var privateLbs []knativenetworkingv1alpha1.LoadBalancerIngressStatus + if p.PrivateService.Name != "" && p.PrivateService.Namespace != "" { + privateLbs = append(privateLbs, knativenetworkingv1alpha1.LoadBalancerIngressStatus{ + DomainInternal: network.GetServiceHostname(p.PrivateService.Name, p.PrivateService.Namespace), + }) + } + + if ingress.GetStatus() == nil || !ingress.GetStatus().GetCondition(knativenetworkingv1alpha1.IngressConditionNetworkConfigured).IsTrue() || ingress.GetGeneration() != ingress.GetStatus().ObservedGeneration { + ingress.Status.MarkNetworkConfigured() + ingress.Status.MarkLoadBalancerReady(publicLbs, privateLbs) + ingress.Status.ObservedGeneration = ingress.GetGeneration() + + return p.client.UpdateIngressStatus(ingress) + } + return nil +} + +func buildRule(hosts []string, headers map[string]knativenetworkingv1alpha1.HeaderMatch, path string) string { + var operands []string + + if len(hosts) > 0 { + var hostRules []string + for _, host := range hosts { + hostRules = append(hostRules, fmt.Sprintf("Host(`%v`)", host)) + } + operands = append(operands, fmt.Sprintf("(%s)", strings.Join(hostRules, " || "))) + } + + if len(headers) > 0 { + headerKeys := slices.Collect(maps.Keys(headers)) + slices.Sort(headerKeys) + + var headerRules []string + for _, key := range headerKeys { + headerRules = append(headerRules, fmt.Sprintf("Header(`%s`,`%s`)", key, headers[key].Exact)) + } + operands = append(operands, fmt.Sprintf("(%s)", strings.Join(headerRules, " && "))) + } + + if len(path) > 0 { + operands = append(operands, fmt.Sprintf("PathPrefix(`%s`)", path)) + } + + return strings.Join(operands, " && ") +} + +func mergeHTTPConfigs(confs ...*dynamic.HTTPConfiguration) *dynamic.HTTPConfiguration { + conf := &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + } + + for _, c := range confs { + for k, v := range c.Routers { + conf.Routers[k] = v + } + for k, v := range c.Middlewares { + conf.Middlewares[k] = v + } + for k, v := range c.Services { + conf.Services[k] = v + } + } + + return conf +} + +func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *safe.Pool, eventsChan <-chan interface{}) chan interface{} { + logger := log.Ctx(ctx).With().Logger() + if throttleDuration == 0 { + return nil + } + // Create a buffered channel to hold the pending event (if we're delaying processing the event due to throttling) + eventsChanBuffered := make(chan interface{}, 1) + + // Run a goroutine that reads events from eventChan and does a non-blocking write to pendingEvent. + // This guarantees that writing to eventChan will never block, + // and that pendingEvent will have something in it if there's been an event since we read from that channel. + pool.GoCtx(func(ctxPool context.Context) { + for { + select { + case <-ctxPool.Done(): + return + case nextEvent := <-eventsChan: + select { + case eventsChanBuffered <- nextEvent: + default: + // We already have an event in eventsChanBuffered, so we'll do a refresh as soon as our throttle allows us to. + // It's fine to drop the event and keep whatever's in the buffer -- we don't do different things for different events + logger.Debug().Msgf("Dropping event kind %T due to throttling", nextEvent) + } + } + } + }) + + return eventsChanBuffered +} diff --git a/pkg/provider/kubernetes/knative/kubernetes_test.go b/pkg/provider/kubernetes/knative/kubernetes_test.go new file mode 100644 index 000000000..08ea0e2c3 --- /dev/null +++ b/pkg/provider/kubernetes/knative/kubernetes_test.go @@ -0,0 +1,478 @@ +package knative + +import ( + "os" + "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/traefik/paerser/types" + "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/provider/kubernetes/k8s" + "k8s.io/apimachinery/pkg/runtime" + kubefake "k8s.io/client-go/kubernetes/fake" + kscheme "k8s.io/client-go/kubernetes/scheme" + "k8s.io/utils/ptr" + knativenetworkingv1alpha1 "knative.dev/networking/pkg/apis/networking/v1alpha1" + knfake "knative.dev/networking/pkg/client/clientset/versioned/fake" +) + +func init() { + // required by k8s.MustParseYaml + if err := knativenetworkingv1alpha1.AddToScheme(kscheme.Scheme); err != nil { + panic(err) + } +} + +func Test_loadConfiguration(t *testing.T) { + testCases := []struct { + desc string + paths []string + want *dynamic.Configuration + wantLen int + }{ + { + desc: "Wrong ingress class", + paths: []string{"wrong_ingress_class.yaml"}, + wantLen: 0, + want: &dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{}, + Services: map[string]*dynamic.Service{}, + Middlewares: map[string]*dynamic.Middleware{}, + }, + }, + }, + { + desc: "Cluster Local", + paths: []string{"cluster_local.yaml", "services.yaml"}, + wantLen: 1, + want: &dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "default-helloworld-go-rule-0-path-0": { + EntryPoints: []string{"priv-http", "priv-https"}, + Service: "default-helloworld-go-rule-0-path-0-wrr", + Rule: "(Host(`helloworld-go.default`) || Host(`helloworld-go.default.svc`) || Host(`helloworld-go.default.svc.cluster.local`))", + Middlewares: []string{}, + }, + }, + Services: map[string]*dynamic.Service{ + "default-helloworld-go-rule-0-path-0-split-0": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: "wrr", + PassHostHeader: ptr.To(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: types.Duration(100 * time.Millisecond), + }, + Servers: []dynamic.Server{ + { + URL: "http://10.43.38.208:80", + }, + }, + }, + }, + "default-helloworld-go-rule-0-path-0-split-1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: "wrr", + PassHostHeader: ptr.To(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: types.Duration(100 * time.Millisecond), + }, + Servers: []dynamic.Server{ + { + URL: "http://10.43.44.18:80", + }, + }, + }, + }, + "default-helloworld-go-rule-0-path-0-wrr": { + Weighted: &dynamic.WeightedRoundRobin{ + Services: []dynamic.WRRService{ + { + Name: "default-helloworld-go-rule-0-path-0-split-0", + Weight: ptr.To(50), + Headers: map[string]string{ + "Knative-Serving-Namespace": "default", + "Knative-Serving-Revision": "helloworld-go-00001", + }, + }, + { + Name: "default-helloworld-go-rule-0-path-0-split-1", + Weight: ptr.To(50), + Headers: map[string]string{ + "Knative-Serving-Namespace": "default", + "Knative-Serving-Revision": "helloworld-go-00002", + }, + }, + }, + }, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + }, + }, + }, + { + desc: "External IP", + paths: []string{"external_ip.yaml", "services.yaml"}, + wantLen: 1, + want: &dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "default-helloworld-go-rule-0-path-0": { + EntryPoints: []string{"http", "https"}, + Service: "default-helloworld-go-rule-0-path-0-wrr", + Rule: "(Host(`helloworld-go.default`) || Host(`helloworld-go.default.svc`) || Host(`helloworld-go.default.svc.cluster.local`))", + Middlewares: []string{}, + }, + }, + Services: map[string]*dynamic.Service{ + "default-helloworld-go-rule-0-path-0-split-0": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: "wrr", + PassHostHeader: ptr.To(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: types.Duration(100 * time.Millisecond), + }, + Servers: []dynamic.Server{ + { + URL: "http://10.43.38.208:80", + }, + }, + }, + }, + "default-helloworld-go-rule-0-path-0-split-1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: "wrr", + PassHostHeader: ptr.To(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: types.Duration(100 * time.Millisecond), + }, + Servers: []dynamic.Server{ + { + URL: "http://10.43.44.18:80", + }, + }, + }, + }, + "default-helloworld-go-rule-0-path-0-wrr": { + Weighted: &dynamic.WeightedRoundRobin{ + Services: []dynamic.WRRService{ + { + Name: "default-helloworld-go-rule-0-path-0-split-0", + Weight: ptr.To(50), + Headers: map[string]string{ + "Knative-Serving-Namespace": "default", + "Knative-Serving-Revision": "helloworld-go-00001", + }, + }, + { + Name: "default-helloworld-go-rule-0-path-0-split-1", + Weight: ptr.To(50), + Headers: map[string]string{ + "Knative-Serving-Namespace": "default", + "Knative-Serving-Revision": "helloworld-go-00002", + }, + }, + }, + }, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + }, + }, + }, + { + desc: "TLS", + paths: []string{"tls.yaml", "services.yaml"}, + wantLen: 1, + want: &dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "default-helloworld-go-rule-0-path-0": { + EntryPoints: []string{"http", "https"}, + Service: "default-helloworld-go-rule-0-path-0-wrr", + Rule: "(Host(`helloworld-go.default`) || Host(`helloworld-go.default.svc`) || Host(`helloworld-go.default.svc.cluster.local`))", + Middlewares: []string{}, + }, + "default-helloworld-go-rule-0-path-0-tls": { + EntryPoints: []string{"http", "https"}, + Service: "default-helloworld-go-rule-0-path-0-wrr", + Rule: "(Host(`helloworld-go.default`) || Host(`helloworld-go.default.svc`) || Host(`helloworld-go.default.svc.cluster.local`))", + Middlewares: []string{}, + TLS: &dynamic.RouterTLSConfig{}, + }, + }, + Services: map[string]*dynamic.Service{ + "default-helloworld-go-rule-0-path-0-split-0": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: "wrr", + PassHostHeader: ptr.To(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: types.Duration(100 * time.Millisecond), + }, + Servers: []dynamic.Server{ + { + URL: "http://10.43.38.208:80", + }, + }, + }, + }, + "default-helloworld-go-rule-0-path-0-split-1": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: "wrr", + PassHostHeader: ptr.To(true), + ResponseForwarding: &dynamic.ResponseForwarding{ + FlushInterval: types.Duration(100 * time.Millisecond), + }, + Servers: []dynamic.Server{ + { + URL: "http://10.43.44.18:80", + }, + }, + }, + }, + "default-helloworld-go-rule-0-path-0-wrr": { + Weighted: &dynamic.WeightedRoundRobin{ + Services: []dynamic.WRRService{ + { + Name: "default-helloworld-go-rule-0-path-0-split-0", + Weight: ptr.To(50), + Headers: map[string]string{ + "Knative-Serving-Namespace": "default", + "Knative-Serving-Revision": "helloworld-go-00001", + }, + }, + { + Name: "default-helloworld-go-rule-0-path-0-split-1", + Weight: ptr.To(50), + Headers: map[string]string{ + "Knative-Serving-Namespace": "default", + "Knative-Serving-Revision": "helloworld-go-00002", + }, + }, + }, + }, + }, + }, + Middlewares: map[string]*dynamic.Middleware{}, + }, + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.desc, func(t *testing.T) { + t.Parallel() + + k8sObjects, knObjects := readResources(t, testCase.paths) + + k8sClient := kubefake.NewClientset(k8sObjects...) + knClient := knfake.NewSimpleClientset(knObjects...) + + client := newClientImpl(knClient, k8sClient) + + eventCh, err := client.WatchAll(nil, make(chan struct{})) + require.NoError(t, err) + + if len(k8sObjects) > 0 || len(knObjects) > 0 { + // just wait for the first event + <-eventCh + } + + p := Provider{ + PublicEntrypoints: []string{"http", "https"}, + PrivateEntrypoints: []string{"priv-http", "priv-https"}, + client: client, + } + + got, gotIngresses := p.loadConfiguration(t.Context()) + assert.Len(t, gotIngresses, testCase.wantLen) + assert.Equal(t, testCase.want, got) + }) + } +} + +func Test_buildRule(t *testing.T) { + testCases := []struct { + desc string + hosts []string + headers map[string]knativenetworkingv1alpha1.HeaderMatch + path string + want string + }{ + { + desc: "single host, no headers, no path", + hosts: []string{"example.com"}, + want: "(Host(`example.com`))", + }, + { + desc: "multiple hosts, no headers, no path", + hosts: []string{"example.com", "foo.com"}, + want: "(Host(`example.com`) || Host(`foo.com`))", + }, + { + desc: "single host, single header, no path", + hosts: []string{"example.com"}, + headers: map[string]knativenetworkingv1alpha1.HeaderMatch{ + "X-Header": {Exact: "value"}, + }, + want: "(Host(`example.com`)) && (Header(`X-Header`,`value`))", + }, + { + desc: "single host, multiple headers, no path", + hosts: []string{"example.com"}, + headers: map[string]knativenetworkingv1alpha1.HeaderMatch{ + "X-Header": {Exact: "value"}, + "X-Header2": {Exact: "value2"}, + }, + want: "(Host(`example.com`)) && (Header(`X-Header`,`value`) && Header(`X-Header2`,`value2`))", + }, + { + desc: "single host, multiple headers, with path", + hosts: []string{"example.com"}, + headers: map[string]knativenetworkingv1alpha1.HeaderMatch{ + "X-Header": {Exact: "value"}, + "X-Header2": {Exact: "value2"}, + }, + path: "/foo", + want: "(Host(`example.com`)) && (Header(`X-Header`,`value`) && Header(`X-Header2`,`value2`)) && PathPrefix(`/foo`)", + }, + { + desc: "single host, no headers, with path", + hosts: []string{"example.com"}, + path: "/foo", + want: "(Host(`example.com`)) && PathPrefix(`/foo`)", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + got := buildRule(test.hosts, test.headers, test.path) + assert.Equal(t, test.want, got) + }) + } +} + +func Test_mergeHTTPConfigs(t *testing.T) { + testCases := []struct { + desc string + configs []*dynamic.HTTPConfiguration + want *dynamic.HTTPConfiguration + }{ + { + desc: "one empty configuration", + configs: []*dynamic.HTTPConfiguration{ + { + Routers: map[string]*dynamic.Router{ + "router1": {Rule: "Host(`example.com`)"}, + }, + Middlewares: map[string]*dynamic.Middleware{ + "middleware1": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"X-Test": "value"}}}, + }, + Services: map[string]*dynamic.Service{ + "service1": {LoadBalancer: &dynamic.ServersLoadBalancer{Servers: []dynamic.Server{{URL: "http://example.com"}}}}, + }, + }, + { + Routers: map[string]*dynamic.Router{}, + Middlewares: map[string]*dynamic.Middleware{}, + Services: map[string]*dynamic.Service{}, + }, + }, + want: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "router1": {Rule: "Host(`example.com`)"}, + }, + Middlewares: map[string]*dynamic.Middleware{ + "middleware1": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"X-Test": "value"}}}, + }, + Services: map[string]*dynamic.Service{ + "service1": {LoadBalancer: &dynamic.ServersLoadBalancer{Servers: []dynamic.Server{{URL: "http://example.com"}}}}, + }, + }, + }, + { + desc: "merging two non-empty configurations", + configs: []*dynamic.HTTPConfiguration{ + { + Routers: map[string]*dynamic.Router{ + "router1": {Rule: "Host(`example.com`)"}, + }, + Middlewares: map[string]*dynamic.Middleware{ + "middleware1": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"X-Test": "value"}}}, + }, + Services: map[string]*dynamic.Service{ + "service1": {LoadBalancer: &dynamic.ServersLoadBalancer{Servers: []dynamic.Server{{URL: "http://example.com"}}}}, + }, + }, + { + Routers: map[string]*dynamic.Router{ + "router2": {Rule: "PathPrefix(`/test`)"}, + }, + Middlewares: map[string]*dynamic.Middleware{ + "middleware2": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"X-Test": "value"}}}, + }, + Services: map[string]*dynamic.Service{ + "service2": {LoadBalancer: &dynamic.ServersLoadBalancer{Servers: []dynamic.Server{{URL: "http://example.com"}}}}, + }, + }, + }, + want: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "router1": {Rule: "Host(`example.com`)"}, + "router2": {Rule: "PathPrefix(`/test`)"}, + }, + Middlewares: map[string]*dynamic.Middleware{ + "middleware1": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"X-Test": "value"}}}, + "middleware2": {Headers: &dynamic.Headers{CustomRequestHeaders: map[string]string{"X-Test": "value"}}}, + }, + Services: map[string]*dynamic.Service{ + "service1": {LoadBalancer: &dynamic.ServersLoadBalancer{Servers: []dynamic.Server{{URL: "http://example.com"}}}}, + "service2": {LoadBalancer: &dynamic.ServersLoadBalancer{Servers: []dynamic.Server{{URL: "http://example.com"}}}}, + }, + }, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + got := mergeHTTPConfigs(test.configs...) + assert.Equal(t, test.want, got) + }) + } +} + +func readResources(t *testing.T, paths []string) ([]runtime.Object, []runtime.Object) { + t.Helper() + + var ( + k8sObjects []runtime.Object + knObjects []runtime.Object + ) + for _, path := range paths { + yamlContent, err := os.ReadFile(filepath.FromSlash("./fixtures/" + path)) + if err != nil { + panic(err) + } + + objects := k8s.MustParseYaml(yamlContent) + for _, obj := range objects { + switch obj.GetObjectKind().GroupVersionKind().Group { + case "networking.internal.knative.dev": + knObjects = append(knObjects, obj) + default: + k8sObjects = append(k8sObjects, obj) + } + } + } + + return k8sObjects, knObjects +} diff --git a/pkg/provider/kv/consul/consul.go b/pkg/provider/kv/consul/consul.go index 2a5fdfd49..14d8e82b6 100644 --- a/pkg/provider/kv/consul/consul.go +++ b/pkg/provider/kv/consul/consul.go @@ -97,3 +97,8 @@ func (p *Provider) Init() error { return p.Provider.Init(consul.StoreName, p.name, config) } + +// Namespace returns the namespace of the Consul provider. +func (p *Provider) Namespace() string { + return p.namespace +} diff --git a/pkg/provider/kv/redis/redis.go b/pkg/provider/kv/redis/redis.go index c6e357f03..930cf087d 100644 --- a/pkg/provider/kv/redis/redis.go +++ b/pkg/provider/kv/redis/redis.go @@ -60,10 +60,20 @@ func (p *Provider) Init() error { } if p.Sentinel != nil { - switch { - case p.Sentinel.LatencyStrategy && !(p.Sentinel.RandomStrategy || p.Sentinel.ReplicaStrategy): - case p.Sentinel.RandomStrategy && !(p.Sentinel.LatencyStrategy || p.Sentinel.ReplicaStrategy): - case p.Sentinel.ReplicaStrategy && !(p.Sentinel.RandomStrategy || p.Sentinel.LatencyStrategy): + count := 0 + if p.Sentinel.LatencyStrategy { + count++ + } + + if p.Sentinel.ReplicaStrategy { + count++ + } + + if p.Sentinel.RandomStrategy { + count++ + } + + if count > 1 { return errors.New("latencyStrategy, randomStrategy and replicaStrategy options are mutually exclusive, please use only one of those options") } diff --git a/pkg/provider/nomad/nomad.go b/pkg/provider/nomad/nomad.go index 77876468e..c9581a036 100644 --- a/pkg/provider/nomad/nomad.go +++ b/pkg/provider/nomad/nomad.go @@ -571,3 +571,8 @@ func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *s return eventsChanBuffered } + +// Namespace returns the namespace of the Nomad provider. +func (p *Provider) Namespace() string { + return p.namespace +} diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 2597a05e6..8ebe66055 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -12,3 +12,14 @@ type Provider interface { Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error Init() error } + +// NamespacedProvider is implemented by providers that support namespace-scoped configurations, +// where each configured namespace results in a dedicated provider instance. +// This enables clear identification of which namespace each provider instance serves during +// startup logging and operational monitoring. +type NamespacedProvider interface { + Provider + + // Namespace returns the specific namespace this provider instance is configured for. + Namespace() string +} diff --git a/pkg/provider/traefik/internal.go b/pkg/provider/traefik/internal.go index 73d17d63f..7c99f977e 100644 --- a/pkg/provider/traefik/internal.go +++ b/pkg/provider/traefik/internal.go @@ -231,7 +231,7 @@ func (i *Provider) entryPointModels(cfg *dynamic.Configuration) { } } - if len(ep.HTTP.Middlewares) == 0 && ep.HTTP.TLS == nil && defaultRuleSyntax == "" && ep.Observability == nil { + if len(ep.HTTP.Middlewares) == 0 && ep.HTTP.TLS == nil && defaultRuleSyntax == "" && ep.Observability == nil && ep.HTTP.EncodedCharacters == nil { continue } @@ -240,6 +240,18 @@ func (i *Provider) entryPointModels(cfg *dynamic.Configuration) { Middlewares: ep.HTTP.Middlewares, } + if ep.HTTP.EncodedCharacters != nil { + httpModel.DeniedEncodedPathCharacters = &dynamic.RouterDeniedEncodedPathCharacters{ + AllowEncodedSlash: ep.HTTP.EncodedCharacters.AllowEncodedSlash, + AllowEncodedBackSlash: ep.HTTP.EncodedCharacters.AllowEncodedBackSlash, + AllowEncodedPercent: ep.HTTP.EncodedCharacters.AllowEncodedPercent, + AllowEncodedQuestionMark: ep.HTTP.EncodedCharacters.AllowEncodedQuestionMark, + AllowEncodedSemicolon: ep.HTTP.EncodedCharacters.AllowEncodedSemicolon, + AllowEncodedHash: ep.HTTP.EncodedCharacters.AllowEncodedHash, + AllowEncodedNullCharacter: ep.HTTP.EncodedCharacters.AllowEncodedNullCharacter, + } + } + if ep.Observability != nil { httpModel.Observability = dynamic.RouterObservabilityConfig{ AccessLogs: ep.Observability.AccessLogs, diff --git a/pkg/server/aggregator.go b/pkg/server/aggregator.go index 8f22474fe..36326af76 100644 --- a/pkg/server/aggregator.go +++ b/pkg/server/aggregator.go @@ -46,7 +46,9 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint for pvd, configuration := range configurations { if configuration.HTTP != nil { for routerName, router := range configuration.HTTP.Routers { - if len(router.EntryPoints) == 0 { + // If no entrypoint is defined, and the router has no parentRefs (i.e. is not a child router), + // we set the default entrypoints. + if len(router.EntryPoints) == 0 && router.ParentRefs == nil { log.Debug(). Str(logs.RouterName, routerName). Strs(logs.EntryPointName, defaultEntryPoints). @@ -63,6 +65,16 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint Msg("Router's `ruleSyntax` option is deprecated, please remove any usage of this option.") } + var qualifiedParentRefs []string + for _, parentRef := range router.ParentRefs { + if parts := strings.Split(parentRef, "@"); len(parts) == 1 { + parentRef = provider.MakeQualifiedName(pvd, parentRef) + } + + qualifiedParentRefs = append(qualifiedParentRefs, parentRef) + } + router.ParentRefs = qualifiedParentRefs + conf.HTTP.Routers[provider.MakeQualifiedName(pvd, routerName)] = router } for middlewareName, middleware := range configuration.HTTP.Middlewares { @@ -163,7 +175,14 @@ func applyModel(cfg dynamic.Configuration) dynamic.Configuration { if cfg.HTTP != nil && len(cfg.HTTP.Models) > 0 { rts := make(map[string]*dynamic.Router) + modelRouterNames := make(map[string][]string) for name, rt := range cfg.HTTP.Routers { + // Only root routers can have models applied. + if rt.ParentRefs != nil { + rts[name] = rt + continue + } + router := rt.DeepCopy() if !router.DefaultRule && router.RuleSyntax == "" { @@ -193,6 +212,12 @@ func applyModel(cfg dynamic.Configuration) dynamic.Configuration { cp.Middlewares = append(m.Middlewares, cp.Middlewares...) + if m.DeniedEncodedPathCharacters != nil { + // As the denied encoded path characters option is not configurable at the router level, + // we can simply copy the whole structure to override the router's default config. + cp.DeniedEncodedPathCharacters = m.DeniedEncodedPathCharacters + } + if cp.Observability == nil { cp.Observability = &dynamic.RouterObservabilityConfig{} } @@ -216,7 +241,9 @@ func applyModel(cfg dynamic.Configuration) dynamic.Configuration { rtName := name if len(eps) > 1 { rtName = epName + "-" + name + modelRouterNames[name] = append(modelRouterNames[name], rtName) } + rts[rtName] = cp } else { router.EntryPoints = append(router.EntryPoints, epName) @@ -226,6 +253,26 @@ func applyModel(cfg dynamic.Configuration) dynamic.Configuration { } } + for _, rt := range rts { + if rt.ParentRefs == nil { + continue + } + + var parentRefs []string + for _, ref := range rt.ParentRefs { + // Only add the initial parent ref if it still exists. + if _, ok := rts[ref]; ok { + parentRefs = append(parentRefs, ref) + } + + if names, ok := modelRouterNames[ref]; ok { + parentRefs = append(parentRefs, names...) + } + } + + rt.ParentRefs = parentRefs + } + cfg.HTTP.Routers = rts } @@ -265,6 +312,11 @@ func applyModel(cfg dynamic.Configuration) dynamic.Configuration { func applyDefaultObservabilityModel(cfg dynamic.Configuration) { if cfg.HTTP != nil { for _, router := range cfg.HTTP.Routers { + // Only root routers can have models applied. + if router.ParentRefs != nil { + continue + } + if router.Observability == nil { router.Observability = &dynamic.RouterObservabilityConfig{ AccessLogs: pointer(true), diff --git a/pkg/server/aggregator_test.go b/pkg/server/aggregator_test.go index e33ef7ae1..8d78447d2 100644 --- a/pkg/server/aggregator_test.go +++ b/pkg/server/aggregator_test.go @@ -810,6 +810,414 @@ func Test_applyModel(t *testing.T) { }, }, }, + { + desc: "child router with parentRefs, parent not split", + input: dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "parent": { + EntryPoints: []string{"web"}, + }, + "child": { + ParentRefs: []string{"parent"}, + }, + }, + Middlewares: make(map[string]*dynamic.Middleware), + Services: make(map[string]*dynamic.Service), + Models: make(map[string]*dynamic.Model), + }, + }, + expected: dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "parent": { + EntryPoints: []string{"web"}, + Observability: &dynamic.RouterObservabilityConfig{ + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), + TraceVerbosity: otypes.MinimalVerbosity, + }, + }, + "child": { + ParentRefs: []string{"parent"}, + }, + }, + Middlewares: make(map[string]*dynamic.Middleware), + Services: make(map[string]*dynamic.Service), + Models: make(map[string]*dynamic.Model), + }, + }, + }, + { + desc: "child router with parentRefs, parent split by model", + input: dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "parent": { + EntryPoints: []string{"websecure", "web"}, + }, + "child": { + ParentRefs: []string{"parent"}, + }, + }, + Middlewares: make(map[string]*dynamic.Middleware), + Services: make(map[string]*dynamic.Service), + Models: map[string]*dynamic.Model{ + "websecure@internal": { + Middlewares: []string{"test"}, + TLS: &dynamic.RouterTLSConfig{}, + }, + }, + }, + }, + expected: dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "parent": { + EntryPoints: []string{"web"}, + Observability: &dynamic.RouterObservabilityConfig{ + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), + TraceVerbosity: otypes.MinimalVerbosity, + }, + }, + "websecure-parent": { + EntryPoints: []string{"websecure"}, + Middlewares: []string{"test"}, + TLS: &dynamic.RouterTLSConfig{}, + Observability: &dynamic.RouterObservabilityConfig{ + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), + TraceVerbosity: otypes.MinimalVerbosity, + }, + }, + "child": { + ParentRefs: []string{"parent", "websecure-parent"}, + }, + }, + Middlewares: make(map[string]*dynamic.Middleware), + Services: make(map[string]*dynamic.Service), + Models: map[string]*dynamic.Model{ + "websecure@internal": { + Middlewares: []string{"test"}, + TLS: &dynamic.RouterTLSConfig{}, + }, + }, + }, + }, + }, + { + desc: "multiple child routers with parentRefs, parent split by model", + input: dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "parent": { + EntryPoints: []string{"websecure", "web"}, + }, + "child1": { + ParentRefs: []string{"parent"}, + }, + "child2": { + ParentRefs: []string{"parent"}, + }, + }, + Middlewares: make(map[string]*dynamic.Middleware), + Services: make(map[string]*dynamic.Service), + Models: map[string]*dynamic.Model{ + "websecure@internal": { + Middlewares: []string{"auth"}, + }, + }, + }, + }, + expected: dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "parent": { + EntryPoints: []string{"web"}, + Observability: &dynamic.RouterObservabilityConfig{ + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), + TraceVerbosity: otypes.MinimalVerbosity, + }, + }, + "websecure-parent": { + EntryPoints: []string{"websecure"}, + Middlewares: []string{"auth"}, + Observability: &dynamic.RouterObservabilityConfig{ + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), + TraceVerbosity: otypes.MinimalVerbosity, + }, + }, + "child1": { + ParentRefs: []string{"parent", "websecure-parent"}, + }, + "child2": { + ParentRefs: []string{"parent", "websecure-parent"}, + }, + }, + Middlewares: make(map[string]*dynamic.Middleware), + Services: make(map[string]*dynamic.Service), + Models: map[string]*dynamic.Model{ + "websecure@internal": { + Middlewares: []string{"auth"}, + }, + }, + }, + }, + }, + { + desc: "child router with parentRefs to non-existing parent", + input: dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "child": { + ParentRefs: []string{"nonexistent"}, + }, + }, + Middlewares: make(map[string]*dynamic.Middleware), + Services: make(map[string]*dynamic.Service), + Models: make(map[string]*dynamic.Model), + }, + }, + expected: dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "child": { + ParentRefs: []string{"nonexistent"}, + }, + }, + Middlewares: make(map[string]*dynamic.Middleware), + Services: make(map[string]*dynamic.Service), + Models: make(map[string]*dynamic.Model), + }, + }, + }, + { + desc: "child router with multiple parentRefs, some split", + input: dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "parent1": { + EntryPoints: []string{"websecure", "web"}, + }, + "parent2": { + EntryPoints: []string{"web"}, + }, + "child": { + ParentRefs: []string{"parent1", "parent2"}, + }, + }, + Middlewares: make(map[string]*dynamic.Middleware), + Services: make(map[string]*dynamic.Service), + Models: map[string]*dynamic.Model{ + "websecure@internal": { + TLS: &dynamic.RouterTLSConfig{}, + }, + }, + }, + }, + expected: dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "parent1": { + EntryPoints: []string{"web"}, + Observability: &dynamic.RouterObservabilityConfig{ + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), + TraceVerbosity: otypes.MinimalVerbosity, + }, + }, + "websecure-parent1": { + EntryPoints: []string{"websecure"}, + TLS: &dynamic.RouterTLSConfig{}, + Observability: &dynamic.RouterObservabilityConfig{ + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), + TraceVerbosity: otypes.MinimalVerbosity, + }, + }, + "parent2": { + EntryPoints: []string{"web"}, + Observability: &dynamic.RouterObservabilityConfig{ + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), + TraceVerbosity: otypes.MinimalVerbosity, + }, + }, + "child": { + ParentRefs: []string{"parent1", "websecure-parent1", "parent2"}, + }, + }, + Middlewares: make(map[string]*dynamic.Middleware), + Services: make(map[string]*dynamic.Service), + Models: map[string]*dynamic.Model{ + "websecure@internal": { + TLS: &dynamic.RouterTLSConfig{}, + }, + }, + }, + }, + }, + { + desc: "child router with multiple parentRefs, all split", + input: dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "parent1": { + EntryPoints: []string{"websecure", "web"}, + }, + "parent2": { + EntryPoints: []string{"web"}, + }, + "child": { + ParentRefs: []string{"parent1", "parent2"}, + }, + }, + Middlewares: make(map[string]*dynamic.Middleware), + Services: make(map[string]*dynamic.Service), + Models: map[string]*dynamic.Model{ + "web@internal": { + TLS: &dynamic.RouterTLSConfig{}, + }, + "websecure@internal": { + TLS: &dynamic.RouterTLSConfig{}, + }, + }, + }, + }, + expected: dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "web-parent1": { + EntryPoints: []string{"web"}, + TLS: &dynamic.RouterTLSConfig{}, + Observability: &dynamic.RouterObservabilityConfig{ + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), + TraceVerbosity: otypes.MinimalVerbosity, + }, + }, + "websecure-parent1": { + EntryPoints: []string{"websecure"}, + TLS: &dynamic.RouterTLSConfig{}, + Observability: &dynamic.RouterObservabilityConfig{ + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), + TraceVerbosity: otypes.MinimalVerbosity, + }, + }, + "parent2": { + EntryPoints: []string{"web"}, + TLS: &dynamic.RouterTLSConfig{}, + Observability: &dynamic.RouterObservabilityConfig{ + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), + TraceVerbosity: otypes.MinimalVerbosity, + }, + }, + "child": { + ParentRefs: []string{"websecure-parent1", "web-parent1", "parent2"}, + }, + }, + Middlewares: make(map[string]*dynamic.Middleware), + Services: make(map[string]*dynamic.Service), + Models: map[string]*dynamic.Model{ + "web@internal": { + TLS: &dynamic.RouterTLSConfig{}, + }, + "websecure@internal": { + TLS: &dynamic.RouterTLSConfig{}, + }, + }, + }, + }, + }, + { + desc: "child router with parentRefs, parent split into three routers", + input: dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "parent": { + EntryPoints: []string{"websecure", "web", "admin"}, + }, + "child": { + ParentRefs: []string{"parent"}, + }, + }, + Middlewares: make(map[string]*dynamic.Middleware), + Services: make(map[string]*dynamic.Service), + Models: map[string]*dynamic.Model{ + "websecure@internal": { + TLS: &dynamic.RouterTLSConfig{}, + }, + "admin@internal": { + Middlewares: []string{"admin-auth"}, + }, + }, + }, + }, + expected: dynamic.Configuration{ + HTTP: &dynamic.HTTPConfiguration{ + Routers: map[string]*dynamic.Router{ + "parent": { + EntryPoints: []string{"web"}, + Observability: &dynamic.RouterObservabilityConfig{ + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), + TraceVerbosity: otypes.MinimalVerbosity, + }, + }, + "websecure-parent": { + EntryPoints: []string{"websecure"}, + TLS: &dynamic.RouterTLSConfig{}, + Observability: &dynamic.RouterObservabilityConfig{ + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), + TraceVerbosity: otypes.MinimalVerbosity, + }, + }, + "admin-parent": { + EntryPoints: []string{"admin"}, + Middlewares: []string{"admin-auth"}, + Observability: &dynamic.RouterObservabilityConfig{ + AccessLogs: pointer(true), + Metrics: pointer(true), + Tracing: pointer(true), + TraceVerbosity: otypes.MinimalVerbosity, + }, + }, + "child": { + ParentRefs: []string{"parent", "websecure-parent", "admin-parent"}, + }, + }, + Middlewares: make(map[string]*dynamic.Middleware), + Services: make(map[string]*dynamic.Service), + Models: map[string]*dynamic.Model{ + "websecure@internal": { + TLS: &dynamic.RouterTLSConfig{}, + }, + "admin@internal": { + Middlewares: []string{"admin-auth"}, + }, + }, + }, + }, + }, } for _, test := range testCases { diff --git a/pkg/server/router/deny.go b/pkg/server/router/deny.go new file mode 100644 index 000000000..154370e83 --- /dev/null +++ b/pkg/server/router/deny.go @@ -0,0 +1,43 @@ +package router + +import ( + "net/http" + + "github.com/rs/zerolog/log" +) + +// denyEncodedPathCharacters reject the request if the escaped path contains encoded characters in the given list. +func denyEncodedPathCharacters(encodedCharacters map[string]struct{}, h http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + if len(encodedCharacters) == 0 { + h.ServeHTTP(rw, req) + return + } + + escapedPath := req.URL.EscapedPath() + + for i := 0; i < len(escapedPath); i++ { + if escapedPath[i] != '%' { + continue + } + + // This should never happen as the standard library will reject requests containing invalid percent-encodings. + // This discards URLs with a percent character at the end. + if i+2 >= len(escapedPath) { + rw.WriteHeader(http.StatusBadRequest) + return + } + + // This rejects a request with a path containing the given encoded characters. + if _, exists := encodedCharacters[escapedPath[i:i+3]]; exists { + log.Debug().Msgf("Rejecting request because it contains encoded character %s in the URL path: %s", escapedPath[i:i+3], escapedPath) + rw.WriteHeader(http.StatusBadRequest) + return + } + + i += 2 + } + + h.ServeHTTP(rw, req) + }) +} diff --git a/pkg/server/router/deny_test.go b/pkg/server/router/deny_test.go new file mode 100644 index 000000000..de17fe5fc --- /dev/null +++ b/pkg/server/router/deny_test.go @@ -0,0 +1,62 @@ +package router + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_denyEncodedPathCharacters(t *testing.T) { + tests := []struct { + name string + encoded map[string]struct{} + url string + wantStatus int + }{ + { + name: "Rejects disallowed characters", + encoded: map[string]struct{}{ + "%0A": {}, + "%0D": {}, + }, + url: "http://example.com/foo%0Abar", + wantStatus: http.StatusBadRequest, + }, + { + name: "Allows valid paths", + encoded: map[string]struct{}{ + "%0A": {}, + "%0D": {}, + }, + url: "http://example.com/foo%20bar", + wantStatus: http.StatusOK, + }, + { + name: "Handles empty path", + encoded: map[string]struct{}{ + "%0A": {}, + }, + url: "http://example.com/", + wantStatus: http.StatusOK, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + handler := denyEncodedPathCharacters(test.encoded, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + + req := httptest.NewRequest(http.MethodGet, test.url, nil) + res := httptest.NewRecorder() + + handler.ServeHTTP(res, req) + + assert.Equal(t, test.wantStatus, res.Code) + }) + } +} diff --git a/pkg/server/router/router.go b/pkg/server/router/router.go index bac2d5c48..a1bd011a0 100644 --- a/pkg/server/router/router.go +++ b/pkg/server/router/router.go @@ -6,6 +6,8 @@ import ( "fmt" "math" "net/http" + "slices" + "sort" "strings" "github.com/containous/alice" @@ -47,7 +49,13 @@ type Manager struct { } // NewManager creates a new Manager. -func NewManager(conf *runtime.Configuration, serviceManager serviceManager, middlewaresBuilder middlewareBuilder, observabilityMgr *middleware.ObservabilityMgr, tlsManager *tls.Manager, parser httpmuxer.SyntaxParser) *Manager { +func NewManager(conf *runtime.Configuration, + serviceManager serviceManager, + middlewaresBuilder middlewareBuilder, + observabilityMgr *middleware.ObservabilityMgr, + tlsManager *tls.Manager, + parser httpmuxer.SyntaxParser, +) *Manager { return &Manager{ routerHandlers: make(map[string]http.Handler), serviceManager: serviceManager, @@ -149,7 +157,13 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, entryPointName str continue } - handler, err := m.buildRouterHandler(ctxRouter, routerName, routerConfig) + // Only build handlers for root routers (routers without ParentRefs). + // Routers with ParentRefs will be built as part of their parent router's muxer. + if len(routerConfig.ParentRefs) > 0 { + continue + } + + handler, err := m.buildRouterHandler(ctxRouter, entryPointName, routerName, routerConfig) if err != nil { routerConfig.AddError(err, true) logger.Error().Err(err).Send() @@ -183,7 +197,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, entryPointName str return chain.Then(muxer) } -func (m *Manager) buildRouterHandler(ctx context.Context, routerName string, routerConfig *runtime.RouterInfo) (http.Handler, error) { +func (m *Manager) buildRouterHandler(ctx context.Context, entryPointName, routerName string, routerConfig *runtime.RouterInfo) (http.Handler, error) { if handler, ok := m.routerHandlers[routerName]; ok { return handler, nil } @@ -199,49 +213,296 @@ func (m *Manager) buildRouterHandler(ctx context.Context, routerName string, rou } } - handler, err := m.buildHTTPHandler(ctx, routerConfig, routerName) + handler, err := m.buildHTTPHandler(ctx, routerConfig, entryPointName, routerName) if err != nil { return nil, err } m.routerHandlers[routerName] = handler - return m.routerHandlers[routerName], nil + return handler, nil } -func (m *Manager) buildHTTPHandler(ctx context.Context, router *runtime.RouterInfo, routerName string) (http.Handler, error) { +func (m *Manager) buildHTTPHandler(ctx context.Context, router *runtime.RouterInfo, entryPointName, routerName string) (http.Handler, error) { var qualifiedNames []string for _, name := range router.Middlewares { qualifiedNames = append(qualifiedNames, provider.GetQualifiedName(ctx, name)) } router.Middlewares = qualifiedNames - if router.Service == "" { - return nil, errors.New("the service is missing on the router") - } - - qualifiedService := provider.GetQualifiedName(ctx, router.Service) - chain := alice.New() if router.DefaultRule { chain = chain.Append(denyrouterrecursion.WrapHandler(routerName)) } - // Access logs, metrics, and tracing middlewares are idempotent if the associated signal is disabled. - chain = chain.Append(observability.WrapRouterHandler(ctx, routerName, router.Rule, qualifiedService)) - metricsHandler := metricsMiddle.RouterMetricsHandler(ctx, m.observabilityMgr.MetricsRegistry(), routerName, qualifiedService) + var ( + nextHandler http.Handler + serviceName string + err error + ) + // Check if this router has child routers or a service. + switch { + case len(router.ChildRefs) > 0: + // This router routes to child routers - create a muxer for them + nextHandler, err = m.buildChildRoutersMuxer(ctx, entryPointName, router.ChildRefs) + if err != nil { + return nil, fmt.Errorf("building child routers muxer: %w", err) + } + serviceName = fmt.Sprintf("%s-muxer", routerName) + case router.Service != "": + // This router routes to a service + qualifiedService := provider.GetQualifiedName(ctx, router.Service) + nextHandler, err = m.serviceManager.BuildHTTP(ctx, qualifiedService) + if err != nil { + return nil, err + } + serviceName = qualifiedService + default: + return nil, errors.New("router must have either a service or child routers") + } + + // Access logs, metrics, and tracing middlewares are idempotent if the associated signal is disabled. + chain = chain.Append(observability.WrapRouterHandler(ctx, routerName, router.Rule, serviceName)) + + metricsHandler := metricsMiddle.RouterMetricsHandler(ctx, m.observabilityMgr.MetricsRegistry(), routerName, serviceName) chain = chain.Append(observability.WrapMiddleware(ctx, metricsHandler)) + chain = chain.Append(func(next http.Handler) (http.Handler, error) { - return accesslog.NewFieldHandler(next, accesslog.RouterName, routerName, nil), nil + return accesslog.NewConcatFieldHandler(next, accesslog.RouterName, routerName), nil }) + // Here we are adding deny handlers for encoded path characters and fragment. + // Deny handler are only added for root routers, child routers are protected by their parent router deny handlers. + if len(router.ParentRefs) == 0 && router.DeniedEncodedPathCharacters != nil { + chain = chain.Append(func(next http.Handler) (http.Handler, error) { + return denyEncodedPathCharacters(router.DeniedEncodedPathCharacters.Map(), next), nil + }) + } + mHandler := m.middlewaresBuilder.BuildChain(ctx, router.Middlewares) - sHandler, err := m.serviceManager.BuildHTTP(ctx, qualifiedService) - if err != nil { - return nil, err + return chain.Extend(*mHandler).Then(nextHandler) +} + +// ParseRouterTree sets up router tree and validates router configuration. +// This function performs the following operations in order: +// +// 1. Populate ChildRefs: Uses ParentRefs to build the parent-child relationship graph +// 2. Root-first traversal: Starting from root routers (no ParentRefs), traverses the tree +// 3. Cycle detection: Detects circular dependencies and removes cyclic links +// 4. Reachability check: Marks routers unreachable from any root as disabled +// 5. Dead-end detection: Marks routers with no service and no children as disabled +// 6. Validation: Checks for configuration errors +// +// Router status is set during this process: +// - Enabled: Reachable routers with valid configuration +// - Disabled: Unreachable, dead-end, or routers with critical errors +// - Warning: Routers with non-critical errors (like cycles) +// +// The function modifies router.Status, router.ChildRefs, and adds errors to router.Err. +func (m *Manager) ParseRouterTree() { + if m.conf == nil || m.conf.Routers == nil { + return } - return chain.Extend(*mHandler).Then(sHandler) + // Populate ChildRefs based on ParentRefs and find root routers. + var rootRouters []string + for routerName, router := range m.conf.Routers { + if len(router.ParentRefs) == 0 { + rootRouters = append(rootRouters, routerName) + continue + } + + for _, parentName := range router.ParentRefs { + if parentRouter, exists := m.conf.Routers[parentName]; exists { + // Add this router as a child of its parent + if !slices.Contains(parentRouter.ChildRefs, routerName) { + parentRouter.ChildRefs = append(parentRouter.ChildRefs, routerName) + } + } else { + router.AddError(fmt.Errorf("parent router %q does not exist", parentName), true) + } + } + + // Check for non-root router with TLS config. + if router.TLS != nil { + router.AddError(errors.New("non-root router cannot have TLS configuration"), true) + continue + } + + // Check for non-root router with Observability config. + if router.Observability != nil { + router.AddError(errors.New("non-root router cannot have Observability configuration"), true) + continue + } + + // Check for non-root router with Entrypoint config. + if len(router.EntryPoints) > 0 { + router.AddError(errors.New("non-root router cannot have Entrypoints configuration"), true) + continue + } + } + sort.Strings(rootRouters) + + // Root-first traversal with cycle detection. + visited := make(map[string]bool) + currentPath := make(map[string]bool) + var path []string + + for _, rootName := range rootRouters { + if !visited[rootName] { + m.traverse(rootName, visited, currentPath, path) + } + } + + for routerName, router := range m.conf.Routers { + // Set status for all routers based on reachability. + if !visited[routerName] { + router.AddError(errors.New("router is not reachable"), true) + continue + } + + // Detect dead-end routers (no service + no children) - AFTER cycle handling. + if router.Service == "" && len(router.ChildRefs) == 0 { + router.AddError(errors.New("router has no service and no child routers"), true) + continue + } + + // Check for router with service that is referenced as a parent. + if router.Service != "" && len(router.ChildRefs) > 0 { + router.AddError(errors.New("router has both a service and is referenced as a parent by other routers"), true) + continue + } + } +} + +// traverse performs a depth-first traversal starting from root routers, +// detecting cycles and marking visited routers for reachability detection. +func (m *Manager) traverse(routerName string, visited, currentPath map[string]bool, path []string) { + if currentPath[routerName] { + // Found a cycle - handle it properly. + m.handleCycle(routerName, path) + return + } + + if visited[routerName] { + return + } + + router, exists := m.conf.Routers[routerName] + // Since the ChildRefs population already guarantees router existence, this check is purely defensive. + if !exists { + visited[routerName] = true + return + } + + visited[routerName] = true + currentPath[routerName] = true + newPath := append(path, routerName) + + // Sort ChildRefs for deterministic behavior. + sortedChildRefs := make([]string, len(router.ChildRefs)) + copy(sortedChildRefs, router.ChildRefs) + sort.Strings(sortedChildRefs) + + // Traverse children. + for _, childName := range sortedChildRefs { + m.traverse(childName, visited, currentPath, newPath) + } + + currentPath[routerName] = false +} + +// handleCycle handles cycle detection and removes the victim from guilty router's ChildRefs. +func (m *Manager) handleCycle(victimRouter string, path []string) { + // Find where the cycle starts in the path + cycleStart := -1 + for i, name := range path { + if name == victimRouter { + cycleStart = i + break + } + } + + if cycleStart < 0 { + return + } + + // Build the cycle path: from cycle start to current + victim. + cyclePath := append(path[cycleStart:], victimRouter) + cycleRouters := strings.Join(cyclePath, " -> ") + + // The guilty router is the last one in the path (the one creating the cycle). + if len(path) > 0 { + guiltyRouterName := path[len(path)-1] + guiltyRouter, exists := m.conf.Routers[guiltyRouterName] + if !exists { + return + } + + // Add cycle error to guilty router. + guiltyRouter.AddError(fmt.Errorf("cyclic reference detected in router tree: %s", cycleRouters), false) + + // Remove victim from guilty router's ChildRefs. + for i, childRef := range guiltyRouter.ChildRefs { + if childRef == victimRouter { + guiltyRouter.ChildRefs = append(guiltyRouter.ChildRefs[:i], guiltyRouter.ChildRefs[i+1:]...) + break + } + } + } +} + +// buildChildRoutersMuxer creates a muxer for child routers. +func (m *Manager) buildChildRoutersMuxer(ctx context.Context, entryPointName string, childRefs []string) (http.Handler, error) { + childMuxer := httpmuxer.NewMuxer(m.parser) + + // Set a default handler for the child muxer (404 Not Found). + childMuxer.SetDefaultHandler(http.NotFoundHandler()) + + childCount := 0 + for _, childName := range childRefs { + childRouter, exists := m.conf.Routers[childName] + if !exists { + return nil, fmt.Errorf("child router %q does not exist", childName) + } + + // Skip child routers with errors. + if len(childRouter.Err) > 0 { + continue + } + + logger := log.Ctx(ctx).With().Str(logs.RouterName, childName).Logger() + ctxChild := logger.WithContext(provider.AddInContext(ctx, childName)) + + // Set priority if not set. + if childRouter.Priority == 0 { + childRouter.Priority = httpmuxer.GetRulePriority(childRouter.Rule) + } + + // Build the child router handler. + childHandler, err := m.buildRouterHandler(ctxChild, entryPointName, childName, childRouter) + if err != nil { + childRouter.AddError(err, true) + logger.Error().Err(err).Send() + continue + } + + // Add the child router to the muxer. + if err = childMuxer.AddRoute(childRouter.Rule, childRouter.RuleSyntax, childRouter.Priority, childHandler); err != nil { + childRouter.AddError(err, true) + logger.Error().Err(err).Send() + continue + } + + childCount++ + } + + // Prevent empty muxer. + if childCount == 0 { + return nil, fmt.Errorf("no child routers could be added to muxer (%d skipped)", len(childRefs)) + } + + return childMuxer, nil } diff --git a/pkg/server/router/router_test.go b/pkg/server/router/router_test.go index 1458b2e99..a10c597b7 100644 --- a/pkg/server/router/router_test.go +++ b/pkg/server/router/router_test.go @@ -1,6 +1,7 @@ package router import ( + "context" "crypto/tls" "io" "math" @@ -11,6 +12,7 @@ import ( "testing" "time" + "github.com/containous/alice" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ptypes "github.com/traefik/paerser/types" @@ -809,30 +811,6 @@ func TestProviderOnMiddlewares(t *testing.T) { assert.Equal(t, []string{"m1@docker", "m2@docker", "m1@file"}, rtConf.Middlewares["chain@docker"].Chain.Middlewares) } -type staticTransportManager struct { - res *http.Response -} - -func (s staticTransportManager) GetRoundTripper(_ string) (http.RoundTripper, error) { - return &staticTransport{res: s.res}, nil -} - -func (s staticTransportManager) GetTLSConfig(_ string) (*tls.Config, error) { - panic("implement me") -} - -func (s staticTransportManager) Get(_ string) (*dynamic.ServersTransport, error) { - panic("implement me") -} - -type staticTransport struct { - res *http.Response -} - -func (t *staticTransport) RoundTrip(_ *http.Request) (*http.Response, error) { - return t.res, nil -} - func BenchmarkRouterServe(b *testing.B) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) @@ -927,6 +905,1106 @@ func BenchmarkService(b *testing.B) { } } +func TestManager_ComputeMultiLayerRouting(t *testing.T) { + testCases := []struct { + desc string + routers map[string]*dynamic.Router + expectedStatuses map[string]string + expectedChildRefs map[string][]string + expectedErrors map[string][]string + expectedErrorCounts map[string]int + }{ + { + desc: "Simple router", + routers: map[string]*dynamic.Router{ + "A": { + Service: "A-service", + }, + }, + expectedStatuses: map[string]string{ + "A": runtime.StatusEnabled, + }, + expectedChildRefs: map[string][]string{ + "A": {}, + }, + }, + { + // A->B1 + // ->B2 + desc: "Router with two children", + routers: map[string]*dynamic.Router{ + "A": {}, + "B1": { + ParentRefs: []string{"A"}, + Service: "B1-service", + }, + "B2": { + ParentRefs: []string{"A"}, + Service: "B2-service", + }, + }, + expectedStatuses: map[string]string{ + "A": runtime.StatusEnabled, + "B1": runtime.StatusEnabled, + "B2": runtime.StatusEnabled, + }, + expectedChildRefs: map[string][]string{ + "A": {"B1", "B2"}, + "B1": nil, + "B2": nil, + }, + }, + { + desc: "Non-root router with TLS config", + routers: map[string]*dynamic.Router{ + "A": {}, + "B": { + ParentRefs: []string{"A"}, + Service: "B-service", + TLS: &dynamic.RouterTLSConfig{}, + }, + }, + expectedStatuses: map[string]string{ + "A": runtime.StatusEnabled, + "B": runtime.StatusDisabled, + }, + expectedChildRefs: map[string][]string{ + "A": {"B"}, + "B": nil, + }, + expectedErrors: map[string][]string{ + "B": {"non-root router cannot have TLS configuration"}, + }, + }, + { + desc: "Non-root router with observability config", + routers: map[string]*dynamic.Router{ + "A": {}, + "B": { + ParentRefs: []string{"A"}, + Service: "B-service", + Observability: &dynamic.RouterObservabilityConfig{}, + }, + }, + expectedStatuses: map[string]string{ + "A": runtime.StatusEnabled, + "B": runtime.StatusDisabled, + }, + expectedChildRefs: map[string][]string{ + "A": {"B"}, + "B": nil, + }, + expectedErrors: map[string][]string{ + "B": {"non-root router cannot have Observability configuration"}, + }, + }, + { + desc: "Non-root router with EntryPoints config", + routers: map[string]*dynamic.Router{ + "A": {}, + "B": { + ParentRefs: []string{"A"}, + Service: "B-service", + EntryPoints: []string{"web"}, + }, + }, + expectedStatuses: map[string]string{ + "A": runtime.StatusEnabled, + "B": runtime.StatusDisabled, + }, + expectedChildRefs: map[string][]string{ + "A": {"B"}, + "B": nil, + }, + expectedErrors: map[string][]string{ + "B": {"non-root router cannot have Entrypoints configuration"}, + }, + }, + + { + desc: "Router with non-existing parent", + routers: map[string]*dynamic.Router{ + "B": { + ParentRefs: []string{"A"}, + Service: "B-service", + }, + }, + expectedStatuses: map[string]string{ + "B": runtime.StatusDisabled, + }, + expectedChildRefs: map[string][]string{ + "B": nil, + }, + expectedErrors: map[string][]string{ + "B": {"parent router \"A\" does not exist", "router is not reachable"}, + }, + }, + { + desc: "Dead-end router with no child and no service", + routers: map[string]*dynamic.Router{ + "A": {}, + }, + expectedStatuses: map[string]string{ + "A": runtime.StatusDisabled, + }, + expectedChildRefs: map[string][]string{ + "A": {}, + }, + expectedErrors: map[string][]string{ + "A": {"router has no service and no child routers"}, + }, + }, + { + // A->B->A + desc: "Router is not reachable", + routers: map[string]*dynamic.Router{ + "A": { + ParentRefs: []string{"B"}, + }, + "B": { + ParentRefs: []string{"A"}, + }, + }, + expectedStatuses: map[string]string{ + "A": runtime.StatusDisabled, + "B": runtime.StatusDisabled, + }, + expectedChildRefs: map[string][]string{ + "A": {"B"}, + "B": {"A"}, + }, + // Cycle detection does not visit unreachable routers (it avoids computing the cycle dependency graph for unreachable routers). + expectedErrors: map[string][]string{ + "A": {"router is not reachable"}, + "B": {"router is not reachable"}, + }, + }, + { + // A->B->C->D->B + desc: "Router creating a cycle is a dead-end and should be disabled", + routers: map[string]*dynamic.Router{ + "A": {}, + "B": { + ParentRefs: []string{"A", "D"}, + }, + "C": { + ParentRefs: []string{"B"}, + }, + "D": { + ParentRefs: []string{"C"}, + }, + }, + expectedStatuses: map[string]string{ + "A": runtime.StatusEnabled, + "B": runtime.StatusEnabled, + "C": runtime.StatusEnabled, + "D": runtime.StatusDisabled, // Dead-end router is disabled, because the cycle error broke the link with B. + }, + expectedChildRefs: map[string][]string{ + "A": {"B"}, + "B": {"C"}, + "C": {"D"}, + "D": {}, + }, + expectedErrors: map[string][]string{ + "D": { + "cyclic reference detected in router tree: B -> C -> D -> B", + "router has no service and no child routers", + }, + }, + }, + { + // A->B->C->D->B + // ->E + desc: "Router creating a cycle A->B->C->D->B but which is referenced elsewhere, must be set to warning status", + routers: map[string]*dynamic.Router{ + "A": {}, + "B": { + ParentRefs: []string{"A", "D"}, + }, + "C": { + ParentRefs: []string{"B"}, + }, + "D": { + ParentRefs: []string{"C"}, + }, + "E": { + ParentRefs: []string{"D"}, + Service: "E-service", + }, + }, + expectedStatuses: map[string]string{ + "A": runtime.StatusEnabled, + "B": runtime.StatusEnabled, + "C": runtime.StatusEnabled, + "D": runtime.StatusWarning, + "E": runtime.StatusEnabled, + }, + expectedChildRefs: map[string][]string{ + "A": {"B"}, + "B": {"C"}, + "C": {"D"}, + "D": {"E"}, + }, + expectedErrors: map[string][]string{ + "D": {"cyclic reference detected in router tree: B -> C -> D -> B"}, + }, + }, + { + desc: "Parent router with all children having errors", + routers: map[string]*dynamic.Router{ + "parent": {}, + "child-a": { + ParentRefs: []string{"parent"}, + Service: "child-a-service", + TLS: &dynamic.RouterTLSConfig{}, // Invalid: non-root cannot have TLS + }, + "child-b": { + ParentRefs: []string{"parent"}, + Service: "child-b-service", + TLS: &dynamic.RouterTLSConfig{}, // Invalid: non-root cannot have TLS + }, + }, + expectedStatuses: map[string]string{ + "parent": runtime.StatusEnabled, // Enabled during ParseRouterTree (no config errors). Would be disabled during handler building when empty muxer is detected. + "child-a": runtime.StatusDisabled, + "child-b": runtime.StatusDisabled, + }, + expectedChildRefs: map[string][]string{ + "parent": {"child-a", "child-b"}, + "child-a": nil, + "child-b": nil, + }, + expectedErrors: map[string][]string{ + "child-a": {"non-root router cannot have TLS configuration"}, + "child-b": {"non-root router cannot have TLS configuration"}, + }, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + // Create runtime routers + runtimeRouters := make(map[string]*runtime.RouterInfo) + for name, router := range test.routers { + runtimeRouters[name] = &runtime.RouterInfo{ + Router: router, + Status: runtime.StatusEnabled, + } + } + + conf := &runtime.Configuration{ + Routers: runtimeRouters, + } + + manager := &Manager{ + conf: conf, + } + + // Execute the function we're testing + manager.ParseRouterTree() + + // Verify ChildRefs are populated correctly + for routerName, expectedChildren := range test.expectedChildRefs { + router := runtimeRouters[routerName] + assert.ElementsMatch(t, expectedChildren, router.ChildRefs) + } + + // Verify statuses are set correctly + var gotStatuses map[string]string + for routerName, router := range runtimeRouters { + if gotStatuses == nil { + gotStatuses = make(map[string]string) + } + gotStatuses[routerName] = router.Status + } + assert.Equal(t, test.expectedStatuses, gotStatuses) + + // Verify errors are added correctly + var gotErrors map[string][]string + for routerName, router := range runtimeRouters { + for _, err := range router.Err { + if gotErrors == nil { + gotErrors = make(map[string][]string) + } + gotErrors[routerName] = append(gotErrors[routerName], err) + } + } + assert.Equal(t, test.expectedErrors, gotErrors) + }) + } +} + +func TestManager_buildChildRoutersMuxer(t *testing.T) { + testCases := []struct { + desc string + childRefs []string + routers map[string]*dynamic.Router + services map[string]*dynamic.Service + middlewares map[string]*dynamic.Middleware + expectedError string + expectedRequests []struct { + path string + statusCode int + } + }{ + { + desc: "simple child router with service", + childRefs: []string{"child1"}, + routers: map[string]*dynamic.Router{ + "child1": { + Rule: "Path(`/api`)", + Service: "child1-service", + }, + }, + services: map[string]*dynamic.Service{ + "child1-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8080"}}, + }, + }, + }, + expectedRequests: []struct { + path string + statusCode int + }{ + {path: "/api", statusCode: http.StatusOK}, + {path: "/unknown", statusCode: http.StatusNotFound}, + }, + }, + { + desc: "multiple child routers with different rules", + childRefs: []string{"child1", "child2"}, + routers: map[string]*dynamic.Router{ + "child1": { + Rule: "Path(`/api`)", + Service: "child1-service", + }, + "child2": { + Rule: "Path(`/web`)", + Service: "child2-service", + }, + }, + services: map[string]*dynamic.Service{ + "child1-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8080"}}, + }, + }, + "child2-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8081"}}, + }, + }, + }, + expectedRequests: []struct { + path string + statusCode int + }{ + {path: "/api", statusCode: http.StatusOK}, + {path: "/web", statusCode: http.StatusOK}, + {path: "/unknown", statusCode: http.StatusNotFound}, + }, + }, + { + desc: "child router with middleware", + childRefs: []string{"child1"}, + routers: map[string]*dynamic.Router{ + "child1": { + Rule: "Path(`/api`)", + Service: "child1-service", + Middlewares: []string{"test-middleware"}, + }, + }, + services: map[string]*dynamic.Service{ + "child1-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8080"}}, + }, + }, + }, + middlewares: map[string]*dynamic.Middleware{ + "test-middleware": { + Headers: &dynamic.Headers{ + CustomRequestHeaders: map[string]string{"X-Test": "value"}, + }, + }, + }, + expectedRequests: []struct { + path string + statusCode int + }{ + {path: "/api", statusCode: http.StatusOK}, + {path: "/unknown", statusCode: http.StatusNotFound}, + }, + }, + { + desc: "nested child routers (child with its own children)", + childRefs: []string{"intermediate"}, + routers: map[string]*dynamic.Router{ + "intermediate": { + Rule: "PathPrefix(`/api`)", + // No service - this will have its own children + }, + "leaf1": { + Rule: "Path(`/api/v1`)", + Service: "leaf1-service", + ParentRefs: []string{"intermediate"}, + }, + "leaf2": { + Rule: "Path(`/api/v2`)", + Service: "leaf2-service", + ParentRefs: []string{"intermediate"}, + }, + }, + services: map[string]*dynamic.Service{ + "leaf1-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8080"}}, + }, + }, + "leaf2-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8081"}}, + }, + }, + }, + expectedRequests: []struct { + path string + statusCode int + }{ + {path: "/api/v1", statusCode: http.StatusOK}, + {path: "/api/v2", statusCode: http.StatusOK}, + {path: "/unknown", statusCode: http.StatusNotFound}, + }, + }, + { + desc: "all child routers have errors - should return error", + childRefs: []string{"child1", "child2"}, + routers: map[string]*dynamic.Router{ + "child1": { + Rule: "Path(`/api`)", + Service: "child1-service", + ParentRefs: []string{"parent"}, + TLS: &dynamic.RouterTLSConfig{}, // Invalid: non-root router cannot have TLS + }, + "child2": { + Rule: "Path(`/web`)", + Service: "child2-service", + ParentRefs: []string{"parent"}, + TLS: &dynamic.RouterTLSConfig{}, // Invalid: non-root router cannot have TLS + }, + }, + services: map[string]*dynamic.Service{ + "child1-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8080"}}, + }, + }, + "child2-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8081"}}, + }, + }, + }, + expectedError: "no child routers could be added to muxer (2 skipped)", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + // Create runtime routers + runtimeRouters := make(map[string]*runtime.RouterInfo) + for name, router := range test.routers { + runtimeRouters[name] = &runtime.RouterInfo{ + Router: router, + } + } + + // Create runtime services + runtimeServices := make(map[string]*runtime.ServiceInfo) + for name, service := range test.services { + runtimeServices[name] = &runtime.ServiceInfo{ + Service: service, + } + } + + // Create runtime middlewares + runtimeMiddlewares := make(map[string]*runtime.MiddlewareInfo) + for name, middleware := range test.middlewares { + runtimeMiddlewares[name] = &runtime.MiddlewareInfo{ + Middleware: middleware, + } + } + + conf := &runtime.Configuration{ + Routers: runtimeRouters, + Services: runtimeServices, + Middlewares: runtimeMiddlewares, + } + + // Set up the manager with mocks + serviceManager := &mockServiceManager{} + middlewareBuilder := &mockMiddlewareBuilder{} + parser, err := httpmuxer.NewSyntaxParser() + require.NoError(t, err) + + manager := NewManager(conf, serviceManager, middlewareBuilder, nil, nil, parser) + + // Compute multi-layer routing to populate ChildRefs + manager.ParseRouterTree() + + // Build the child routers muxer + ctx := t.Context() + muxer, err := manager.buildChildRoutersMuxer(ctx, "test", test.childRefs) + + if test.expectedError != "" { + require.Error(t, err) + assert.Contains(t, err.Error(), test.expectedError) + return + } + + if len(test.childRefs) == 0 { + assert.Error(t, err) + return + } + + require.NoError(t, err) + require.NotNil(t, muxer) + + // Test that the muxer routes requests correctly + for _, req := range test.expectedRequests { + recorder := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, req.path, nil) + muxer.ServeHTTP(recorder, request) + + assert.Equal(t, req.statusCode, recorder.Code, "unexpected status code for path %s", req.path) + } + }) + } +} + +func TestManager_buildHTTPHandler_WithChildRouters(t *testing.T) { + testCases := []struct { + desc string + router *runtime.RouterInfo + childRouters map[string]*dynamic.Router + services map[string]*dynamic.Service + expectedError string + expectedRequests []struct { + path string + statusCode int + } + }{ + { + desc: "router with child routers", + router: &runtime.RouterInfo{ + Router: &dynamic.Router{ + Rule: "PathPrefix(`/api`)", + }, + ChildRefs: []string{"child1", "child2"}, + }, + childRouters: map[string]*dynamic.Router{ + "child1": { + Rule: "Path(`/api/v1`)", + Service: "child1-service", + }, + "child2": { + Rule: "Path(`/api/v2`)", + Service: "child2-service", + }, + }, + services: map[string]*dynamic.Service{ + "child1-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8080"}}, + }, + }, + "child2-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8081"}}, + }, + }, + }, + expectedRequests: []struct { + path string + statusCode int + }{ + {path: "/unknown", statusCode: http.StatusNotFound}, + }, + }, + { + desc: "router with service (normal case)", + router: &runtime.RouterInfo{ + Router: &dynamic.Router{ + Rule: "PathPrefix(`/api`)", + Service: "main-service", + }, + }, + services: map[string]*dynamic.Service{ + "main-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8080"}}, + }, + }, + }, + expectedRequests: []struct { + path string + statusCode int + }{}, + }, + { + desc: "router with neither service nor child routers - error", + router: &runtime.RouterInfo{ + Router: &dynamic.Router{ + Rule: "PathPrefix(`/api`)", + }, + }, + expectedError: "router must have either a service or child routers", + }, + { + desc: "router with child routers but missing child - error", + router: &runtime.RouterInfo{ + Router: &dynamic.Router{ + Rule: "PathPrefix(`/api`)", + }, + ChildRefs: []string{"nonexistent"}, + }, + expectedError: "child router \"nonexistent\" does not exist", + }, + { + desc: "router with all children having errors - returns empty muxer error", + router: &runtime.RouterInfo{ + Router: &dynamic.Router{ + Rule: "PathPrefix(`/api`)", + }, + ChildRefs: []string{"child1", "child2"}, + }, + childRouters: map[string]*dynamic.Router{ + "child1": { + Rule: "Path(`/api/v1`)", + Service: "child1-service", + ParentRefs: []string{"parent"}, + TLS: &dynamic.RouterTLSConfig{}, // Invalid for non-root + }, + "child2": { + Rule: "Path(`/api/v2`)", + Service: "child2-service", + ParentRefs: []string{"parent"}, + TLS: &dynamic.RouterTLSConfig{}, // Invalid for non-root + }, + }, + services: map[string]*dynamic.Service{ + "child1-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8080"}}, + }, + }, + "child2-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8081"}}, + }, + }, + }, + expectedError: "no child routers could be added to muxer (2 skipped)", + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + // Create runtime routers + runtimeRouters := make(map[string]*runtime.RouterInfo) + runtimeRouters["test-router"] = test.router + for name, router := range test.childRouters { + runtimeRouters[name] = &runtime.RouterInfo{ + Router: router, + } + } + + // Create runtime services + runtimeServices := make(map[string]*runtime.ServiceInfo) + for name, service := range test.services { + runtimeServices[name] = &runtime.ServiceInfo{ + Service: service, + } + } + + conf := &runtime.Configuration{ + Routers: runtimeRouters, + Services: runtimeServices, + } + + // Set up the manager with mocks + serviceManager := &mockServiceManager{} + middlewareBuilder := &mockMiddlewareBuilder{} + parser, err := httpmuxer.NewSyntaxParser() + require.NoError(t, err) + + manager := NewManager(conf, serviceManager, middlewareBuilder, nil, nil, parser) + + // Run ParseRouterTree to validate configuration and populate ChildRefs/errors + manager.ParseRouterTree() + + // Build the HTTP handler + ctx := t.Context() + handler, err := manager.buildHTTPHandler(ctx, test.router, "test", "test-router") + + if test.expectedError != "" { + assert.Error(t, err) + assert.Contains(t, err.Error(), test.expectedError) + return + } + + require.NoError(t, err) + require.NotNil(t, handler) + + // Test that the handler routes requests correctly + for _, req := range test.expectedRequests { + recorder := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, req.path, nil) + handler.ServeHTTP(recorder, request) + + assert.Equal(t, req.statusCode, recorder.Code, "unexpected status code for path %s", req.path) + } + }) + } +} + +func TestManager_BuildHandlers_WithChildRouters(t *testing.T) { + testCases := []struct { + desc string + routers map[string]*dynamic.Router + services map[string]*dynamic.Service + expectedRequests []struct { + path string + statusCode int + } + }{ + { + desc: "parent router with child routers", + routers: map[string]*dynamic.Router{ + "parent": { + EntryPoints: []string{"web"}, + Rule: "PathPrefix(`/api`)", + }, + "child1": { + Rule: "Path(`/api/v1`)", + Service: "child1-service", + ParentRefs: []string{"parent"}, + }, + "child2": { + Rule: "Path(`/api/v2`)", + Service: "child2-service", + ParentRefs: []string{"parent"}, + }, + }, + services: map[string]*dynamic.Service{ + "child1-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8080"}}, + }, + }, + "child2-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8081"}}, + }, + }, + }, + expectedRequests: []struct { + path string + statusCode int + }{ + {path: "/unknown", statusCode: http.StatusNotFound}, + }, + }, + { + desc: "multiple parent routers with children", + routers: map[string]*dynamic.Router{ + "api-parent": { + EntryPoints: []string{"web"}, + Rule: "PathPrefix(`/api`)", + }, + "web-parent": { + EntryPoints: []string{"web"}, + Rule: "PathPrefix(`/web`)", + }, + "api-child": { + Rule: "Path(`/api/v1`)", + Service: "api-service", + ParentRefs: []string{"api-parent"}, + }, + "web-child": { + Rule: "Path(`/web/index`)", + Service: "web-service", + ParentRefs: []string{"web-parent"}, + }, + }, + services: map[string]*dynamic.Service{ + "api-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8080"}}, + }, + }, + "web-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8081"}}, + }, + }, + }, + expectedRequests: []struct { + path string + statusCode int + }{}, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + // Create runtime routers + runtimeRouters := make(map[string]*runtime.RouterInfo) + for name, router := range test.routers { + runtimeRouters[name] = &runtime.RouterInfo{ + Router: router, + } + } + + // Create runtime services + runtimeServices := make(map[string]*runtime.ServiceInfo) + for name, service := range test.services { + runtimeServices[name] = &runtime.ServiceInfo{ + Service: service, + } + } + + conf := &runtime.Configuration{ + Routers: runtimeRouters, + Services: runtimeServices, + } + + // Set up the manager with mocks + serviceManager := &mockServiceManager{} + middlewareBuilder := &mockMiddlewareBuilder{} + parser, err := httpmuxer.NewSyntaxParser() + require.NoError(t, err) + + manager := NewManager(conf, serviceManager, middlewareBuilder, nil, nil, parser) + + // Compute multi-layer routing to set up parent-child relationships + manager.ParseRouterTree() + + // Build handlers + ctx := t.Context() + handlers := manager.BuildHandlers(ctx, []string{"web"}, false) + + handler := handlers["web"] + require.NotNil(t, handler) + + // Test that the handler routes requests correctly + for _, req := range test.expectedRequests { + recorder := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, req.path, nil) + request.Host = "test.com" + handler.ServeHTTP(recorder, request) + + assert.Equal(t, req.statusCode, recorder.Code, "unexpected status code for path %s", req.path) + } + }) + } +} + +func TestManager_BuildHandlers_Deny(t *testing.T) { + testCases := []struct { + desc string + routers map[string]*dynamic.Router + services map[string]*dynamic.Service + requestPath string + expectedStatusCode int + }{ + { + desc: "parent router without child routers, request with encoded slash", + requestPath: "/foo%2F", + routers: map[string]*dynamic.Router{ + "parent": { + EntryPoints: []string{"web"}, + Rule: "PathPrefix(`/`)", + Service: "service", + }, + }, + services: map[string]*dynamic.Service{ + "service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8080"}}, + }, + }, + }, + expectedStatusCode: http.StatusOK, + }, + { + desc: "parent router with child routers, request with encoded slash", + requestPath: "/foo%2F", + routers: map[string]*dynamic.Router{ + "parent": { + EntryPoints: []string{"web"}, + Rule: "PathPrefix(`/`)", + }, + "child1": { + Rule: "PathPrefix(`/`)", + Service: "child1-service", + ParentRefs: []string{"parent"}, + }, + }, + services: map[string]*dynamic.Service{ + "child1-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8080"}}, + }, + }, + }, + expectedStatusCode: http.StatusOK, + }, + { + desc: "parent router disallowing encoded slash without child router", + requestPath: "/foo%2F", + routers: map[string]*dynamic.Router{ + "parent": { + EntryPoints: []string{"web"}, + Rule: "PathPrefix(`/`)", + Service: "service", + DeniedEncodedPathCharacters: &dynamic.RouterDeniedEncodedPathCharacters{ + AllowEncodedSlash: false, + }, + }, + }, + services: map[string]*dynamic.Service{ + "service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8080"}}, + }, + }, + }, + expectedStatusCode: http.StatusBadRequest, + }, + { + desc: "parent router disallowing encoded slash with child routers", + requestPath: "/foo%2F", + routers: map[string]*dynamic.Router{ + "parent": { + EntryPoints: []string{"web"}, + Rule: "PathPrefix(`/`)", + DeniedEncodedPathCharacters: &dynamic.RouterDeniedEncodedPathCharacters{ + AllowEncodedSlash: false, + }, + }, + "child1": { + Rule: "PathPrefix(`/`)", + Service: "child1-service", + ParentRefs: []string{"parent"}, + }, + }, + services: map[string]*dynamic.Service{ + "child1-service": { + LoadBalancer: &dynamic.ServersLoadBalancer{ + Servers: []dynamic.Server{{URL: "http://localhost:8080"}}, + }, + }, + }, + expectedStatusCode: http.StatusBadRequest, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + // Create runtime routers + runtimeRouters := make(map[string]*runtime.RouterInfo) + for name, router := range test.routers { + runtimeRouters[name] = &runtime.RouterInfo{ + Router: router, + } + } + + // Create runtime services + runtimeServices := make(map[string]*runtime.ServiceInfo) + for name, service := range test.services { + runtimeServices[name] = &runtime.ServiceInfo{ + Service: service, + } + } + + conf := &runtime.Configuration{ + Routers: runtimeRouters, + Services: runtimeServices, + } + + // Set up the manager with mocks + serviceManager := &mockServiceManager{} + middlewareBuilder := &mockMiddlewareBuilder{} + + parser, err := httpmuxer.NewSyntaxParser() + require.NoError(t, err) + + manager := NewManager(conf, serviceManager, middlewareBuilder, nil, nil, parser) + + // Compute multi-layer routing to set up parent-child relationships + manager.ParseRouterTree() + + // Build handlers + ctx := t.Context() + handlers := manager.BuildHandlers(ctx, []string{"web"}, false) + + recorder := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, test.requestPath, http.NoBody) + + handlers["web"].ServeHTTP(recorder, request) + + assert.Equal(t, test.expectedStatusCode, recorder.Code) + }) + } +} + +// Mock implementations for testing + +type staticTransportManager struct { + res *http.Response +} + +func (s staticTransportManager) GetRoundTripper(_ string) (http.RoundTripper, error) { + return &staticTransport{res: s.res}, nil +} + +func (s staticTransportManager) GetTLSConfig(_ string) (*tls.Config, error) { + panic("implement me") +} + +func (s staticTransportManager) Get(_ string) (*dynamic.ServersTransport, error) { + panic("implement me") +} + +type staticTransport struct { + res *http.Response +} + +func (t *staticTransport) RoundTrip(_ *http.Request) (*http.Response, error) { + return t.res, nil +} + +type mockServiceManager struct{} + +func (m *mockServiceManager) BuildHTTP(_ context.Context, _ string) (http.Handler, error) { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte("mock service response")) + }), nil +} + +func (m *mockServiceManager) LaunchHealthCheck(_ context.Context) {} + +type mockMiddlewareBuilder struct{} + +func (m *mockMiddlewareBuilder) BuildChain(_ context.Context, _ []string) *alice.Chain { + chain := alice.New() + return &chain +} + type proxyBuilderMock struct{} func (p proxyBuilderMock) Build(_ string, _ *url.URL, _, _ bool, _ time.Duration) (http.Handler, error) { diff --git a/pkg/server/router/tcp/router.go b/pkg/server/router/tcp/router.go index 0f5c8f843..283f1364c 100644 --- a/pkg/server/router/tcp/router.go +++ b/pkg/server/router/tcp/router.go @@ -3,6 +3,7 @@ package tcp import ( "bufio" "bytes" + "context" "crypto/tls" "errors" "io" @@ -222,7 +223,17 @@ func (r *Router) acmeTLSALPNHandler() tcp.Handler { } return tcp.HandlerFunc(func(conn tcp.WriteCloser) { - _ = tls.Server(conn, r.httpsTLSConfig).Handshake() + tlsConn := tls.Server(conn, r.httpsTLSConfig) + defer tlsConn.Close() + + // This avoids stale connections when validating the ACME challenge, + // as we expect a validation request to complete in a short period of time. + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + if err := tlsConn.HandshakeContext(ctx); err != nil { + log.Debug().Err(err).Msg("Error during ACME-TLS/1 handshake") + } }) } diff --git a/pkg/server/router/tcp/router_test.go b/pkg/server/router/tcp/router_test.go index 1c2875776..768a78ea9 100644 --- a/pkg/server/router/tcp/router_test.go +++ b/pkg/server/router/tcp/router_test.go @@ -697,6 +697,64 @@ func Test_Routing(t *testing.T) { } } +func Test_Router_acmeTLSALPNHandlerTimeout(t *testing.T) { + router, err := NewRouter() + require.NoError(t, err) + + router.httpsTLSConfig = &tls.Config{} + + listener, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + + acceptCh := make(chan struct{}, 1) + go func() { + close(acceptCh) + + conn, err := listener.Accept() + require.NoError(t, err) + + defer listener.Close() + + router.acmeTLSALPNHandler(). + ServeTCP(conn.(*net.TCPConn)) + }() + + <-acceptCh + + conn, err := net.DialTimeout("tcp", listener.Addr().String(), 2*time.Second) + require.NoError(t, err) + + // This is a minimal truncated Client Hello message + // to simulate a hanging connection during TLS handshake. + clientHello := []byte{ + // TLS Record Header + 0x16, // Content Type: Handshake + 0x03, 0x01, // Version: TLS 1.0 (for compatibility) + 0x00, 0x50, // Length: 80 bytes + } + + _, err = conn.Write(clientHello) + require.NoError(t, err) + + errCh := make(chan error, 1) + go func() { + // This will return an EOF as the acmeTLSALPNHandler will close the connection + // after a timeout during the TLS handshake. + b := make([]byte, 256) + _, err = conn.Read(b) + + errCh <- err + }() + + select { + case err := <-errCh: + assert.ErrorIs(t, err, io.EOF) + + case <-time.After(3 * time.Second): + t.Fatal("Error: Timeout waiting for acmeTLSALPNHandler to close the connection") + } +} + // routerTCPCatchAll configures a TCP CatchAll No TLS - HostSNI(`*`) router. func routerTCPCatchAll(conf *runtime.Configuration) { conf.TCPRouters["tcp-catchall"] = &runtime.TCPRouterInfo{ diff --git a/pkg/server/routerfactory.go b/pkg/server/routerfactory.go index f4e0a1102..698de82b4 100644 --- a/pkg/server/routerfactory.go +++ b/pkg/server/routerfactory.go @@ -23,8 +23,9 @@ import ( // RouterFactory the factory of TCP/UDP routers. type RouterFactory struct { - entryPointsTCP []string - entryPointsUDP []string + entryPointsTCP []string + entryPointsUDP []string + allowACMEByPass map[string]bool managerFactory *service.ManagerFactory @@ -105,6 +106,8 @@ func (f *RouterFactory) CreateRouters(rtConf *runtime.Configuration) (map[string routerManager := router.NewManager(rtConf, serviceManager, middlewaresBuilder, f.observabilityMgr, f.tlsManager, f.parser) + routerManager.ParseRouterTree() + handlersNonTLS := routerManager.BuildHandlers(ctx, f.entryPointsTCP, false) handlersTLS := routerManager.BuildHandlers(ctx, f.entryPointsTCP, true) @@ -124,6 +127,8 @@ func (f *RouterFactory) CreateRouters(rtConf *runtime.Configuration) (map[string } } + svcTCPManager.LaunchHealthCheck(ctx) + // UDP svcUDPManager := udpsvc.NewManager(rtConf) rtUDPManager := udprouter.NewManager(rtConf, svcUDPManager) diff --git a/pkg/server/server_entrypoint_tcp.go b/pkg/server/server_entrypoint_tcp.go index aef3954bf..71f5fc2b3 100644 --- a/pkg/server/server_entrypoint_tcp.go +++ b/pkg/server/server_entrypoint_tcp.go @@ -631,6 +631,12 @@ func newHTTPServer(ctx context.Context, ln net.Listener, configuration *static.E if configuration.HTTP2.MaxConcurrentStreams < 0 { return nil, errors.New("max concurrent streams value must be greater than or equal to zero") } + if configuration.HTTP2.MaxDecoderHeaderTableSize < 0 { + return nil, errors.New("max decoder header table size value must be greater than or equal to zero") + } + if configuration.HTTP2.MaxEncoderHeaderTableSize < 0 { + return nil, errors.New("max encoder header table size value must be greater than or equal to zero") + } httpSwitcher := middlewares.NewHandlerSwitcher(http.NotFoundHandler()) @@ -688,7 +694,9 @@ func newHTTPServer(ctx context.Context, ln net.Listener, configuration *static.E IdleTimeout: time.Duration(configuration.Transport.RespondingTimeouts.IdleTimeout), MaxHeaderBytes: configuration.HTTP.MaxHeaderBytes, HTTP2: &http.HTTP2Config{ - MaxConcurrentStreams: int(configuration.HTTP2.MaxConcurrentStreams), + MaxConcurrentStreams: int(configuration.HTTP2.MaxConcurrentStreams), + MaxDecoderHeaderTableSize: int(configuration.HTTP2.MaxDecoderHeaderTableSize), + MaxEncoderHeaderTableSize: int(configuration.HTTP2.MaxEncoderHeaderTableSize), }, } if debugConnection || (configuration.Transport != nil && (configuration.Transport.KeepAliveMaxTime > 0 || configuration.Transport.KeepAliveMaxRequests > 0)) { @@ -759,6 +767,24 @@ func (t *trackedConnection) Close() error { return t.WriteCloser.Close() } +// denyFragment rejects the request if the URL path contains a fragment (hash character). +// When go receives an HTTP request, it assumes the absence of fragment URL. +// However, it is still possible to send a fragment in the request. +// In this case, Traefik will encode the '#' character, altering the request's intended meaning. +// To avoid this behavior, the following function rejects requests that include a fragment in the URL. +func denyFragment(h http.Handler) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + if strings.Contains(req.URL.RawPath, "#") { + log.Debug().Msgf("Rejecting request because it contains a fragment in the URL path: %s", req.URL.RawPath) + rw.WriteHeader(http.StatusBadRequest) + + return + } + + h.ServeHTTP(rw, req) + }) +} + // This function is inspired by http.AllowQuerySemicolons. func encodeQuerySemicolons(h http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { @@ -779,23 +805,6 @@ func encodeQuerySemicolons(h http.Handler) http.Handler { }) } -// When go receives an HTTP request, it assumes the absence of fragment URL. -// However, it is still possible to send a fragment in the request. -// In this case, Traefik will encode the '#' character, altering the request's intended meaning. -// To avoid this behavior, the following function rejects requests that include a fragment in the URL. -func denyFragment(h http.Handler) http.Handler { - return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - if strings.Contains(req.URL.RawPath, "#") { - log.Debug().Msgf("Rejecting request because it contains a fragment in the URL path: %s", req.URL.RawPath) - rw.WriteHeader(http.StatusBadRequest) - - return - } - - h.ServeHTTP(rw, req) - }) -} - // sanitizePath removes the "..", "." and duplicate slash segments from the URL according to https://datatracker.ietf.org/doc/html/rfc3986#section-6.2.2.3. // It cleans the request URL Path and RawPath, and updates the request URI. func sanitizePath(h http.Handler) http.Handler { diff --git a/pkg/server/server_entrypoint_tcp_test.go b/pkg/server/server_entrypoint_tcp_test.go index 8170234a7..7e3e0b112 100644 --- a/pkg/server/server_entrypoint_tcp_test.go +++ b/pkg/server/server_entrypoint_tcp_test.go @@ -387,6 +387,42 @@ func TestKeepAliveH2c(t *testing.T) { require.Contains(t, err.Error(), "use of closed network connection") } +func Test_denyFragment(t *testing.T) { + tests := []struct { + name string + url string + wantStatus int + }{ + { + name: "Rejects fragment character", + url: "http://example.com/#", + wantStatus: http.StatusBadRequest, + }, + { + name: "Allows without fragment", + url: "http://example.com/", + wantStatus: http.StatusOK, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + t.Parallel() + + handler := denyFragment(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + })) + + req := httptest.NewRequest(http.MethodGet, test.url, nil) + res := httptest.NewRecorder() + + handler.ServeHTTP(res, req) + + assert.Equal(t, test.wantStatus, res.Code) + }) + } +} + func TestSanitizePath(t *testing.T) { tests := []struct { path string @@ -525,6 +561,12 @@ func TestPathOperations(t *testing.T) { configuration := &static.EntryPoint{} configuration.SetDefaults() + // We need to allow some of the suspicious encoded characters to test the path operations in case they are authorized. + configuration.HTTP.EncodedCharacters = &static.EncodedCharacters{ + AllowEncodedSlash: true, + AllowEncodedPercent: true, + } + // Create the HTTP server using newHTTPServer. server, err := newHTTPServer(t.Context(), ln, configuration, false, requestdecorator.New(nil)) require.NoError(t, err) @@ -648,3 +690,34 @@ func TestPathOperations(t *testing.T) { }) } } + +func TestHTTP2Config(t *testing.T) { + expectedMaxConcurrentStreams := 42 + expectedEncoderTableSize := 128 + expectedDecoderTableSize := 256 + + // Create a listener for the server. + ln, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + t.Cleanup(func() { + _ = ln.Close() + }) + + // Define the server configuration. + configuration := &static.EntryPoint{} + configuration.SetDefaults() + configuration.HTTP2.MaxConcurrentStreams = int32(expectedMaxConcurrentStreams) + configuration.HTTP2.MaxEncoderHeaderTableSize = int32(expectedEncoderTableSize) + configuration.HTTP2.MaxDecoderHeaderTableSize = int32(expectedDecoderTableSize) + + // Create the HTTP server using newHTTPServer. + server, err := newHTTPServer(t.Context(), ln, configuration, false, requestdecorator.New(nil)) + require.NoError(t, err) + + // Get the underlying HTTP Server. + httpServer := server.Server.(*http.Server) + + assert.Equal(t, expectedMaxConcurrentStreams, httpServer.HTTP2.MaxConcurrentStreams) + assert.Equal(t, expectedEncoderTableSize, httpServer.HTTP2.MaxEncoderHeaderTableSize) + assert.Equal(t, expectedDecoderTableSize, httpServer.HTTP2.MaxDecoderHeaderTableSize) +} diff --git a/pkg/server/service/loadbalancer/hrw/hrw.go b/pkg/server/service/loadbalancer/hrw/hrw.go new file mode 100644 index 000000000..51ca397fd --- /dev/null +++ b/pkg/server/service/loadbalancer/hrw/hrw.go @@ -0,0 +1,201 @@ +package hrw + +import ( + "context" + "errors" + "hash/fnv" + "math" + "net/http" + "sync" + + "github.com/rs/zerolog/log" + "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/ip" +) + +var errNoAvailableServer = errors.New("no available server") + +type namedHandler struct { + http.Handler + name string + weight float64 +} + +// Balancer implements the Rendezvous Hashing algorithm for load balancing. +// The idea is to compute a score for each available backend using a hash of the client's +// source (for example, IP) combined with the backend's identifier, and assign the client +// to the backend with the highest score. This ensures that each client consistently +// connects to the same backend while distributing load evenly across all backends. +type Balancer struct { + wantsHealthCheck bool + + strategy ip.RemoteAddrStrategy + + handlersMu sync.RWMutex + // References all the handlers by name and also by the hashed value of the name. + handlers []*namedHandler + // status is a record of which child services of the Balancer are healthy, keyed + // by name of child service. A service is initially added to the map when it is + // created via Add, and it is later removed or added to the map as needed, + // through the SetStatus method. + status map[string]struct{} + // updaters is the list of hooks that are run (to update the Balancer + // parent(s)), whenever the Balancer status changes. + // No mutex is needed, as it is modified only during the configuration build. + updaters []func(bool) + // fenced is the list of terminating yet still serving child services. + fenced map[string]struct{} +} + +// New creates a new load balancer. +func New(wantHealthCheck bool) *Balancer { + balancer := &Balancer{ + status: make(map[string]struct{}), + fenced: make(map[string]struct{}), + wantsHealthCheck: wantHealthCheck, + strategy: ip.RemoteAddrStrategy{}, + } + + return balancer +} + +// getNodeScore calculates the score of the couple of src and handler name. +func getNodeScore(handler *namedHandler, src string) float64 { + h := fnv.New64a() + h.Write([]byte(src + handler.name)) + sum := h.Sum64() + score := float64(sum) / math.Pow(2, 64) + logScore := 1.0 / -math.Log(score) + + return logScore * handler.weight +} + +// SetStatus sets on the balancer that its given child is now of the given +// status. balancerName is only needed for logging purposes. +func (b *Balancer) SetStatus(ctx context.Context, childName string, up bool) { + b.handlersMu.Lock() + defer b.handlersMu.Unlock() + + upBefore := len(b.status) > 0 + + status := "DOWN" + if up { + status = "UP" + } + + log.Ctx(ctx).Debug().Msgf("Setting status of %s to %v", childName, status) + + if up { + b.status[childName] = struct{}{} + } else { + delete(b.status, childName) + } + + upAfter := len(b.status) > 0 + status = "DOWN" + if upAfter { + status = "UP" + } + + // No Status Change + if upBefore == upAfter { + // We're still with the same status, no need to propagate + log.Ctx(ctx).Debug().Msgf("Still %s, no need to propagate", status) + return + } + + // Status Change + log.Ctx(ctx).Debug().Msgf("Propagating new %s status", status) + for _, fn := range b.updaters { + fn(upAfter) + } +} + +// RegisterStatusUpdater adds fn to the list of hooks that are run when the +// status of the Balancer changes. +// Not thread safe. +func (b *Balancer) RegisterStatusUpdater(fn func(up bool)) error { + if !b.wantsHealthCheck { + return errors.New("healthCheck not enabled in config for this HRW service") + } + b.updaters = append(b.updaters, fn) + + return nil +} + +func (b *Balancer) nextServer(ip string) (*namedHandler, error) { + b.handlersMu.RLock() + var healthy []*namedHandler + for _, h := range b.handlers { + if _, ok := b.status[h.name]; ok { + if _, fenced := b.fenced[h.name]; !fenced { + healthy = append(healthy, h) + } + } + } + b.handlersMu.RUnlock() + + if len(healthy) == 0 { + return nil, errNoAvailableServer + } + + var handler *namedHandler + score := 0.0 + for _, h := range healthy { + s := getNodeScore(h, ip) + if s > score { + handler = h + score = s + } + } + + log.Debug().Msgf("Service selected by HRW: %s", handler.name) + + return handler, nil +} + +func (b *Balancer) ServeHTTP(w http.ResponseWriter, req *http.Request) { + // give ip fetched to b.nextServer + clientIP := b.strategy.GetIP(req) + log.Debug().Msgf("ServeHTTP() clientIP=%s", clientIP) + + server, err := b.nextServer(clientIP) + if err != nil { + if errors.Is(err, errNoAvailableServer) { + http.Error(w, errNoAvailableServer.Error(), http.StatusServiceUnavailable) + } else { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + return + } + + server.ServeHTTP(w, req) +} + +// AddServer adds a handler with a server. +func (b *Balancer) AddServer(name string, handler http.Handler, server dynamic.Server) { + b.Add(name, handler, server.Weight, server.Fenced) +} + +// Add adds a handler. +// A handler with a non-positive weight is ignored. +func (b *Balancer) Add(name string, handler http.Handler, weight *int, fenced bool) { + w := 1 + if weight != nil { + w = *weight + } + + if w <= 0 { // non-positive weight is meaningless + return + } + + h := &namedHandler{Handler: handler, name: name, weight: float64(w)} + + b.handlersMu.Lock() + b.handlers = append(b.handlers, h) + b.status[name] = struct{}{} + if fenced { + b.fenced[name] = struct{}{} + } + b.handlersMu.Unlock() +} diff --git a/pkg/server/service/loadbalancer/hrw/hrw_test.go b/pkg/server/service/loadbalancer/hrw/hrw_test.go new file mode 100644 index 000000000..a843fdb3d --- /dev/null +++ b/pkg/server/service/loadbalancer/hrw/hrw_test.go @@ -0,0 +1,309 @@ +package hrw + +import ( + "context" + "encoding/binary" + "math/rand" + "net" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +// newTestRand creates a deterministic random source for reproducible tests. +func newTestRand() *rand.Rand { + return rand.New(rand.NewSource(12345)) +} + +// genIPAddress generate randomly an IP address as a string. +func genIPAddress(rng *rand.Rand) string { + buf := make([]byte, 4) + + ip := rng.Uint32() + + binary.LittleEndian.PutUint32(buf, ip) + ipStr := net.IP(buf) + return ipStr.String() +} + +// initStatusArray initialize an array filled with status value for test assertions. +func initStatusArray(size int, value int) []int { + status := make([]int, 0, size) + for i := 1; i <= size; i++ { + status = append(status, value) + } + return status +} + +// Tests evaluate load balancing of single and multiple clients. +// Due to the randomness of IP Adresses, repartition between services is not perfect +// The tests validate repartition using a margin of 10% of the number of requests + +func TestBalancer(t *testing.T) { + rng := newTestRand() + balancer := New(false) + + balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), Int(4), false) + + balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "second") + rw.WriteHeader(http.StatusOK) + }), Int(1), false) + + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + req := httptest.NewRequest(http.MethodGet, "/", nil) + for range 100 { + req.RemoteAddr = genIPAddress(rng) + balancer.ServeHTTP(recorder, req) + } + assert.InDelta(t, 80, recorder.save["first"], 10) + assert.InDelta(t, 20, recorder.save["second"], 10) +} + +func TestBalancerNoService(t *testing.T) { + balancer := New(false) + + recorder := httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + + assert.Equal(t, http.StatusServiceUnavailable, recorder.Result().StatusCode) +} + +func TestBalancerOneServerZeroWeight(t *testing.T) { + balancer := New(false) + + balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), Int(1), false) + + balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), Int(0), false) + + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + for range 3 { + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + + assert.Equal(t, 3, recorder.save["first"]) +} + +type key string + +const serviceName key = "serviceName" + +func TestBalancerNoServiceUp(t *testing.T) { + balancer := New(false) + + balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.WriteHeader(http.StatusInternalServerError) + }), Int(1), false) + + balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.WriteHeader(http.StatusInternalServerError) + }), Int(1), false) + + balancer.SetStatus(context.WithValue(t.Context(), serviceName, "parent"), "first", false) + balancer.SetStatus(context.WithValue(t.Context(), serviceName, "parent"), "second", false) + + recorder := httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + + assert.Equal(t, http.StatusServiceUnavailable, recorder.Result().StatusCode) +} + +func TestBalancerOneServerDown(t *testing.T) { + balancer := New(false) + + balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), Int(1), false) + + balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.WriteHeader(http.StatusInternalServerError) + }), Int(1), false) + balancer.SetStatus(context.WithValue(t.Context(), serviceName, "parent"), "second", false) + + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + for range 3 { + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + + assert.Equal(t, 3, recorder.save["first"]) +} + +func TestBalancerDownThenUp(t *testing.T) { + rng := newTestRand() + balancer := New(false) + + balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), Int(1), false) + + balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "second") + rw.WriteHeader(http.StatusOK) + }), Int(1), false) + balancer.SetStatus(context.WithValue(t.Context(), serviceName, "parent"), "second", false) + + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + for range 3 { + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + assert.Equal(t, 3, recorder.save["first"]) + + balancer.SetStatus(context.WithValue(t.Context(), serviceName, "parent"), "second", true) + recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + req := httptest.NewRequest(http.MethodGet, "/", nil) + for range 100 { + req.RemoteAddr = genIPAddress(rng) + balancer.ServeHTTP(recorder, req) + } + assert.InDelta(t, 50, recorder.save["first"], 10) + assert.InDelta(t, 50, recorder.save["second"], 10) +} + +func TestBalancerPropagate(t *testing.T) { + rng := newTestRand() + balancer1 := New(true) + + balancer1.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), Int(1), false) + balancer1.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "second") + rw.WriteHeader(http.StatusOK) + }), Int(1), false) + + balancer2 := New(true) + balancer2.Add("third", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "third") + rw.WriteHeader(http.StatusOK) + }), Int(1), false) + balancer2.Add("fourth", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "fourth") + rw.WriteHeader(http.StatusOK) + }), Int(1), false) + + topBalancer := New(true) + topBalancer.Add("balancer1", balancer1, Int(1), false) + _ = balancer1.RegisterStatusUpdater(func(up bool) { + topBalancer.SetStatus(context.WithValue(t.Context(), serviceName, "top"), "balancer1", up) + }) + topBalancer.Add("balancer2", balancer2, Int(1), false) + _ = balancer2.RegisterStatusUpdater(func(up bool) { + topBalancer.SetStatus(context.WithValue(t.Context(), serviceName, "top"), "balancer2", up) + }) + + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + req := httptest.NewRequest(http.MethodGet, "/", nil) + for range 100 { + req.RemoteAddr = genIPAddress(rng) + topBalancer.ServeHTTP(recorder, req) + } + assert.InDelta(t, 25, recorder.save["first"], 10) + assert.InDelta(t, 25, recorder.save["second"], 10) + assert.InDelta(t, 25, recorder.save["third"], 10) + assert.InDelta(t, 25, recorder.save["fourth"], 10) + wantStatus := initStatusArray(100, 200) + assert.Equal(t, wantStatus, recorder.status) + + // fourth gets downed, but balancer2 still up since third is still up. + balancer2.SetStatus(context.WithValue(t.Context(), serviceName, "top"), "fourth", false) + recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + req = httptest.NewRequest(http.MethodGet, "/", nil) + for range 100 { + req.RemoteAddr = genIPAddress(rng) + topBalancer.ServeHTTP(recorder, req) + } + assert.InDelta(t, 25, recorder.save["first"], 10) + assert.InDelta(t, 25, recorder.save["second"], 10) + assert.InDelta(t, 50, recorder.save["third"], 10) + assert.InDelta(t, 0, recorder.save["fourth"], 0) + wantStatus = initStatusArray(100, 200) + assert.Equal(t, wantStatus, recorder.status) + + // third gets downed, and the propagation triggers balancer2 to be marked as + // down as well for topBalancer. + balancer2.SetStatus(context.WithValue(t.Context(), serviceName, "top"), "third", false) + recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + req = httptest.NewRequest(http.MethodGet, "/", nil) + for range 100 { + req.RemoteAddr = genIPAddress(rng) + topBalancer.ServeHTTP(recorder, req) + } + assert.InDelta(t, 50, recorder.save["first"], 10) + assert.InDelta(t, 50, recorder.save["second"], 10) + assert.InDelta(t, 0, recorder.save["third"], 0) + assert.InDelta(t, 0, recorder.save["fourth"], 0) + wantStatus = initStatusArray(100, 200) + assert.Equal(t, wantStatus, recorder.status) +} + +func TestBalancerAllServersZeroWeight(t *testing.T) { + balancer := New(false) + + balancer.Add("test", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), Int(0), false) + balancer.Add("test2", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), Int(0), false) + + recorder := httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + + assert.Equal(t, http.StatusServiceUnavailable, recorder.Result().StatusCode) +} + +func TestSticky(t *testing.T) { + rng := newTestRand() + balancer := New(false) + + balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), Int(1), false) + + balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "second") + rw.WriteHeader(http.StatusOK) + }), Int(2), false) + + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.RemoteAddr = genIPAddress(rng) + for range 10 { + for _, cookie := range recorder.Result().Cookies() { + req.AddCookie(cookie) + } + recorder.ResponseRecorder = httptest.NewRecorder() + + balancer.ServeHTTP(recorder, req) + } + + assert.True(t, recorder.save["first"] == 0 || recorder.save["first"] == 10) + assert.True(t, recorder.save["second"] == 0 || recorder.save["second"] == 10) + // from one IP, the choice between server must be the same for the 10 requests + // weight does not impose what would be chosen from 1 client +} + +func Int(v int) *int { return &v } + +type responseRecorder struct { + *httptest.ResponseRecorder + save map[string]int + sequence []string + status []int +} + +func (r *responseRecorder) WriteHeader(statusCode int) { + r.save[r.Header().Get("server")]++ + r.sequence = append(r.sequence, r.Header().Get("server")) + r.status = append(r.status, statusCode) + r.ResponseRecorder.WriteHeader(statusCode) +} diff --git a/pkg/server/service/loadbalancer/leasttime/leasttime.go b/pkg/server/service/loadbalancer/leasttime/leasttime.go new file mode 100644 index 000000000..41f4f5218 --- /dev/null +++ b/pkg/server/service/loadbalancer/leasttime/leasttime.go @@ -0,0 +1,373 @@ +package leasttime + +import ( + "context" + "errors" + "math" + "net/http" + "net/http/httptrace" + "sync" + "sync/atomic" + "time" + + "github.com/rs/zerolog/log" + "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/server/service/loadbalancer" +) + +const sampleSize = 100 // Number of response time samples to track. + +var errNoAvailableServer = errors.New("no available server") + +// namedHandler wraps an HTTP handler with metrics and server information. +// Tracks response time (TTFB) and inflight request count for load balancing decisions. +type namedHandler struct { + http.Handler + name string + weight float64 + + deadlineMu sync.RWMutex + deadline float64 // WRR tie-breaking (EDF scheduling). + + inflightCount atomic.Int64 // Number of inflight requests. + + responseTimeMu sync.RWMutex + responseTimes [sampleSize]float64 // Fixed-size ring buffer (TTFB measurements in ms). + responseTimeIdx int // Current position in ring buffer. + responseTimeSum float64 // Sum of all values in buffer. + sampleCount int // Number of samples collected so far. +} + +// updateResponseTime updates the average response time for this server using a ring buffer. +func (s *namedHandler) updateResponseTime(elapsed time.Duration) { + s.responseTimeMu.Lock() + defer s.responseTimeMu.Unlock() + + ms := float64(elapsed.Milliseconds()) + + if s.sampleCount < sampleSize { + // Still filling the buffer. + s.responseTimes[s.responseTimeIdx] = ms + s.responseTimeSum += ms + s.sampleCount++ + } else { + // Buffer is full, replace oldest value. + oldValue := s.responseTimes[s.responseTimeIdx] + s.responseTimes[s.responseTimeIdx] = ms + s.responseTimeSum = s.responseTimeSum - oldValue + ms + } + + s.responseTimeIdx = (s.responseTimeIdx + 1) % sampleSize +} + +// getAvgResponseTime returns the average response time in milliseconds. +// Returns 0 if no samples have been collected yet (cold start). +func (s *namedHandler) getAvgResponseTime() float64 { + s.responseTimeMu.RLock() + defer s.responseTimeMu.RUnlock() + + if s.sampleCount == 0 { + return 0 + } + return s.responseTimeSum / float64(s.sampleCount) +} + +func (s *namedHandler) getDeadline() float64 { + s.deadlineMu.RLock() + defer s.deadlineMu.RUnlock() + return s.deadline +} + +func (s *namedHandler) setDeadline(deadline float64) { + s.deadlineMu.Lock() + defer s.deadlineMu.Unlock() + s.deadline = deadline +} + +// Balancer implements the least-time load balancing algorithm. +// It selects the server with the lowest average response time (TTFB) and fewest active connections. +type Balancer struct { + wantsHealthCheck bool + + // handlersMu protects the handlers slice, the status and the fenced maps. + handlersMu sync.RWMutex + handlers []*namedHandler + // status is a record of which child services of the Balancer are healthy, keyed + // by name of child service. A service is initially added to the map when it is + // created via Add, and it is later removed or added to the map as needed, + // through the SetStatus method. + status map[string]struct{} + // fenced is the list of terminating yet still serving child services. + fenced map[string]struct{} + + // updaters is the list of hooks that are run (to update the Balancer + // parent(s)), whenever the Balancer status changes. + // No mutex is needed, as it is modified only during the configuration build. + updaters []func(bool) + + sticky *loadbalancer.Sticky + + // deadlineMu protects EDF scheduling state (curDeadline and all handler deadline fields). + // Separate from handlersMu to reduce lock contention during tie-breaking. + curDeadlineMu sync.RWMutex + // curDeadline is used for WRR tie-breaking (EDF scheduling). + curDeadline float64 +} + +// New creates a new least-time load balancer. +func New(stickyConfig *dynamic.Sticky, wantsHealthCheck bool) *Balancer { + balancer := &Balancer{ + status: make(map[string]struct{}), + fenced: make(map[string]struct{}), + wantsHealthCheck: wantsHealthCheck, + } + if stickyConfig != nil && stickyConfig.Cookie != nil { + balancer.sticky = loadbalancer.NewSticky(*stickyConfig.Cookie) + } + + return balancer +} + +// SetStatus sets on the balancer that its given child is now of the given +// status. childName is only needed for logging purposes. +func (b *Balancer) SetStatus(ctx context.Context, childName string, up bool) { + b.handlersMu.Lock() + defer b.handlersMu.Unlock() + + upBefore := len(b.status) > 0 + + status := "DOWN" + if up { + status = "UP" + } + + log.Ctx(ctx).Debug().Msgf("Setting status of %s to %v", childName, status) + + if up { + b.status[childName] = struct{}{} + } else { + delete(b.status, childName) + } + + upAfter := len(b.status) > 0 + status = "DOWN" + if upAfter { + status = "UP" + } + + // No Status Change. + if upBefore == upAfter { + // We're still with the same status, no need to propagate. + log.Ctx(ctx).Debug().Msgf("Still %s, no need to propagate", status) + return + } + + // Status Change. + log.Ctx(ctx).Debug().Msgf("Propagating new %s status", status) + for _, fn := range b.updaters { + fn(upAfter) + } +} + +// RegisterStatusUpdater adds fn to the list of hooks that are run when the +// status of the Balancer changes. +// Not thread safe. +func (b *Balancer) RegisterStatusUpdater(fn func(up bool)) error { + if !b.wantsHealthCheck { + return errors.New("healthCheck not enabled in config for this LeastTime service") + } + b.updaters = append(b.updaters, fn) + return nil +} + +// getHealthyServers returns the list of healthy, non-fenced servers. +func (b *Balancer) getHealthyServers() []*namedHandler { + b.handlersMu.RLock() + defer b.handlersMu.RUnlock() + + var healthy []*namedHandler + for _, h := range b.handlers { + if _, ok := b.status[h.name]; ok { + if _, fenced := b.fenced[h.name]; !fenced { + healthy = append(healthy, h) + } + } + } + return healthy +} + +// selectWRR selects a server from candidates using Weighted Round Robin (EDF scheduling). +// This is used for tie-breaking when multiple servers have identical scores. +func (b *Balancer) selectWRR(candidates []*namedHandler) *namedHandler { + if len(candidates) == 0 { + return nil + } + + selected := candidates[0] + minDeadline := math.MaxFloat64 + + // Find handler with earliest deadline. + for _, h := range candidates { + handlerDeadline := h.getDeadline() + if handlerDeadline < minDeadline { + minDeadline = handlerDeadline + selected = h + } + } + + // Update deadline based on when this server was selected (minDeadline), + // not the global curDeadline. This ensures proper weighted distribution. + newDeadline := minDeadline + 1/selected.weight + selected.setDeadline(newDeadline) + + // Track the maximum deadline assigned for initializing new servers. + b.curDeadlineMu.Lock() + if newDeadline > b.curDeadline { + b.curDeadline = newDeadline + } + b.curDeadlineMu.Unlock() + + return selected +} + +// Score = (avgResponseTime × (1 + inflightCount)) / weight. +func (b *Balancer) nextServer() (*namedHandler, error) { + healthy := b.getHealthyServers() + + if len(healthy) == 0 { + return nil, errNoAvailableServer + } + + if len(healthy) == 1 { + return healthy[0], nil + } + + // Calculate scores and find minimum. + minScore := math.MaxFloat64 + var candidates []*namedHandler + + for _, h := range healthy { + avgRT := h.getAvgResponseTime() + inflight := float64(h.inflightCount.Load()) + score := (avgRT * (1 + inflight)) / h.weight + + if score < minScore { + minScore = score + candidates = []*namedHandler{h} + } else if score == minScore { + candidates = append(candidates, h) + } + } + + if len(candidates) == 1 { + return candidates[0], nil + } + + // Multiple servers with same score: use WRR (EDF) tie-breaking. + selected := b.selectWRR(candidates) + if selected == nil { + return nil, errNoAvailableServer + } + + return selected, nil +} + +func (b *Balancer) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + // Handle sticky sessions first. + if b.sticky != nil { + h, rewrite, err := b.sticky.StickyHandler(req) + if err != nil { + log.Error().Err(err).Msg("Error while getting sticky handler") + } else if h != nil { + b.handlersMu.RLock() + _, ok := b.status[h.Name] + b.handlersMu.RUnlock() + if ok { + if rewrite { + if err := b.sticky.WriteStickyCookie(rw, h.Name); err != nil { + log.Error().Err(err).Msg("Writing sticky cookie") + } + } + + h.ServeHTTP(rw, req) + return + } + } + } + + server, err := b.nextServer() + if err != nil { + if errors.Is(err, errNoAvailableServer) { + http.Error(rw, errNoAvailableServer.Error(), http.StatusServiceUnavailable) + } else { + http.Error(rw, err.Error(), http.StatusInternalServerError) + } + return + } + + if b.sticky != nil { + if err := b.sticky.WriteStickyCookie(rw, server.name); err != nil { + log.Error().Err(err).Msg("Error while writing sticky cookie") + } + } + + // Track inflight requests. + server.inflightCount.Add(1) + defer server.inflightCount.Add(-1) + + startTime := time.Now() + trace := &httptrace.ClientTrace{ + GotFirstResponseByte: func() { + // Update average response time (TTFB). + server.updateResponseTime(time.Since(startTime)) + }, + } + traceCtx := httptrace.WithClientTrace(req.Context(), trace) + server.ServeHTTP(rw, req.WithContext(traceCtx)) +} + +// AddServer adds a handler with a server. +func (b *Balancer) AddServer(name string, handler http.Handler, server dynamic.Server) { + b.Add(name, handler, server.Weight, server.Fenced) +} + +// Add adds a handler. +// A handler with a non-positive weight is ignored. +func (b *Balancer) Add(name string, handler http.Handler, weight *int, fenced bool) { + w := 1 + if weight != nil { + w = *weight + } + + if w <= 0 { // non-positive weight is meaningless. + return + } + + h := &namedHandler{Handler: handler, name: name, weight: float64(w)} + + // Initialize deadline by adding 1/weight to current deadline. + // This staggers servers to prevent all starting at the same time. + var deadline float64 + b.curDeadlineMu.RLock() + deadline = b.curDeadline + 1/h.weight + b.curDeadlineMu.RUnlock() + + h.setDeadline(deadline) + + // Update balancer's current deadline with the new server's deadline. + b.curDeadlineMu.Lock() + b.curDeadline = deadline + b.curDeadlineMu.Unlock() + + b.handlersMu.Lock() + b.handlers = append(b.handlers, h) + b.status[name] = struct{}{} + if fenced { + b.fenced[name] = struct{}{} + } + b.handlersMu.Unlock() + + if b.sticky != nil { + b.sticky.AddHandler(name, handler) + } +} diff --git a/pkg/server/service/loadbalancer/leasttime/leasttime_test.go b/pkg/server/service/loadbalancer/leasttime/leasttime_test.go new file mode 100644 index 000000000..53bb81505 --- /dev/null +++ b/pkg/server/service/loadbalancer/leasttime/leasttime_test.go @@ -0,0 +1,1092 @@ +package leasttime + +import ( + "context" + "net/http" + "net/http/httptest" + "net/http/httptrace" + "sync" + "sync/atomic" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/traefik/traefik/v3/pkg/config/dynamic" +) + +type key string + +const serviceName key = "serviceName" + +func pointer[T any](v T) *T { return &v } + +// responseRecorder tracks which servers handled requests. +type responseRecorder struct { + *httptest.ResponseRecorder + save map[string]int +} + +func (r *responseRecorder) WriteHeader(statusCode int) { + server := r.Header().Get("server") + if server != "" { + r.save[server]++ + } + r.ResponseRecorder.WriteHeader(statusCode) +} + +// TestBalancer tests basic server addition and least-time selection. +func TestBalancer(t *testing.T) { + balancer := New(nil, false) + + balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(5 * time.Millisecond) + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(5 * time.Millisecond) + rw.Header().Set("server", "second") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + for range 10 { + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + + // With least-time and equal response times, both servers should get some traffic. + assert.Positive(t, recorder.save["first"]) + assert.Positive(t, recorder.save["second"]) + assert.Equal(t, 10, recorder.save["first"]+recorder.save["second"]) +} + +// TestBalancerNoService tests behavior when no servers are configured. +func TestBalancerNoService(t *testing.T) { + balancer := New(nil, false) + + recorder := httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + + assert.Equal(t, http.StatusServiceUnavailable, recorder.Result().StatusCode) +} + +// TestBalancerNoServiceUp tests behavior when all servers are marked down. +func TestBalancerNoServiceUp(t *testing.T) { + balancer := New(nil, false) + + balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.WriteHeader(http.StatusInternalServerError) + }), pointer(1), false) + + balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.WriteHeader(http.StatusInternalServerError) + }), pointer(1), false) + + balancer.SetStatus(context.WithValue(t.Context(), serviceName, "parent"), "first", false) + balancer.SetStatus(context.WithValue(t.Context(), serviceName, "parent"), "second", false) + + recorder := httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + + assert.Equal(t, http.StatusServiceUnavailable, recorder.Result().StatusCode) +} + +// TestBalancerOneServerDown tests that down servers are excluded from selection. +func TestBalancerOneServerDown(t *testing.T) { + balancer := New(nil, false) + + balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.WriteHeader(http.StatusInternalServerError) + }), pointer(1), false) + balancer.SetStatus(context.WithValue(t.Context(), serviceName, "parent"), "second", false) + + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + for range 3 { + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + + assert.Equal(t, 3, recorder.save["first"]) + assert.Equal(t, 0, recorder.save["second"]) +} + +// TestBalancerOneServerDownThenUp tests server status transitions. +func TestBalancerOneServerDownThenUp(t *testing.T) { + balancer := New(nil, false) + + balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(5 * time.Millisecond) + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(5 * time.Millisecond) + rw.Header().Set("server", "second") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + balancer.SetStatus(context.WithValue(t.Context(), serviceName, "parent"), "second", false) + + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + for range 3 { + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + assert.Equal(t, 3, recorder.save["first"]) + assert.Equal(t, 0, recorder.save["second"]) + + balancer.SetStatus(context.WithValue(t.Context(), serviceName, "parent"), "second", true) + recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + for range 20 { + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + // Both servers should get some traffic. + assert.Positive(t, recorder.save["first"]) + assert.Positive(t, recorder.save["second"]) + assert.Equal(t, 20, recorder.save["first"]+recorder.save["second"]) +} + +// TestBalancerAllServersZeroWeight tests that all zero-weight servers result in no available server. +func TestBalancerAllServersZeroWeight(t *testing.T) { + balancer := New(nil, false) + + balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), pointer(0), false) + balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), pointer(0), false) + + recorder := httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + + assert.Equal(t, http.StatusServiceUnavailable, recorder.Result().StatusCode) +} + +// TestBalancerOneServerZeroWeight tests that zero-weight servers are ignored. +func TestBalancerOneServerZeroWeight(t *testing.T) { + balancer := New(nil, false) + + balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), pointer(0), false) + + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + for range 3 { + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + + // Only first server should receive traffic. + assert.Equal(t, 3, recorder.save["first"]) + assert.Equal(t, 0, recorder.save["second"]) +} + +// TestBalancerPropagate tests status propagation to parent balancers. +func TestBalancerPropagate(t *testing.T) { + balancer1 := New(nil, true) + + balancer1.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + balancer1.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "second") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + balancer2 := New(nil, true) + balancer2.Add("third", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "third") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + balancer2.Add("fourth", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "fourth") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + topBalancer := New(nil, true) + topBalancer.Add("balancer1", balancer1, pointer(1), false) + topBalancer.Add("balancer2", balancer2, pointer(1), false) + err := balancer1.RegisterStatusUpdater(func(up bool) { + topBalancer.SetStatus(context.WithValue(t.Context(), serviceName, "top"), "balancer1", up) + }) + assert.NoError(t, err) + err = balancer2.RegisterStatusUpdater(func(up bool) { + topBalancer.SetStatus(context.WithValue(t.Context(), serviceName, "top"), "balancer2", up) + }) + assert.NoError(t, err) + + // Set all children of balancer1 to down, should propagate to top. + balancer1.SetStatus(context.WithValue(t.Context(), serviceName, "top"), "first", false) + balancer1.SetStatus(context.WithValue(t.Context(), serviceName, "top"), "second", false) + + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + for range 4 { + topBalancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + + // Only balancer2 should receive traffic. + assert.Equal(t, 0, recorder.save["first"]) + assert.Equal(t, 0, recorder.save["second"]) + assert.Equal(t, 4, recorder.save["third"]+recorder.save["fourth"]) +} + +// TestBalancerOneServerFenced tests that fenced servers are excluded from selection. +func TestBalancerOneServerFenced(t *testing.T) { + balancer := New(nil, false) + + balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "second") + rw.WriteHeader(http.StatusOK) + }), pointer(1), true) // fenced + + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + for range 3 { + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + + // Only first server should receive traffic. + assert.Equal(t, 3, recorder.save["first"]) + assert.Equal(t, 0, recorder.save["second"]) +} + +// TestBalancerAllFencedServers tests that all fenced servers result in no available server. +func TestBalancerAllFencedServers(t *testing.T) { + balancer := New(nil, false) + + balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), pointer(1), true) + balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), pointer(1), true) + + recorder := httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + + assert.Equal(t, http.StatusServiceUnavailable, recorder.Result().StatusCode) +} + +// TestBalancerRegisterStatusUpdaterWithoutHealthCheck tests error when registering updater without health check. +func TestBalancerRegisterStatusUpdaterWithoutHealthCheck(t *testing.T) { + balancer := New(nil, false) + + err := balancer.RegisterStatusUpdater(func(up bool) {}) + assert.Error(t, err) + assert.Contains(t, err.Error(), "healthCheck not enabled") +} + +// TestBalancerSticky tests sticky session support. +func TestBalancerSticky(t *testing.T) { + balancer := New(&dynamic.Sticky{ + Cookie: &dynamic.Cookie{ + Name: "test", + }, + }, false) + + balancer.Add("first", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "first") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + balancer.Add("second", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "second") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + // First request should set cookie. + recorder := httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + firstServer := recorder.Header().Get("server") + assert.NotEmpty(t, firstServer) + + // Extract cookie from first response. + cookies := recorder.Result().Cookies() + assert.NotEmpty(t, cookies) + + // Second request with cookie should hit same server. + req := httptest.NewRequest(http.MethodGet, "/", nil) + for _, cookie := range cookies { + req.AddCookie(cookie) + } + + recorder2 := httptest.NewRecorder() + balancer.ServeHTTP(recorder2, req) + secondServer := recorder2.Header().Get("server") + + assert.Equal(t, firstServer, secondServer) +} + +// TestBalancerStickyFallback tests that sticky sessions fallback to least-time when sticky server is down. +func TestBalancerStickyFallback(t *testing.T) { + balancer := New(&dynamic.Sticky{ + Cookie: &dynamic.Cookie{ + Name: "test", + }, + }, false) + + balancer.Add("server1", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(50 * time.Millisecond) + rw.Header().Set("server", "server1") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + balancer.Add("server2", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(50 * time.Millisecond) + rw.Header().Set("server", "server2") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + // Make initial request to establish sticky session with server1. + recorder1 := httptest.NewRecorder() + balancer.ServeHTTP(recorder1, httptest.NewRequest(http.MethodGet, "/", nil)) + firstServer := recorder1.Header().Get("server") + assert.NotEmpty(t, firstServer) + + // Extract cookie from first response. + cookies := recorder1.Result().Cookies() + assert.NotEmpty(t, cookies) + + // Mark the sticky server as DOWN + balancer.SetStatus(context.WithValue(t.Context(), serviceName, "test"), firstServer, false) + + // Request with sticky cookie should fallback to the other server + req2 := httptest.NewRequest(http.MethodGet, "/", nil) + for _, cookie := range cookies { + req2.AddCookie(cookie) + } + recorder2 := httptest.NewRecorder() + balancer.ServeHTTP(recorder2, req2) + fallbackServer := recorder2.Header().Get("server") + assert.NotEqual(t, firstServer, fallbackServer) + assert.NotEmpty(t, fallbackServer) + + // New sticky cookie should be written for the fallback server + newCookies := recorder2.Result().Cookies() + assert.NotEmpty(t, newCookies) + + // Verify sticky session persists with the fallback server + req3 := httptest.NewRequest(http.MethodGet, "/", nil) + for _, cookie := range newCookies { + req3.AddCookie(cookie) + } + recorder3 := httptest.NewRecorder() + balancer.ServeHTTP(recorder3, req3) + assert.Equal(t, fallbackServer, recorder3.Header().Get("server")) + + // Bring original server back UP + balancer.SetStatus(context.WithValue(t.Context(), serviceName, "test"), firstServer, true) + + // Request with fallback server cookie should still stick to fallback server + req4 := httptest.NewRequest(http.MethodGet, "/", nil) + for _, cookie := range newCookies { + req4.AddCookie(cookie) + } + recorder4 := httptest.NewRecorder() + balancer.ServeHTTP(recorder4, req4) + assert.Equal(t, fallbackServer, recorder4.Header().Get("server")) +} + +// TestBalancerStickyFenced tests that sticky sessions persist to fenced servers (graceful shutdown) +// Fencing enables zero-downtime deployments: fenced servers reject NEW connections +// but continue serving EXISTING sticky sessions until they complete. +func TestBalancerStickyFenced(t *testing.T) { + balancer := New(&dynamic.Sticky{ + Cookie: &dynamic.Cookie{ + Name: "test", + }, + }, false) + + balancer.Add("server1", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "server1") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + balancer.Add("server2", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + rw.Header().Set("server", "server2") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + // Establish sticky session with any server. + recorder1 := httptest.NewRecorder() + balancer.ServeHTTP(recorder1, httptest.NewRequest(http.MethodGet, "/", nil)) + stickyServer := recorder1.Header().Get("server") + assert.NotEmpty(t, stickyServer) + + cookies := recorder1.Result().Cookies() + assert.NotEmpty(t, cookies) + + // Fence the sticky server (simulate graceful shutdown). + balancer.handlersMu.Lock() + balancer.fenced[stickyServer] = struct{}{} + balancer.handlersMu.Unlock() + + // Existing sticky session should STILL work (graceful draining). + req := httptest.NewRequest(http.MethodGet, "/", nil) + for _, cookie := range cookies { + req.AddCookie(cookie) + } + recorder2 := httptest.NewRecorder() + balancer.ServeHTTP(recorder2, req) + assert.Equal(t, stickyServer, recorder2.Header().Get("server")) + + // But NEW requests should NOT go to the fenced server. + recorder3 := httptest.NewRecorder() + balancer.ServeHTTP(recorder3, httptest.NewRequest(http.MethodGet, "/", nil)) + newServer := recorder3.Header().Get("server") + assert.NotEqual(t, stickyServer, newServer) + assert.NotEmpty(t, newServer) +} + +// TestRingBufferBasic tests basic ring buffer functionality with few samples. +func TestRingBufferBasic(t *testing.T) { + handler := &namedHandler{ + Handler: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), + name: "test", + weight: 1, + } + + // Test cold start - no samples. + avg := handler.getAvgResponseTime() + assert.InDelta(t, 0.0, avg, 0) + + // Add one sample. + handler.updateResponseTime(10 * time.Millisecond) + avg = handler.getAvgResponseTime() + assert.InDelta(t, 10.0, avg, 0) + + // Add more samples. + handler.updateResponseTime(20 * time.Millisecond) + handler.updateResponseTime(30 * time.Millisecond) + avg = handler.getAvgResponseTime() + assert.InDelta(t, 20.0, avg, 0) // (10 + 20 + 30) / 3 = 20 +} + +// TestRingBufferWraparound tests ring buffer behavior when it wraps around +func TestRingBufferWraparound(t *testing.T) { + handler := &namedHandler{ + Handler: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), + name: "test", + weight: 1, + } + + // Fill the buffer with 100 samples of 10ms each. + for range sampleSize { + handler.updateResponseTime(10 * time.Millisecond) + } + avg := handler.getAvgResponseTime() + assert.InDelta(t, 10.0, avg, 0) + + // Add one more sample (should replace oldest). + handler.updateResponseTime(20 * time.Millisecond) + avg = handler.getAvgResponseTime() + // Sum: 99*10 + 1*20 = 1010, avg = 1010/100 = 10.1 + assert.InDelta(t, 10.1, avg, 0) + + // Add 10 more samples of 30ms. + for range 10 { + handler.updateResponseTime(30 * time.Millisecond) + } + avg = handler.getAvgResponseTime() + // Sum: 89*10 + 1*20 + 10*30 = 890 + 20 + 300 = 1210, avg = 1210/100 = 12.1 + assert.InDelta(t, 12.1, avg, 0) +} + +// TestRingBufferLarge tests ring buffer with many samples (> 100). +func TestRingBufferLarge(t *testing.T) { + handler := &namedHandler{ + Handler: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), + name: "test", + weight: 1, + } + + // Add 150 samples. + for i := range 150 { + handler.updateResponseTime(time.Duration(i+1) * time.Millisecond) + } + + // Should only track last 100 samples: 51, 52, ..., 150 + // Sum = (51 + 150) * 100 / 2 = 10050 + // Avg = 10050 / 100 = 100.5 + avg := handler.getAvgResponseTime() + assert.InDelta(t, 100.5, avg, 0) +} + +// TestInflightCounter tests inflight request tracking. +func TestInflightCounter(t *testing.T) { + balancer := New(nil, false) + + var inflightAtRequest atomic.Int64 + + balancer.Add("test", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + inflightAtRequest.Store(balancer.handlers[0].inflightCount.Load()) + rw.Header().Set("server", "test") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + // Check that inflight count is 0 initially. + balancer.handlersMu.RLock() + handler := balancer.handlers[0] + balancer.handlersMu.RUnlock() + assert.Equal(t, int64(0), handler.inflightCount.Load()) + + // Make a request. + recorder := httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + + // During request, inflight should have been 1. + assert.Equal(t, int64(1), inflightAtRequest.Load()) + + // After request completes, inflight should be back to 0. + assert.Equal(t, int64(0), handler.inflightCount.Load()) +} + +// TestConcurrentResponseTimeUpdates tests thread safety of response time updates. +func TestConcurrentResponseTimeUpdates(t *testing.T) { + handler := &namedHandler{ + Handler: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), + name: "test", + weight: 1, + } + + // Concurrently update response times. + var wg sync.WaitGroup + numGoroutines := 10 + updatesPerGoroutine := 20 + + for i := range numGoroutines { + wg.Add(1) + go func(id int) { + defer wg.Done() + for range updatesPerGoroutine { + handler.updateResponseTime(time.Duration(id+1) * time.Millisecond) + } + }(i) + } + + wg.Wait() + + // Should have exactly 100 samples (buffer size). + assert.Equal(t, sampleSize, handler.sampleCount) +} + +// TestConcurrentInflightTracking tests thread safety of inflight counter. +func TestConcurrentInflightTracking(t *testing.T) { + handler := &namedHandler{ + Handler: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(10 * time.Millisecond) + rw.WriteHeader(http.StatusOK) + }), + name: "test", + weight: 1, + } + + var maxInflight atomic.Int64 + + var wg sync.WaitGroup + numRequests := 50 + + for range numRequests { + wg.Add(1) + go func() { + defer wg.Done() + handler.inflightCount.Add(1) + defer handler.inflightCount.Add(-1) + + // Track maximum inflight count. + current := handler.inflightCount.Load() + for { + maxLoad := maxInflight.Load() + if current <= maxLoad || maxInflight.CompareAndSwap(maxLoad, current) { + break + } + } + + time.Sleep(1 * time.Millisecond) + }() + } + + wg.Wait() + + // All requests completed, inflight should be 0. + assert.Equal(t, int64(0), handler.inflightCount.Load()) + // Max inflight should be > 1 (concurrent requests). + assert.Greater(t, maxInflight.Load(), int64(1)) +} + +// TestConcurrentRequestsRespectInflight tests that the load balancer dynamically +// adapts to inflight request counts during concurrent request processing. +func TestConcurrentRequestsRespectInflight(t *testing.T) { + balancer := New(nil, false) + + // Use a channel to control when handlers start sleeping. + // This ensures we can fill one server with inflight requests before routing new ones. + blockChan := make(chan struct{}) + + // Add two servers with equal response times and weights. + balancer.Add("server1", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + <-blockChan // Wait for signal to proceed. + time.Sleep(10 * time.Millisecond) + rw.Header().Set("server", "server1") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + balancer.Add("server2", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + <-blockChan // Wait for signal to proceed. + time.Sleep(10 * time.Millisecond) + rw.Header().Set("server", "server2") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + // Pre-warm both servers to establish equal average response times. + for i := range sampleSize { + balancer.handlers[0].responseTimes[i] = 10.0 + } + balancer.handlers[0].responseTimeSum = 10.0 * sampleSize + balancer.handlers[0].sampleCount = sampleSize + + for i := range sampleSize { + balancer.handlers[1].responseTimes[i] = 10.0 + } + balancer.handlers[1].responseTimeSum = 10.0 * sampleSize + balancer.handlers[1].sampleCount = sampleSize + + // Phase 1: Launch concurrent requests to server1 that will block. + var wg sync.WaitGroup + inflightRequests := 5 + + for range inflightRequests { + wg.Add(1) + go func() { + defer wg.Done() + recorder := httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + }() + } + + // Wait for goroutines to start and increment inflight counters. + // They will block on the channel, keeping inflight count high. + time.Sleep(50 * time.Millisecond) + + // Verify inflight counts before making new requests. + server1Inflight := balancer.handlers[0].inflightCount.Load() + server2Inflight := balancer.handlers[1].inflightCount.Load() + assert.Equal(t, int64(5), server1Inflight+server2Inflight) + + // Phase 2: Make new requests while the initial requests are blocked. + // These should see the high inflight counts and route to the less-loaded server. + var saveMu sync.Mutex + save := map[string]int{} + newRequests := 50 + + // Launch new requests in background so they don't block. + var newWg sync.WaitGroup + for range newRequests { + newWg.Add(1) + go func() { + defer newWg.Done() + rec := httptest.NewRecorder() + balancer.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/", nil)) + server := rec.Header().Get("server") + if server != "" { + saveMu.Lock() + save[server]++ + saveMu.Unlock() + } + }() + } + + // Wait for new requests to start and see the inflight counts. + time.Sleep(50 * time.Millisecond) + + close(blockChan) + + wg.Wait() + newWg.Wait() + + saveMu.Lock() + total := save["server1"] + save["server2"] + server1Count := save["server1"] + server2Count := save["server2"] + saveMu.Unlock() + + assert.Equal(t, newRequests, total) + + // With inflight tracking, load should naturally balance toward equal distribution. + // We allow variance due to concurrent execution and race windows in server selection. + assert.InDelta(t, 25.0, float64(server1Count), 5.0) // 20-30 requests + assert.InDelta(t, 25.0, float64(server2Count), 5.0) // 20-30 requests +} + +// TestTTFBMeasurement tests TTFB measurement accuracy. +func TestTTFBMeasurement(t *testing.T) { + balancer := New(nil, false) + + // Add server with known delay. + delay := 50 * time.Millisecond + balancer.Add("slow", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(delay) + rw.Header().Set("server", "slow") + rw.WriteHeader(http.StatusOK) + httptrace.ContextClientTrace(req.Context()).GotFirstResponseByte() + }), pointer(1), false) + + // Make multiple requests to build average. + for range 5 { + recorder := httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + + // Check that average response time is approximately the delay. + avg := balancer.handlers[0].getAvgResponseTime() + + // Allow 5ms tolerance for Go timing jitter and test environment variations. + assert.InDelta(t, float64(delay.Milliseconds()), avg, 5.0) +} + +// TestZeroSamplesReturnsZero tests that getAvgResponseTime returns 0 when no samples. +func TestZeroSamplesReturnsZero(t *testing.T) { + handler := &namedHandler{ + Handler: http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {}), + name: "test", + weight: 1, + } + + avg := handler.getAvgResponseTime() + assert.InDelta(t, 0.0, avg, 0) +} + +// TestScoreCalculationWithWeights tests that weights are properly considered in score calculation. +func TestScoreCalculationWithWeights(t *testing.T) { + balancer := New(nil, false) + + // Add two servers with same response time but different weights. + // Server with higher weight should be preferred. + balancer.Add("weighted", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(50 * time.Millisecond) + rw.Header().Set("server", "weighted") + rw.WriteHeader(http.StatusOK) + httptrace.ContextClientTrace(req.Context()).GotFirstResponseByte() + }), pointer(3), false) // Weight 3 + + balancer.Add("normal", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(50 * time.Millisecond) + rw.Header().Set("server", "normal") + rw.WriteHeader(http.StatusOK) + httptrace.ContextClientTrace(req.Context()).GotFirstResponseByte() + }), pointer(1), false) // Weight 1 + + // Make requests to build up response time averages. + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + for range 2 { + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + + // Score for weighted: (50 × (1 + 0)) / 3 = 16.67 + // Score for normal: (50 × (1 + 0)) / 1 = 50 + // After warmup, weighted server has 3x better score (16.67 vs 50) and should receive nearly all requests. + recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + for range 10 { + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + + assert.Equal(t, 10, recorder.save["weighted"]) + assert.Zero(t, recorder.save["normal"]) +} + +// TestScoreCalculationWithInflight tests that inflight requests are considered in score calculation. +func TestScoreCalculationWithInflight(t *testing.T) { + balancer := New(nil, false) + + // We'll manually control the inflight counters to test the score calculation. + // Add two servers with same response time. + balancer.Add("server1", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(10 * time.Millisecond) + rw.Header().Set("server", "server1") + rw.WriteHeader(http.StatusOK) + httptrace.ContextClientTrace(req.Context()).GotFirstResponseByte() + }), pointer(1), false) + + balancer.Add("server2", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(10 * time.Millisecond) + rw.Header().Set("server", "server2") + rw.WriteHeader(http.StatusOK) + httptrace.ContextClientTrace(req.Context()).GotFirstResponseByte() + }), pointer(1), false) + + // Build up response time averages for both servers. + for range 2 { + recorder := httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + + // Now manually set server1 to have high inflight count. + balancer.handlers[0].inflightCount.Store(5) + + // Make requests - they should prefer server2 because: + // Score for server1: (10 × (1 + 5)) / 1 = 60 + // Score for server2: (10 × (1 + 0)) / 1 = 10 + recorder := &responseRecorder{save: map[string]int{}} + for range 5 { + // Manually increment to simulate the ServeHTTP behavior. + server, _ := balancer.nextServer() + server.inflightCount.Add(1) + + if server.name == "server1" { + recorder.save["server1"]++ + } else { + recorder.save["server2"]++ + } + } + + // Server2 should get all requests + assert.Equal(t, 5, recorder.save["server2"]) + assert.Zero(t, recorder.save["server1"]) +} + +// TestScoreCalculationColdStart tests that new servers (0ms avg) get fair selection +func TestScoreCalculationColdStart(t *testing.T) { + balancer := New(nil, false) + + // Add a warm server with established response time + balancer.Add("warm", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(50 * time.Millisecond) + rw.Header().Set("server", "warm") + rw.WriteHeader(http.StatusOK) + httptrace.ContextClientTrace(req.Context()).GotFirstResponseByte() + }), pointer(1), false) + + // Warm up the first server + for range 10 { + recorder := httptest.NewRecorder() + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + + // Now add a cold server (new, no response time data) + balancer.Add("cold", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(10 * time.Millisecond) // Actually faster + rw.Header().Set("server", "cold") + rw.WriteHeader(http.StatusOK) + httptrace.ContextClientTrace(req.Context()).GotFirstResponseByte() + }), pointer(1), false) + + // Cold server should get selected because: + // Score for warm: (50 × (1 + 0)) / 1 = 50 + // Score for cold: (0 × (1 + 0)) / 1 = 0 + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + for range 20 { + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + + // Cold server should get all or most requests initially due to 0ms average + assert.Greater(t, recorder.save["cold"], 10) + + // After cold server builds up its average, it should continue to get more traffic + // because it's actually faster (10ms vs 50ms) + recorder = &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + for range 20 { + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + assert.Greater(t, recorder.save["cold"], recorder.save["warm"]) +} + +// TestFastServerGetsMoreTraffic verifies that servers with lower response times +// receive proportionally more traffic in steady state (after cold start). +// This tests the core selection bias of the least-time algorithm. +func TestFastServerGetsMoreTraffic(t *testing.T) { + balancer := New(nil, false) + + // Add two servers with different static response times. + balancer.Add("fast", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(20 * time.Millisecond) + rw.Header().Set("server", "fast") + rw.WriteHeader(http.StatusOK) + httptrace.ContextClientTrace(req.Context()).GotFirstResponseByte() + }), pointer(1), false) + + balancer.Add("slow", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(100 * time.Millisecond) + rw.Header().Set("server", "slow") + rw.WriteHeader(http.StatusOK) + httptrace.ContextClientTrace(req.Context()).GotFirstResponseByte() + }), pointer(1), false) + + // After just 1 request to each server, the algorithm identifies the fastest server + // and routes nearly all subsequent traffic there (converges in ~2 requests). + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + for range 50 { + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + + assert.Greater(t, recorder.save["fast"], recorder.save["slow"]) + assert.Greater(t, recorder.save["fast"], 48) // Expect ~96-98% to fast server (48-49/50). +} + +// TestTrafficShiftsWhenPerformanceDegrades verifies that the load balancer +// adapts to changing server performance by shifting traffic away from degraded servers. +// This tests the adaptive behavior - the core value proposition of least-time load balancing. +func TestTrafficShiftsWhenPerformanceDegrades(t *testing.T) { + balancer := New(nil, false) + + // Use atomic to dynamically control server1's response time. + server1Delay := atomic.Int64{} + server1Delay.Store(5) // Start with 5ms + + balancer.Add("server1", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(time.Duration(server1Delay.Load()) * time.Millisecond) + rw.Header().Set("server", "server1") + rw.WriteHeader(http.StatusOK) + httptrace.ContextClientTrace(req.Context()).GotFirstResponseByte() + }), pointer(1), false) + + balancer.Add("server2", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(5 * time.Millisecond) // Static 5ms + rw.Header().Set("server", "server2") + rw.WriteHeader(http.StatusOK) + httptrace.ContextClientTrace(req.Context()).GotFirstResponseByte() + }), pointer(1), false) + + // Pre-fill ring buffers to eliminate cold start effects and ensure deterministic equal performance state. + for _, h := range balancer.handlers { + for i := range sampleSize { + h.responseTimes[i] = 5.0 + } + h.responseTimeSum = 5.0 * sampleSize + h.sampleCount = sampleSize + } + + // Phase 1: Both servers perform equally (5ms each). + recorder := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + for range 50 { + balancer.ServeHTTP(recorder, httptest.NewRequest(http.MethodGet, "/", nil)) + } + + // With equal performance and pre-filled buffers, distribution should be balanced via WRR tie-breaking. + total := recorder.save["server1"] + recorder.save["server2"] + assert.Equal(t, 50, total) + assert.InDelta(t, 25, recorder.save["server1"], 10) // 25 ± 10 requests + assert.InDelta(t, 25, recorder.save["server2"], 10) // 25 ± 10 requests + + // Phase 2: server1 degrades (simulating GC pause, CPU spike, or network latency). + server1Delay.Store(50) // Now 50ms (10x slower) - dramatic degradation for reliable detection + + // Make more requests to shift the moving average. + // Ring buffer has 100 samples, need significant new samples to shift average. + // server1's average will climb from ~5ms toward 50ms. + recorder2 := &responseRecorder{ResponseRecorder: httptest.NewRecorder(), save: map[string]int{}} + for range 60 { + balancer.ServeHTTP(recorder2, httptest.NewRequest(http.MethodGet, "/", nil)) + } + + // server2 should get significantly more traffic + // With 10x performance difference, server2 should dominate. + total2 := recorder2.save["server1"] + recorder2.save["server2"] + assert.Equal(t, 60, total2) + assert.Greater(t, recorder2.save["server2"], 35) // At least ~60% (35/60) + assert.Less(t, recorder2.save["server1"], 25) // At most ~40% (25/60) +} + +// TestMultipleServersWithSameScore tests WRR tie-breaking when multiple servers have identical scores. +// Uses nextServer() directly to avoid timing variations in the test. +func TestMultipleServersWithSameScore(t *testing.T) { + balancer := New(nil, false) + + // Add three servers with identical response times and weights. + balancer.Add("server1", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(5 * time.Millisecond) + rw.Header().Set("server", "server1") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + balancer.Add("server2", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(5 * time.Millisecond) + rw.Header().Set("server", "server2") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + balancer.Add("server3", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(5 * time.Millisecond) + rw.Header().Set("server", "server3") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) + + // Set all servers to identical response times to trigger tie-breaking. + for _, h := range balancer.handlers { + for i := range sampleSize { + h.responseTimes[i] = 5.0 + } + h.responseTimeSum = 5.0 * sampleSize + h.sampleCount = sampleSize + } + + // With all servers having identical scores, WRR tie-breaking should distribute fairly. + // Test the selection logic directly without actual HTTP requests to avoid timing variations. + counts := map[string]int{"server1": 0, "server2": 0, "server3": 0} + for range 90 { + server, err := balancer.nextServer() + assert.NoError(t, err) + counts[server.name]++ + } + + total := counts["server1"] + counts["server2"] + counts["server3"] + assert.Equal(t, 90, total) + + // With WRR and 90 requests, each server should get ~30 requests (±1 due to initialization). + assert.InDelta(t, 30, counts["server1"], 1) + assert.InDelta(t, 30, counts["server2"], 1) + assert.InDelta(t, 30, counts["server3"], 1) +} + +// TestWRRTieBreakingWeightedDistribution tests weighted distribution among tied servers. +// Uses nextServer() directly to avoid timing variations in the test. +func TestWRRTieBreakingWeightedDistribution(t *testing.T) { + balancer := New(nil, false) + + // Add two servers with different weights. + // To create equal scores, response times must be proportional to weights. + balancer.Add("weighted", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(15 * time.Millisecond) // 3x longer due to 3x weight + rw.Header().Set("server", "weighted") + rw.WriteHeader(http.StatusOK) + }), pointer(3), false) // Weight 3 + + balancer.Add("normal", http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + time.Sleep(5 * time.Millisecond) + rw.Header().Set("server", "normal") + rw.WriteHeader(http.StatusOK) + }), pointer(1), false) // Weight 1 + + // Since response times is proportional to weights, both scores are equal, so WRR tie-breaking will apply. + // weighted: score = (15 * 1) / 3 = 5 + // normal: score = (5 * 1) / 1 = 5 + for i := range sampleSize { + balancer.handlers[0].responseTimes[i] = 15.0 + } + balancer.handlers[0].responseTimeSum = 15.0 * sampleSize + balancer.handlers[0].sampleCount = sampleSize + + for i := range sampleSize { + balancer.handlers[1].responseTimes[i] = 5.0 + } + balancer.handlers[1].responseTimeSum = 5.0 * sampleSize + balancer.handlers[1].sampleCount = sampleSize + + // Test the selection logic directly without actual HTTP requests to avoid timing variations. + counts := map[string]int{"weighted": 0, "normal": 0} + for range 80 { + server, err := balancer.nextServer() + assert.NoError(t, err) + counts[server.name]++ + } + + total := counts["weighted"] + counts["normal"] + assert.Equal(t, 80, total) + + // With 3:1 weight ratio, distribution should be ~75%/25% (60/80 and 20/80), ±1 due to initialization. + assert.InDelta(t, 60, counts["weighted"], 1) + assert.InDelta(t, 20, counts["normal"], 1) +} diff --git a/pkg/server/service/loadbalancer/p2c/p2c.go b/pkg/server/service/loadbalancer/p2c/p2c.go index 3bf06a512..948a7d5a1 100644 --- a/pkg/server/service/loadbalancer/p2c/p2c.go +++ b/pkg/server/service/loadbalancer/p2c/p2c.go @@ -14,6 +14,8 @@ import ( "github.com/traefik/traefik/v3/pkg/server/service/loadbalancer" ) +var errNoAvailableServer = errors.New("no available server") + type namedHandler struct { http.Handler @@ -56,6 +58,7 @@ type Balancer struct { // updaters is the list of hooks that are run (to update the Balancer // parent(s)), whenever the Balancer status changes. + // No mutex is needed, as it is modified only during the configuration build. updaters []func(bool) sticky *loadbalancer.Sticky @@ -80,7 +83,7 @@ func New(stickyConfig *dynamic.Sticky, wantsHealthCheck bool) *Balancer { } // SetStatus sets on the balancer that its given child is now of the given -// status. balancerName is only needed for logging purposes. +// status. childName is only needed for logging purposes. func (b *Balancer) SetStatus(ctx context.Context, childName string, up bool) { b.handlersMu.Lock() defer b.handlersMu.Unlock() @@ -125,14 +128,12 @@ func (b *Balancer) SetStatus(ctx context.Context, childName string, up bool) { // Not thread safe. func (b *Balancer) RegisterStatusUpdater(fn func(up bool)) error { if !b.wantsHealthCheck { - return errors.New("healthCheck not enabled in config for this weighted service") + return errors.New("healthCheck not enabled in config for this P2C service") } b.updaters = append(b.updaters, fn) return nil } -var errNoAvailableServer = errors.New("no available server") - func (b *Balancer) nextServer() (*namedHandler, error) { // We kept the same representation (map) as in the WRR strategy to improve maintainability. // However, with the P2C strategy, we only need a slice of healthy servers. diff --git a/pkg/server/service/loadbalancer/wrr/wrr.go b/pkg/server/service/loadbalancer/wrr/wrr.go index 2c9bff0ee..69bc2c498 100644 --- a/pkg/server/service/loadbalancer/wrr/wrr.go +++ b/pkg/server/service/loadbalancer/wrr/wrr.go @@ -12,6 +12,8 @@ import ( "github.com/traefik/traefik/v3/pkg/server/service/loadbalancer" ) +var errNoAvailableServer = errors.New("no available server") + type namedHandler struct { http.Handler name string @@ -40,6 +42,7 @@ type Balancer struct { // updaters is the list of hooks that are run (to update the Balancer // parent(s)), whenever the Balancer status changes. + // No mutex is needed, as it is modified only during the configuration build. updaters []func(bool) sticky *loadbalancer.Sticky @@ -93,7 +96,7 @@ func (b *Balancer) Pop() interface{} { } // SetStatus sets on the balancer that its given child is now of the given -// status. balancerName is only needed for logging purposes. +// status. childName is only needed for logging purposes. func (b *Balancer) SetStatus(ctx context.Context, childName string, up bool) { b.handlersMu.Lock() defer b.handlersMu.Unlock() @@ -138,14 +141,12 @@ func (b *Balancer) SetStatus(ctx context.Context, childName string, up bool) { // Not thread safe. func (b *Balancer) RegisterStatusUpdater(fn func(up bool)) error { if !b.wantsHealthCheck { - return errors.New("healthCheck not enabled in config for this weighted service") + return errors.New("healthCheck not enabled in config for this WRR service") } b.updaters = append(b.updaters, fn) return nil } -var errNoAvailableServer = errors.New("no available server") - func (b *Balancer) nextServer() (*namedHandler, error) { b.handlersMu.Lock() defer b.handlersMu.Unlock() diff --git a/pkg/server/service/service.go b/pkg/server/service/service.go index a8e00cd1d..e61820f3d 100644 --- a/pkg/server/service/service.go +++ b/pkg/server/service/service.go @@ -28,6 +28,8 @@ import ( "github.com/traefik/traefik/v3/pkg/server/middleware" "github.com/traefik/traefik/v3/pkg/server/provider" "github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/failover" + "github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/hrw" + "github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/leasttime" "github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/mirror" "github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/p2c" "github.com/traefik/traefik/v3/pkg/server/service/loadbalancer/wrr" @@ -137,6 +139,13 @@ func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string) (http.H conf.AddError(err, true) return nil, err } + case conf.HighestRandomWeight != nil: + var err error + lb, err = m.getHRWServiceHandler(ctx, serviceName, conf.HighestRandomWeight) + if err != nil { + conf.AddError(err, true) + return nil, err + } case conf.Mirroring != nil: var err error lb, err = m.getMirrorServiceHandler(ctx, conf.Mirroring) @@ -258,19 +267,18 @@ func (m *Manager) getWRRServiceHandler(ctx context.Context, serviceName string, continue } - childName := service.Name updater, ok := serviceHandler.(healthcheck.StatusUpdater) if !ok { - return nil, fmt.Errorf("child service %v of %v not a healthcheck.StatusUpdater (%T)", childName, serviceName, serviceHandler) + return nil, fmt.Errorf("child service %v of %v not a healthcheck.StatusUpdater (%T)", service.Name, serviceName, serviceHandler) } if err := updater.RegisterStatusUpdater(func(up bool) { - balancer.SetStatus(ctx, childName, up) + balancer.SetStatus(ctx, service.Name, up) }); err != nil { - return nil, fmt.Errorf("cannot register %v as updater for %v: %w", childName, serviceName, err) + return nil, fmt.Errorf("cannot register %v as updater for %v: %w", service.Name, serviceName, err) } - log.Ctx(ctx).Debug().Str("parent", serviceName).Str("child", childName). + log.Ctx(ctx).Debug().Str("parent", serviceName).Str("child", service.Name). Msg("Child service will update parent on status change") } @@ -278,13 +286,13 @@ func (m *Manager) getWRRServiceHandler(ctx context.Context, serviceName string, } func (m *Manager) getServiceHandler(ctx context.Context, service dynamic.WRRService) (http.Handler, error) { - switch { - case service.Status != nil: + if service.Status != nil { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { rw.WriteHeader(*service.Status) }), nil + } - case service.GRPCStatus != nil: + if service.GRPCStatus != nil { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { st := status.New(service.GRPCStatus.Code, service.GRPCStatus.Msg) @@ -299,10 +307,57 @@ func (m *Manager) getServiceHandler(ctx context.Context, service dynamic.WRRServ _, _ = rw.Write(body) }), nil - - default: - return m.BuildHTTP(ctx, service.Name) } + + svcHandler, err := m.BuildHTTP(ctx, service.Name) + if err != nil { + return nil, fmt.Errorf("building HTTP service: %w", err) + } + + if service.Headers != nil { + return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + for k, v := range service.Headers { + req.Header.Set(k, v) + } + + svcHandler.ServeHTTP(rw, req) + }), nil + } + + return svcHandler, nil +} + +func (m *Manager) getHRWServiceHandler(ctx context.Context, serviceName string, config *dynamic.HighestRandomWeight) (http.Handler, error) { + // TODO Handle accesslog and metrics with multiple service name + balancer := hrw.New(config.HealthCheck != nil) + for _, service := range shuffle(config.Services, m.rand) { + serviceHandler, err := m.BuildHTTP(ctx, service.Name) + if err != nil { + return nil, err + } + + balancer.Add(service.Name, serviceHandler, service.Weight, false) + + if config.HealthCheck == nil { + continue + } + + updater, ok := serviceHandler.(healthcheck.StatusUpdater) + if !ok { + return nil, fmt.Errorf("child service %v of %v not a healthcheck.StatusUpdater (%T)", service.Name, serviceName, serviceHandler) + } + + if err := updater.RegisterStatusUpdater(func(up bool) { + balancer.SetStatus(ctx, service.Name, up) + }); err != nil { + return nil, fmt.Errorf("cannot register %v as updater for %v: %w", service.Name, serviceName, err) + } + + log.Ctx(ctx).Debug().Str("parent", serviceName).Str("child", service.Name). + Msg("Child service will update parent on status change") + } + + return balancer, nil } type serverBalancer interface { @@ -341,15 +396,30 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName var lb serverBalancer switch service.Strategy { // Here we are handling the empty value to comply with providers that are not applying defaults (e.g. REST provider) - // TODO: remove this when all providers apply default values. + // TODO: remove this empty check when all providers apply default values. case dynamic.BalancerStrategyWRR, "": lb = wrr.New(service.Sticky, service.HealthCheck != nil) case dynamic.BalancerStrategyP2C: lb = p2c.New(service.Sticky, service.HealthCheck != nil) + case dynamic.BalancerStrategyHRW: + lb = hrw.New(service.HealthCheck != nil) + case dynamic.BalancerStrategyLeastTime: + lb = leasttime.New(service.Sticky, service.HealthCheck != nil) default: return nil, fmt.Errorf("unsupported load-balancer strategy %q", service.Strategy) } + var passiveHealthChecker *healthcheck.PassiveServiceHealthChecker + if service.PassiveHealthCheck != nil { + passiveHealthChecker = healthcheck.NewPassiveHealthChecker( + serviceName, + lb, + service.PassiveHealthCheck.MaxFailedAttempts, + service.PassiveHealthCheck.FailureWindow, + service.HealthCheck != nil, + m.observabilityMgr.MetricsRegistry()) + } + healthCheckTargets := make(map[string]*url.URL) for i, server := range shuffle(service.Servers, m.rand) { @@ -368,6 +438,11 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName return nil, fmt.Errorf("error building proxy for server URL %s: %w", server.URL, err) } + if passiveHealthChecker != nil { + // If passive health check is enabled, we wrap the proxy with the passive health checker. + proxy = passiveHealthChecker.WrapHandler(ctx, proxy, target.String()) + } + // The retry wrapping must be done just before the proxy handler, // to make sure that the retry will not be triggered/disabled by // middlewares in the chain. @@ -392,7 +467,7 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName lb.AddServer(server.URL, proxy, server) - // servers are considered UP by default. + // Servers are considered UP by default. info.UpdateServerStatus(target.String(), runtime.StatusUp) healthCheckTargets[server.URL] = target diff --git a/pkg/server/service/service_test.go b/pkg/server/service/service_test.go index 7d00a69fb..574dd1a5b 100644 --- a/pkg/server/service/service_test.go +++ b/pkg/server/service/service_test.go @@ -10,9 +10,11 @@ import ( "net/textproto" "strings" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + ptypes "github.com/traefik/paerser/types" "github.com/traefik/traefik/v3/pkg/config/dynamic" "github.com/traefik/traefik/v3/pkg/config/runtime" "github.com/traefik/traefik/v3/pkg/proxy/httputil" @@ -67,6 +69,33 @@ func TestGetLoadBalancer(t *testing.T) { fwd: &forwarderMock{}, expectError: false, }, + { + desc: "Succeeds when passive health checker is set", + serviceName: "test", + service: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, + PassiveHealthCheck: &dynamic.PassiveServerHealthCheck{ + FailureWindow: ptypes.Duration(30 * time.Second), + MaxFailedAttempts: 3, + }, + }, + fwd: &forwarderMock{}, + expectError: false, + }, + { + desc: "Fails when unsupported strategy is set", + serviceName: "test", + service: &dynamic.ServersLoadBalancer{ + Strategy: "invalid", + Servers: []dynamic.Server{ + { + URL: "http://localhost:8080", + }, + }, + }, + fwd: &forwarderMock{}, + expectError: true, + }, } for _, test := range testCases { @@ -145,7 +174,7 @@ func TestGetLoadBalancerServiceHandler(t *testing.T) { serviceName: "test", service: &dynamic.ServersLoadBalancer{ Strategy: dynamic.BalancerStrategyWRR, - PassHostHeader: boolPtr(true), + PassHostHeader: pointer(true), Servers: []dynamic.Server{ { URL: server1.URL, @@ -464,16 +493,6 @@ func Test1xxResponses(t *testing.T) { } } -type serviceBuilderFunc func(ctx context.Context, serviceName string) (http.Handler, error) - -func (s serviceBuilderFunc) BuildHTTP(ctx context.Context, serviceName string) (http.Handler, error) { - return s(ctx, serviceName) -} - -type internalHandler struct{} - -func (internalHandler) ServeHTTP(_ http.ResponseWriter, _ *http.Request) {} - func TestManager_ServiceBuilders(t *testing.T) { var internalHandler internalHandler @@ -590,7 +609,129 @@ func TestMultipleTypeOnBuildHTTP(t *testing.T) { assert.Error(t, err, "cannot create service: multi-types service not supported, consider declaring two different pieces of service instead") } -func boolPtr(v bool) *bool { return &v } +func TestGetServiceHandler_Headers(t *testing.T) { + pb := httputil.NewProxyBuilder(&transportManagerMock{}, nil) + + testCases := []struct { + desc string + service dynamic.WRRService + userAgent string + expectedHeaders map[string]string + }{ + { + desc: "Service with custom headers", + service: dynamic.WRRService{ + Name: "target-service", + Headers: map[string]string{ + "X-Custom-Header": "custom-value", + "X-Service-Type": "knative-service", + "Authorization": "bearer token123", + }, + }, + userAgent: "test-agent", + expectedHeaders: map[string]string{ + "X-Custom-Header": "custom-value", + "X-Service-Type": "knative-service", + "Authorization": "bearer token123", + }, + }, + { + desc: "Service with empty headers map", + service: dynamic.WRRService{ + Name: "target-service", + Headers: map[string]string{}, + }, + userAgent: "test-agent", + expectedHeaders: map[string]string{}, + }, + { + desc: "Service with nil headers", + service: dynamic.WRRService{ + Name: "target-service", + Headers: nil, + }, + userAgent: "test-agent", + expectedHeaders: map[string]string{}, + }, + { + desc: "Service with headers that override existing request headers", + service: dynamic.WRRService{ + Name: "target-service", + Headers: map[string]string{ + "User-Agent": "overridden-agent", + "Accept": "application/json", + }, + }, + userAgent: "original-agent", + expectedHeaders: map[string]string{ + "User-Agent": "overridden-agent", + "Accept": "application/json", + }, + }, + } + + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + // Create a test server that will verify the headers are properly set for this specific test case + testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // Verify expected headers are present + for key, expectedValue := range test.expectedHeaders { + actualValue := r.Header.Get(key) + assert.Equal(t, expectedValue, actualValue, "Header %s should be %s", key, expectedValue) + } + + w.Header().Set("X-Response", "success") + w.WriteHeader(http.StatusOK) + })) + t.Cleanup(testServer.Close) + + // Create the target service that the WRRService will point to + targetServiceInfo := &runtime.ServiceInfo{ + Service: &dynamic.Service{ + LoadBalancer: &dynamic.ServersLoadBalancer{ + Strategy: dynamic.BalancerStrategyWRR, + Servers: []dynamic.Server{ + {URL: testServer.URL}, + }, + }, + }, + } + + // Create a fresh manager for each test case + sm := NewManager(map[string]*runtime.ServiceInfo{ + "target-service": targetServiceInfo, + }, nil, nil, &transportManagerMock{}, pb) + + // Get the service handler + handler, err := sm.getServiceHandler(t.Context(), test.service) + require.NoError(t, err) + require.NotNil(t, handler) + + // Create a test request + req := testhelpers.MustNewRequest(http.MethodGet, "http://test.example.com/path", nil) + if test.userAgent != "" { + req.Header.Set("User-Agent", test.userAgent) + } + + // Execute the request + recorder := httptest.NewRecorder() + handler.ServeHTTP(recorder, req) + + // Verify the response was successful + assert.Equal(t, http.StatusOK, recorder.Code) + }) + } +} + +type serviceBuilderFunc func(ctx context.Context, serviceName string) (http.Handler, error) + +func (s serviceBuilderFunc) BuildHTTP(ctx context.Context, serviceName string) (http.Handler, error) { + return s(ctx, serviceName) +} + +type internalHandler struct{} + +func (internalHandler) ServeHTTP(_ http.ResponseWriter, _ *http.Request) {} type forwarderMock struct{} diff --git a/pkg/server/service/tcp/service.go b/pkg/server/service/tcp/service.go index 9ff454fad..7928eb3b5 100644 --- a/pkg/server/service/tcp/service.go +++ b/pkg/server/service/tcp/service.go @@ -4,12 +4,15 @@ import ( "context" "errors" "fmt" + "maps" "math/rand" "net" + "slices" "time" "github.com/rs/zerolog/log" "github.com/traefik/traefik/v3/pkg/config/runtime" + "github.com/traefik/traefik/v3/pkg/healthcheck" "github.com/traefik/traefik/v3/pkg/observability/logs" "github.com/traefik/traefik/v3/pkg/server/provider" "github.com/traefik/traefik/v3/pkg/tcp" @@ -17,17 +20,19 @@ import ( // Manager is the TCPHandlers factory. type Manager struct { - dialerManager *tcp.DialerManager - configs map[string]*runtime.TCPServiceInfo - rand *rand.Rand // For the initial shuffling of load-balancers. + dialerManager *tcp.DialerManager + configs map[string]*runtime.TCPServiceInfo + rand *rand.Rand // For the initial shuffling of load-balancers. + healthCheckers map[string]*healthcheck.ServiceTCPHealthChecker } // NewManager creates a new manager. func NewManager(conf *runtime.Configuration, dialerManager *tcp.DialerManager) *Manager { return &Manager{ - dialerManager: dialerManager, - configs: conf.TCPServices, - rand: rand.New(rand.NewSource(time.Now().UnixNano())), + dialerManager: dialerManager, + healthCheckers: make(map[string]*healthcheck.ServiceTCPHealthChecker), + configs: conf.TCPServices, + rand: rand.New(rand.NewSource(time.Now().UnixNano())), } } @@ -51,7 +56,7 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han switch { case conf.LoadBalancer != nil: - loadBalancer := tcp.NewWRRLoadBalancer() + loadBalancer := tcp.NewWRRLoadBalancer(conf.LoadBalancer.HealthCheck != nil) if conf.LoadBalancer.TerminationDelay != nil { log.Ctx(ctx).Warn().Msgf("Service %q load balancer uses `TerminationDelay`, but this option is deprecated, please use ServersTransport configuration instead.", serviceName) @@ -65,6 +70,8 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han conf.LoadBalancer.ServersTransport = provider.GetQualifiedName(ctx, conf.LoadBalancer.ServersTransport) } + uniqHealthCheckTargets := make(map[string]healthcheck.TCPHealthCheckTarget, len(conf.LoadBalancer.Servers)) + for index, server := range shuffle(conf.LoadBalancer.Servers, m.rand) { srvLogger := logger.With(). Int(logs.ServerIndex, index). @@ -86,14 +93,34 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han continue } - loadBalancer.AddServer(handler) + loadBalancer.Add(server.Address, handler, nil) + + // Servers are considered UP by default. + conf.UpdateServerStatus(server.Address, runtime.StatusUp) + + uniqHealthCheckTargets[server.Address] = healthcheck.TCPHealthCheckTarget{ + Address: server.Address, + TLS: server.TLS, + Dialer: dialer, + } + logger.Debug().Msg("Creating TCP server") } + if conf.LoadBalancer.HealthCheck != nil { + m.healthCheckers[serviceName] = healthcheck.NewServiceTCPHealthChecker( + ctx, + conf.LoadBalancer.HealthCheck, + loadBalancer, + conf, + slices.Collect(maps.Values(uniqHealthCheckTargets)), + serviceQualifiedName) + } + return loadBalancer, nil case conf.Weighted != nil: - loadBalancer := tcp.NewWRRLoadBalancer() + loadBalancer := tcp.NewWRRLoadBalancer(conf.Weighted.HealthCheck != nil) for _, service := range shuffle(conf.Weighted.Services, m.rand) { handler, err := m.BuildTCP(ctx, service.Name) @@ -102,7 +129,25 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han return nil, err } - loadBalancer.AddWeightServer(handler, service.Weight) + loadBalancer.Add(service.Name, handler, service.Weight) + + if conf.Weighted.HealthCheck == nil { + continue + } + + updater, ok := handler.(healthcheck.StatusUpdater) + if !ok { + return nil, fmt.Errorf("child service %v of %v not a healthcheck.StatusUpdater (%T)", service.Name, serviceName, handler) + } + + if err := updater.RegisterStatusUpdater(func(up bool) { + loadBalancer.SetStatus(ctx, service.Name, up) + }); err != nil { + return nil, fmt.Errorf("cannot register %v as updater for %v: %w", service.Name, serviceName, err) + } + + log.Ctx(ctx).Debug().Str("parent", serviceName).Str("child", service.Name). + Msg("Child service will update parent on status change") } return loadBalancer, nil @@ -114,6 +159,14 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han } } +// LaunchHealthCheck launches the health checks. +func (m *Manager) LaunchHealthCheck(ctx context.Context) { + for serviceName, hc := range m.healthCheckers { + logger := log.Ctx(ctx).With().Str(logs.ServiceName, serviceName).Logger() + go hc.Launch(logger.WithContext(ctx)) + } +} + func shuffle[T any](values []T, r *rand.Rand) []T { shuffled := make([]T, len(values)) copy(shuffled, values) diff --git a/pkg/server/service/tcp/service_test.go b/pkg/server/service/tcp/service_test.go index 8dd0b7393..bcdfe4bd8 100644 --- a/pkg/server/service/tcp/service_test.go +++ b/pkg/server/service/tcp/service_test.go @@ -233,6 +233,49 @@ func TestManager_BuildTCP(t *testing.T) { providerName: "provider-1", expectedError: "no transport configuration found for \"myServersTransport@provider-1\"", }, + { + desc: "WRR with healthcheck enabled", + stConfigs: map[string]*dynamic.TCPServersTransport{"default@internal": {}}, + serviceName: "serviceName", + configs: map[string]*runtime.TCPServiceInfo{ + "serviceName@provider-1": { + TCPService: &dynamic.TCPService{ + Weighted: &dynamic.TCPWeightedRoundRobin{ + Services: []dynamic.TCPWRRService{ + {Name: "foobar@provider-1", Weight: new(int)}, + {Name: "foobar2@provider-1", Weight: new(int)}, + }, + HealthCheck: &dynamic.HealthCheck{}, + }, + }, + }, + "foobar@provider-1": { + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "192.168.0.12:80", + }, + }, + HealthCheck: &dynamic.TCPServerHealthCheck{}, + }, + }, + }, + "foobar2@provider-1": { + TCPService: &dynamic.TCPService{ + LoadBalancer: &dynamic.TCPServersLoadBalancer{ + Servers: []dynamic.TCPServer{ + { + Address: "192.168.0.13:80", + }, + }, + HealthCheck: &dynamic.TCPServerHealthCheck{}, + }, + }, + }, + }, + providerName: "provider-1", + }, } for _, test := range testCases { diff --git a/pkg/tcp/dialer.go b/pkg/tcp/dialer.go index bc4855b5b..da9a24569 100644 --- a/pkg/tcp/dialer.go +++ b/pkg/tcp/dialer.go @@ -1,6 +1,7 @@ package tcp import ( + "context" "crypto/tls" "crypto/x509" "errors" @@ -33,6 +34,7 @@ type ClientConn interface { // Dialer is an interface to dial a network connection, with support for PROXY protocol and termination delay. type Dialer interface { Dial(network, addr string, clientConn ClientConn) (c net.Conn, err error) + DialContext(ctx context.Context, network, addr string, clientConn ClientConn) (c net.Conn, err error) TerminationDelay() time.Duration } @@ -49,7 +51,12 @@ func (d tcpDialer) TerminationDelay() time.Duration { // Dial dials a network connection and optionally sends a PROXY protocol header. func (d tcpDialer) Dial(network, addr string, clientConn ClientConn) (net.Conn, error) { - conn, err := d.dialer.Dial(network, addr) + return d.DialContext(context.Background(), network, addr, clientConn) +} + +// DialContext dials a network connection and optionally sends a PROXY protocol header, with context. +func (d tcpDialer) DialContext(ctx context.Context, network, addr string, clientConn ClientConn) (net.Conn, error) { + conn, err := d.dialer.DialContext(ctx, network, addr) if err != nil { return nil, err } @@ -72,7 +79,12 @@ type tcpTLSDialer struct { // Dial dials a network connection with the wrapped tcpDialer and performs a TLS handshake. func (d tcpTLSDialer) Dial(network, addr string, clientConn ClientConn) (net.Conn, error) { - conn, err := d.tcpDialer.Dial(network, addr, clientConn) + return d.DialContext(context.Background(), network, addr, clientConn) +} + +// DialContext dials a network connection with the wrapped tcpDialer and performs a TLS handshake, with context. +func (d tcpTLSDialer) DialContext(ctx context.Context, network, addr string, clientConn ClientConn) (net.Conn, error) { + conn, err := d.tcpDialer.DialContext(ctx, network, addr, clientConn) if err != nil { return nil, err } diff --git a/pkg/tcp/wrr_load_balancer.go b/pkg/tcp/wrr_load_balancer.go index 93d43a4fb..c836af17d 100644 --- a/pkg/tcp/wrr_load_balancer.go +++ b/pkg/tcp/wrr_load_balancer.go @@ -1,6 +1,7 @@ package tcp import ( + "context" "errors" "sync" @@ -11,30 +12,42 @@ var errNoServersInPool = errors.New("no servers in the pool") type server struct { Handler + name string weight int } // WRRLoadBalancer is a naive RoundRobin load balancer for TCP services. type WRRLoadBalancer struct { - servers []server - lock sync.Mutex - currentWeight int - index int + // serversMu is a mutex to protect the handlers slice and the status. + serversMu sync.Mutex + servers []server + // status is a record of which child services of the Balancer are healthy, keyed + // by name of child service. A service is initially added to the map when it is + // created via Add, and it is later removed or added to the map as needed, + // through the SetStatus method. + status map[string]struct{} + + // updaters is the list of hooks that are run (to update the Balancer parent(s)), whenever the Balancer status changes. + // No mutex is needed, as it is modified only during the configuration build. + updaters []func(bool) + + index int + currentWeight int + wantsHealthCheck bool } // NewWRRLoadBalancer creates a new WRRLoadBalancer. -func NewWRRLoadBalancer() *WRRLoadBalancer { +func NewWRRLoadBalancer(wantsHealthCheck bool) *WRRLoadBalancer { return &WRRLoadBalancer{ - index: -1, + status: make(map[string]struct{}), + index: -1, + wantsHealthCheck: wantsHealthCheck, } } // ServeTCP forwards the connection to the right service. func (b *WRRLoadBalancer) ServeTCP(conn WriteCloser) { - b.lock.Lock() - next, err := b.next() - b.lock.Unlock() - + next, err := b.nextServer() if err != nil { if !errors.Is(err, errNoServersInPool) { log.Error().Err(err).Msg("Error during load balancing") @@ -46,22 +59,103 @@ func (b *WRRLoadBalancer) ServeTCP(conn WriteCloser) { next.ServeTCP(conn) } -// AddServer appends a server to the existing list. -func (b *WRRLoadBalancer) AddServer(serverHandler Handler) { - w := 1 - b.AddWeightServer(serverHandler, &w) -} - -// AddWeightServer appends a server to the existing list with a weight. -func (b *WRRLoadBalancer) AddWeightServer(serverHandler Handler, weight *int) { - b.lock.Lock() - defer b.lock.Unlock() - +// Add appends a server to the existing list with a name and weight. +func (b *WRRLoadBalancer) Add(name string, handler Handler, weight *int) { w := 1 if weight != nil { w = *weight } - b.servers = append(b.servers, server{Handler: serverHandler, weight: w}) + + b.serversMu.Lock() + b.servers = append(b.servers, server{Handler: handler, name: name, weight: w}) + b.status[name] = struct{}{} + b.serversMu.Unlock() +} + +// SetStatus sets status (UP or DOWN) of a target server. +func (b *WRRLoadBalancer) SetStatus(ctx context.Context, childName string, up bool) { + b.serversMu.Lock() + defer b.serversMu.Unlock() + + upBefore := len(b.status) > 0 + + status := "DOWN" + if up { + status = "UP" + } + + log.Ctx(ctx).Debug().Msgf("Setting status of %s to %v", childName, status) + + if up { + b.status[childName] = struct{}{} + } else { + delete(b.status, childName) + } + + upAfter := len(b.status) > 0 + status = "DOWN" + if upAfter { + status = "UP" + } + + // No Status Change + if upBefore == upAfter { + // We're still with the same status, no need to propagate + log.Ctx(ctx).Debug().Msgf("Still %s, no need to propagate", status) + return + } + + // Status Change + log.Ctx(ctx).Debug().Msgf("Propagating new %s status", status) + for _, fn := range b.updaters { + fn(upAfter) + } +} + +func (b *WRRLoadBalancer) RegisterStatusUpdater(fn func(up bool)) error { + if !b.wantsHealthCheck { + return errors.New("healthCheck not enabled in config for this weighted service") + } + + b.updaters = append(b.updaters, fn) + return nil +} + +func (b *WRRLoadBalancer) nextServer() (Handler, error) { + b.serversMu.Lock() + defer b.serversMu.Unlock() + + if len(b.servers) == 0 || len(b.status) == 0 { + return nil, errNoServersInPool + } + + // The algo below may look messy, but is actually very simple + // it calculates the GCD and subtracts it on every iteration, what interleaves servers + // and allows us not to build an iterator every time we readjust weights. + + // Maximum weight across all enabled servers. + maximum := b.maxWeight() + if maximum == 0 { + return nil, errors.New("all servers have 0 weight") + } + + // GCD across all enabled servers + gcd := b.weightGcd() + + for { + b.index = (b.index + 1) % len(b.servers) + if b.index == 0 { + b.currentWeight -= gcd + if b.currentWeight <= 0 { + b.currentWeight = maximum + } + } + srv := b.servers[b.index] + + if _, ok := b.status[srv.name]; ok && srv.weight >= b.currentWeight { + return srv, nil + } + } } func (b *WRRLoadBalancer) maxWeight() int { @@ -92,36 +186,3 @@ func gcd(a, b int) int { } return a } - -func (b *WRRLoadBalancer) next() (Handler, error) { - if len(b.servers) == 0 { - return nil, errNoServersInPool - } - - // The algo below may look messy, but is actually very simple - // it calculates the GCD and subtracts it on every iteration, what interleaves servers - // and allows us not to build an iterator every time we readjust weights - - // Maximum weight across all enabled servers - maximum := b.maxWeight() - if maximum == 0 { - return nil, errors.New("all servers have 0 weight") - } - - // GCD across all enabled servers - gcd := b.weightGcd() - - for { - b.index = (b.index + 1) % len(b.servers) - if b.index == 0 { - b.currentWeight -= gcd - if b.currentWeight <= 0 { - b.currentWeight = maximum - } - } - srv := b.servers[b.index] - if srv.weight >= b.currentWeight { - return srv, nil - } - } -} diff --git a/pkg/tcp/wrr_load_balancer_test.go b/pkg/tcp/wrr_load_balancer_test.go index fc3438ac6..f51ec4736 100644 --- a/pkg/tcp/wrr_load_balancer_test.go +++ b/pkg/tcp/wrr_load_balancer_test.go @@ -9,50 +9,7 @@ import ( "github.com/stretchr/testify/require" ) -type fakeConn struct { - writeCall map[string]int - closeCall int -} - -func (f *fakeConn) Read(b []byte) (n int, err error) { - panic("implement me") -} - -func (f *fakeConn) Write(b []byte) (n int, err error) { - f.writeCall[string(b)]++ - return len(b), nil -} - -func (f *fakeConn) Close() error { - f.closeCall++ - return nil -} - -func (f *fakeConn) LocalAddr() net.Addr { - panic("implement me") -} - -func (f *fakeConn) RemoteAddr() net.Addr { - panic("implement me") -} - -func (f *fakeConn) SetDeadline(t time.Time) error { - panic("implement me") -} - -func (f *fakeConn) SetReadDeadline(t time.Time) error { - panic("implement me") -} - -func (f *fakeConn) SetWriteDeadline(t time.Time) error { - panic("implement me") -} - -func (f *fakeConn) CloseWrite() error { - panic("implement me") -} - -func TestLoadBalancing(t *testing.T) { +func TestWRRLoadBalancer_LoadBalancing(t *testing.T) { testCases := []struct { desc string serversWeight map[string]int @@ -124,9 +81,9 @@ func TestLoadBalancing(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - balancer := NewWRRLoadBalancer() + balancer := NewWRRLoadBalancer(false) for server, weight := range test.serversWeight { - balancer.AddWeightServer(HandlerFunc(func(conn WriteCloser) { + balancer.Add(server, HandlerFunc(func(conn WriteCloser) { _, err := conn.Write([]byte(server)) require.NoError(t, err) }), &weight) @@ -142,3 +99,196 @@ func TestLoadBalancing(t *testing.T) { }) } } + +func TestWRRLoadBalancer_NoServiceUp(t *testing.T) { + balancer := NewWRRLoadBalancer(false) + + balancer.Add("first", HandlerFunc(func(conn WriteCloser) { + _, err := conn.Write([]byte("first")) + require.NoError(t, err) + }), pointer(1)) + + balancer.Add("second", HandlerFunc(func(conn WriteCloser) { + _, err := conn.Write([]byte("second")) + require.NoError(t, err) + }), pointer(1)) + + balancer.SetStatus(t.Context(), "first", false) + balancer.SetStatus(t.Context(), "second", false) + + conn := &fakeConn{writeCall: make(map[string]int)} + balancer.ServeTCP(conn) + + assert.Empty(t, conn.writeCall) + assert.Equal(t, 1, conn.closeCall) +} + +func TestWRRLoadBalancer_OneServerDown(t *testing.T) { + balancer := NewWRRLoadBalancer(false) + + balancer.Add("first", HandlerFunc(func(conn WriteCloser) { + _, err := conn.Write([]byte("first")) + require.NoError(t, err) + }), pointer(1)) + + balancer.Add("second", HandlerFunc(func(conn WriteCloser) { + _, err := conn.Write([]byte("second")) + require.NoError(t, err) + }), pointer(1)) + + balancer.SetStatus(t.Context(), "second", false) + + conn := &fakeConn{writeCall: make(map[string]int)} + for range 3 { + balancer.ServeTCP(conn) + } + assert.Equal(t, 3, conn.writeCall["first"]) +} + +func TestWRRLoadBalancer_DownThenUp(t *testing.T) { + balancer := NewWRRLoadBalancer(false) + + balancer.Add("first", HandlerFunc(func(conn WriteCloser) { + _, err := conn.Write([]byte("first")) + require.NoError(t, err) + }), pointer(1)) + + balancer.Add("second", HandlerFunc(func(conn WriteCloser) { + _, err := conn.Write([]byte("second")) + require.NoError(t, err) + }), pointer(1)) + + balancer.SetStatus(t.Context(), "second", false) + + conn := &fakeConn{writeCall: make(map[string]int)} + for range 3 { + balancer.ServeTCP(conn) + } + assert.Equal(t, 3, conn.writeCall["first"]) + + balancer.SetStatus(t.Context(), "second", true) + + conn = &fakeConn{writeCall: make(map[string]int)} + for range 2 { + balancer.ServeTCP(conn) + } + assert.Equal(t, 1, conn.writeCall["first"]) + assert.Equal(t, 1, conn.writeCall["second"]) +} + +func TestWRRLoadBalancer_Propagate(t *testing.T) { + balancer1 := NewWRRLoadBalancer(true) + + balancer1.Add("first", HandlerFunc(func(conn WriteCloser) { + _, err := conn.Write([]byte("first")) + require.NoError(t, err) + }), pointer(1)) + + balancer1.Add("second", HandlerFunc(func(conn WriteCloser) { + _, err := conn.Write([]byte("second")) + require.NoError(t, err) + }), pointer(1)) + + balancer2 := NewWRRLoadBalancer(true) + + balancer2.Add("third", HandlerFunc(func(conn WriteCloser) { + _, err := conn.Write([]byte("third")) + require.NoError(t, err) + }), pointer(1)) + + balancer2.Add("fourth", HandlerFunc(func(conn WriteCloser) { + _, err := conn.Write([]byte("fourth")) + require.NoError(t, err) + }), pointer(1)) + + topBalancer := NewWRRLoadBalancer(true) + + topBalancer.Add("balancer1", balancer1, pointer(1)) + _ = balancer1.RegisterStatusUpdater(func(up bool) { + topBalancer.SetStatus(t.Context(), "balancer1", up) + }) + + topBalancer.Add("balancer2", balancer2, pointer(1)) + _ = balancer2.RegisterStatusUpdater(func(up bool) { + topBalancer.SetStatus(t.Context(), "balancer2", up) + }) + + conn := &fakeConn{writeCall: make(map[string]int)} + for range 8 { + topBalancer.ServeTCP(conn) + } + assert.Equal(t, 2, conn.writeCall["first"]) + assert.Equal(t, 2, conn.writeCall["second"]) + assert.Equal(t, 2, conn.writeCall["third"]) + assert.Equal(t, 2, conn.writeCall["fourth"]) + + // fourth gets downed, but balancer2 still up since third is still up. + balancer2.SetStatus(t.Context(), "fourth", false) + + conn = &fakeConn{writeCall: make(map[string]int)} + for range 8 { + topBalancer.ServeTCP(conn) + } + assert.Equal(t, 2, conn.writeCall["first"]) + assert.Equal(t, 2, conn.writeCall["second"]) + assert.Equal(t, 4, conn.writeCall["third"]) + assert.Equal(t, 0, conn.writeCall["fourth"]) + + // third gets downed, and the propagation triggers balancer2 to be marked as + // down as well for topBalancer. + balancer2.SetStatus(t.Context(), "third", false) + + conn = &fakeConn{writeCall: make(map[string]int)} + for range 8 { + topBalancer.ServeTCP(conn) + } + assert.Equal(t, 4, conn.writeCall["first"]) + assert.Equal(t, 4, conn.writeCall["second"]) + assert.Equal(t, 0, conn.writeCall["third"]) + assert.Equal(t, 0, conn.writeCall["fourth"]) +} + +func pointer[T any](v T) *T { return &v } + +type fakeConn struct { + writeCall map[string]int + closeCall int +} + +func (f *fakeConn) Read(b []byte) (n int, err error) { + panic("implement me") +} + +func (f *fakeConn) Write(b []byte) (n int, err error) { + f.writeCall[string(b)]++ + return len(b), nil +} + +func (f *fakeConn) Close() error { + f.closeCall++ + return nil +} + +func (f *fakeConn) LocalAddr() net.Addr { + panic("implement me") +} + +func (f *fakeConn) RemoteAddr() net.Addr { + panic("implement me") +} + +func (f *fakeConn) SetDeadline(t time.Time) error { + panic("implement me") +} + +func (f *fakeConn) SetReadDeadline(t time.Time) error { + panic("implement me") +} + +func (f *fakeConn) SetWriteDeadline(t time.Time) error { + panic("implement me") +} + +func (f *fakeConn) CloseWrite() error { + panic("implement me") +} diff --git a/pkg/tls/zz_generated.deepcopy.go b/pkg/tls/zz_generated.deepcopy.go index a065a73b8..10b4c4ff5 100644 --- a/pkg/tls/zz_generated.deepcopy.go +++ b/pkg/tls/zz_generated.deepcopy.go @@ -4,7 +4,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/types/zz_generated.deepcopy.go b/pkg/types/zz_generated.deepcopy.go index ee095012e..2400d186d 100644 --- a/pkg/types/zz_generated.deepcopy.go +++ b/pkg/types/zz_generated.deepcopy.go @@ -4,7 +4,7 @@ /* The MIT License (MIT) -Copyright (c) 2016-2020 Containous SAS; 2020-2025 Traefik Labs +Copyright (c) 2016-2020 Containous SAS; 2020-2026 Traefik Labs Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/pkg/updater/provider.go b/pkg/updater/provider.go index 26d1fc469..d703bd1a2 100644 --- a/pkg/updater/provider.go +++ b/pkg/updater/provider.go @@ -6,7 +6,9 @@ import ( "net/http" "github.com/rs/zerolog/log" + "github.com/traefik/traefik/v3/pkg/api" "github.com/traefik/traefik/v3/pkg/config/dynamic" + "github.com/traefik/traefik/v3/pkg/config/runtime" "github.com/traefik/traefik/v3/pkg/config/static" "github.com/traefik/traefik/v3/pkg/safe" ) @@ -24,23 +26,23 @@ func New(config *static.Configuration) *Updater { } func (u *Updater) HandleConfigUpdate(cfg dynamic.Configuration) { - body, err := json.Marshal(cfg) + runtimeConfig := runtime.NewConfig(cfg) - if err != nil { - // should never happen? + body := bytes.NewBuffer([]byte{}) + result := api.GetRunTimeRepresentation(runtimeConfig) + + if err := json.NewEncoder(body).Encode(result); err != nil { log.Error().Err(err).Msg("Error while marshalling dynamic configuration data to json") return } - requestBody := bytes.NewBuffer(body) - for _, url := range u.callbackUrls { safe.Go(func() { - resp, err := http.Post(url, "application/json", requestBody) + resp, err := http.Post(url, "application/json", body) if err != nil { log.Error().Err(err).Str("url", url).Msg("Error while sending configuration data to callback") - } else { + } else { log.Debug().Str("url", url).Msg("Configuration data sent") resp.Body.Close() } diff --git a/script/ca-certificates.crt b/script/ca-certificates.crt deleted file mode 100644 index a4e2acce9..000000000 --- a/script/ca-certificates.crt +++ /dev/null @@ -1,3818 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIH0zCCBbugAwIBAgIIXsO3pkN/pOAwDQYJKoZIhvcNAQEFBQAwQjESMBAGA1UE -AwwJQUNDVlJBSVoxMRAwDgYDVQQLDAdQS0lBQ0NWMQ0wCwYDVQQKDARBQ0NWMQsw -CQYDVQQGEwJFUzAeFw0xMTA1MDUwOTM3MzdaFw0zMDEyMzEwOTM3MzdaMEIxEjAQ -BgNVBAMMCUFDQ1ZSQUlaMTEQMA4GA1UECwwHUEtJQUNDVjENMAsGA1UECgwEQUND -VjELMAkGA1UEBhMCRVMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCb -qau/YUqXry+XZpp0X9DZlv3P4uRm7x8fRzPCRKPfmt4ftVTdFXxpNRFvu8gMjmoY -HtiP2Ra8EEg2XPBjs5BaXCQ316PWywlxufEBcoSwfdtNgM3802/J+Nq2DoLSRYWo -G2ioPej0RGy9ocLLA76MPhMAhN9KSMDjIgro6TenGEyxCQ0jVn8ETdkXhBilyNpA -lHPrzg5XPAOBOp0KoVdDaaxXbXmQeOW1tDvYvEyNKKGno6e6Ak4l0Squ7a4DIrhr -IA8wKFSVf+DuzgpmndFALW4ir50awQUZ0m/A8p/4e7MCQvtQqR0tkw8jq8bBD5L/ -0KIV9VMJcRz/RROE5iZe+OCIHAr8Fraocwa48GOEAqDGWuzndN9wrqODJerWx5eH -k6fGioozl2A3ED6XPm4pFdahD9GILBKfb6qkxkLrQaLjlUPTAYVtjrs78yM2x/47 -4KElB0iryYl0/wiPgL/AlmXz7uxLaL2diMMxs0Dx6M/2OLuc5NF/1OVYm3z61PMO -m3WR5LpSLhl+0fXNWhn8ugb2+1KoS5kE3fj5tItQo05iifCHJPqDQsGH+tUtKSpa -cXpkatcnYGMN285J9Y0fkIkyF/hzQ7jSWpOGYdbhdQrqeWZ2iE9x6wQl1gpaepPl -uUsXQA+xtrn13k/c4LOsOxFwYIRKQ26ZIMApcQrAZQIDAQABo4ICyzCCAscwfQYI -KwYBBQUHAQEEcTBvMEwGCCsGAQUFBzAChkBodHRwOi8vd3d3LmFjY3YuZXMvZmls -ZWFkbWluL0FyY2hpdm9zL2NlcnRpZmljYWRvcy9yYWl6YWNjdjEuY3J0MB8GCCsG -AQUFBzABhhNodHRwOi8vb2NzcC5hY2N2LmVzMB0GA1UdDgQWBBTSh7Tj3zcnk1X2 -VuqB5TbMjB4/vTAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNKHtOPfNyeT -VfZW6oHlNsyMHj+9MIIBcwYDVR0gBIIBajCCAWYwggFiBgRVHSAAMIIBWDCCASIG -CCsGAQUFBwICMIIBFB6CARAAQQB1AHQAbwByAGkAZABhAGQAIABkAGUAIABDAGUA -cgB0AGkAZgBpAGMAYQBjAGkA8wBuACAAUgBhAO0AegAgAGQAZQAgAGwAYQAgAEEA -QwBDAFYAIAAoAEEAZwBlAG4AYwBpAGEAIABkAGUAIABUAGUAYwBuAG8AbABvAGcA -7QBhACAAeQAgAEMAZQByAHQAaQBmAGkAYwBhAGMAaQDzAG4AIABFAGwAZQBjAHQA -cgDzAG4AaQBjAGEALAAgAEMASQBGACAAUQA0ADYAMAAxADEANQA2AEUAKQAuACAA -QwBQAFMAIABlAG4AIABoAHQAdABwADoALwAvAHcAdwB3AC4AYQBjAGMAdgAuAGUA -czAwBggrBgEFBQcCARYkaHR0cDovL3d3dy5hY2N2LmVzL2xlZ2lzbGFjaW9uX2Mu -aHRtMFUGA1UdHwROMEwwSqBIoEaGRGh0dHA6Ly93d3cuYWNjdi5lcy9maWxlYWRt -aW4vQXJjaGl2b3MvY2VydGlmaWNhZG9zL3JhaXphY2N2MV9kZXIuY3JsMA4GA1Ud -DwEB/wQEAwIBBjAXBgNVHREEEDAOgQxhY2N2QGFjY3YuZXMwDQYJKoZIhvcNAQEF -BQADggIBAJcxAp/n/UNnSEQU5CmH7UwoZtCPNdpNYbdKl02125DgBS4OxnnQ8pdp -D70ER9m+27Up2pvZrqmZ1dM8MJP1jaGo/AaNRPTKFpV8M9xii6g3+CfYCS0b78gU -JyCpZET/LtZ1qmxNYEAZSUNUY9rizLpm5U9EelvZaoErQNV/+QEnWCzI7UiRfD+m -AM/EKXMRNt6GGT6d7hmKG9Ww7Y49nCrADdg9ZuM8Db3VlFzi4qc1GwQA9j9ajepD -vV+JHanBsMyZ4k0ACtrJJ1vnE5Bc5PUzolVt3OAJTS+xJlsndQAJxGJ3KQhfnlms -tn6tn1QwIgPBHnFk/vk4CpYY3QIUrCPLBhwepH2NDd4nQeit2hW3sCPdK6jT2iWH -7ehVRE2I9DZ+hJp4rPcOVkkO1jMl1oRQQmwgEh0q1b688nCBpHBgvgW1m54ERL5h -I6zppSSMEYCUWqKiuUnSwdzRp+0xESyeGabu4VXhwOrPDYTkF7eifKXeVSUG7szA -h1xA2syVP1XgNce4hL60Xc16gwFy7ofmXx2utYXGJt/mwZrpHgJHnyqobalbz+xF -d3+YJ5oyXSrjhO7FmGYvliAd3djDJ9ew+f7Zfc3Qn48LFFhRny+Lwzgt3uiP1o2H -pPVWQxaZLPSkVrQ0uGE3ycJYgBugl6H8WY3pEfbRD0tVNEYqi4Y7 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsx -CzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJ -WiBGTk1ULVJDTTAeFw0wODEwMjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJ -BgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBG -Tk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALpxgHpMhm5/ -yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcfqQgf -BBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAz -WHFctPVrbtQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxF -tBDXaEAUwED653cXeuYLj2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z -374jNUUeAlz+taibmSXaXvMiwzn15Cou08YfxGyqxRxqAQVKL9LFwag0Jl1mpdIC -IfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mwWsXmo8RZZUc1g16p6DUL -mbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnTtOmlcYF7 -wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peS -MKGJ47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2 -ZSysV4999AeU14ECll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMet -UqIJ5G+GR4of6ygnXYMgrwTJbFaai0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUw -AwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFPd9xf3E6Jobd2Sn9R2gzL+H -YJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3 -LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD -nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1 -RXxlDPiyN8+sD8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYM -LVN0V2Ue1bLdI4E7pWYjJ2cJj+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf -77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrTQfv6MooqtyuGC2mDOL7Nii4LcK2N -JpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW+YJF1DngoABd15jm -fZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7Ixjp -6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp -1txyM/1d8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B -9kiABdcPUXmsEKvU7ANm5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wok -RqEIr9baRRmW1FMdW4R58MD3R++Lj8UGrp1MYp3/RgT408m2ECVAdf4WqslKYIYv -uu8wd+RU4riEmViAqhOLUTpPSPaLtrM= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFuzCCA6OgAwIBAgIIVwoRl0LE48wwDQYJKoZIhvcNAQELBQAwazELMAkGA1UE -BhMCSVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8w -MzM1ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290 -IENBMB4XDTExMDkyMjExMjIwMloXDTMwMDkyMjExMjIwMlowazELMAkGA1UEBhMC -SVQxDjAMBgNVBAcMBU1pbGFuMSMwIQYDVQQKDBpBY3RhbGlzIFMucC5BLi8wMzM1 -ODUyMDk2NzEnMCUGA1UEAwweQWN0YWxpcyBBdXRoZW50aWNhdGlvbiBSb290IENB -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAp8bEpSmkLO/lGMWwUKNv -UTufClrJwkg4CsIcoBh/kbWHuUA/3R1oHwiD1S0eiKD4j1aPbZkCkpAW1V8IbInX -4ay8IMKx4INRimlNAJZaby/ARH6jDuSRzVju3PvHHkVH3Se5CAGfpiEd9UEtL0z9 -KK3giq0itFZljoZUj5NDKd45RnijMCO6zfB9E1fAXdKDa0hMxKufgFpbOr3JpyI/ -gCczWw63igxdBzcIy2zSekciRDXFzMwujt0q7bd9Zg1fYVEiVRvjRuPjPdA1Yprb -rxTIW6HMiRvhMCb8oJsfgadHHwTrozmSBp+Z07/T6k9QnBn+locePGX2oxgkg4YQ -51Q+qDp2JE+BIcXjDwL4k5RHILv+1A7TaLndxHqEguNTVHnd25zS8gebLra8Pu2F -be8lEfKXGkJh90qX6IuxEAf6ZYGyojnP9zz/GPvG8VqLWeICrHuS0E4UT1lF9gxe -KF+w6D9Fz8+vm2/7hNN3WpVvrJSEnu68wEqPSpP4RCHiMUVhUE4Q2OM1fEwZtN4F -v6MGn8i1zeQf1xcGDXqVdFUNaBr8EBtiZJ1t4JWgw5QHVw0U5r0F+7if5t+L4sbn -fpb2U8WANFAoWPASUHEXMLrmeGO89LKtmyuy/uE5jF66CyCU3nuDuP/jVo23Eek7 -jPKxwV2dpAtMK9myGPW1n0sCAwEAAaNjMGEwHQYDVR0OBBYEFFLYiDrIn3hm7Ynz -ezhwlMkCAjbQMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUUtiIOsifeGbt -ifN7OHCUyQICNtAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAL -e3KHwGCmSUyIWOYdiPcUZEim2FgKDk8TNd81HdTtBjHIgT5q1d07GjLukD0R0i70 -jsNjLiNmsGe+b7bAEzlgqqI0JZN1Ut6nna0Oh4lScWoWPBkdg/iaKWW+9D+a2fDz -WochcYBNy+A4mz+7+uAwTc+G02UQGRjRlwKxK3JCaKygvU5a2hi/a5iB0P2avl4V -SM0RFbnAKVy06Ij3Pjaut2L9HmLecHgQHEhb2rykOLpn7VU+Xlff1ANATIGk0k9j -pwlCCRT8AKnCgHNPLsBA2RF7SOp6AsDT6ygBJlh0wcBzIm2Tlf05fbsq4/aC4yyX -X04fkZT6/iyj2HYauE2yOE+b+h1IYHkm4vP9qdCa6HCPSXrW5b0KDtst842/6+Ok -fcvHlXHo2qN8xcL4dJIEG4aspCJTQLas/kx2z/uUMsA1n3Y/buWQbqCmJqK4LL7R -K4X9p2jIugErsWx0Hbhzlefut8cl8ABMALJ+tguLHPPAUJ4lueAI3jZm/zel0btU -ZCzJJ7VLkn5l/9Mt4blOvH+kQSGQQXemOR/qnuOf0GZvBeyqdn6/axag67XH/JJU -LysRJyU3eExRarDzzFhdFPFqSBX/wge2sY0PjlxQRrM9vwGYT7JZVEc+NHt4bVaT -LnPqZih4zR0Uv6CPLy64Lo7yFIrM6bV8+2ydDKXhlg== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU -MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs -IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290 -MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux -FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h -bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v -dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt -H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9 -uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX -mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX -a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN -E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0 -WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD -VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0 -Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU -cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx -IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN -AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH -YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5 -6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC -Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX -c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a -mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEGDCCAwCgAwIBAgIBATANBgkqhkiG9w0BAQUFADBlMQswCQYDVQQGEwJTRTEU -MBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3 -b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwHhcNMDAwNTMw -MTAzODMxWhcNMjAwNTMwMTAzODMxWjBlMQswCQYDVQQGEwJTRTEUMBIGA1UEChML -QWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFkZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYD -VQQDExhBZGRUcnVzdCBDbGFzcyAxIENBIFJvb3QwggEiMA0GCSqGSIb3DQEBAQUA -A4IBDwAwggEKAoIBAQCWltQhSWDia+hBBwzexODcEyPNwTXH+9ZOEQpnXvUGW2ul -CDtbKRY654eyNAbFvAWlA3yCyykQruGIgb3WntP+LVbBFc7jJp0VLhD7Bo8wBN6n -tGO0/7Gcrjyvd7ZWxbWroulpOj0OM3kyP3CCkplhbY0wCI9xP6ZIVxn4JdxLZlyl -dI+Yrsj5wAYi56xz36Uu+1LcsRVlIPo1Zmne3yzxbrww2ywkEtvrNTVokMsAsJch -PXQhI2U0K7t4WaPW4XY5mqRJjox0r26kmqPZm9I4XJuiGMx1I4S+6+JNM3GOGvDC -+Mcdoq0Dlyz4zyXG9rgkMbFjXZJ/Y/AlyVMuH79NAgMBAAGjgdIwgc8wHQYDVR0O -BBYEFJWxtPCUtr3H2tERCSG+wa9J/RB7MAsGA1UdDwQEAwIBBjAPBgNVHRMBAf8E -BTADAQH/MIGPBgNVHSMEgYcwgYSAFJWxtPCUtr3H2tERCSG+wa9J/RB7oWmkZzBl -MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxHTAbBgNVBAsTFEFk -ZFRydXN0IFRUUCBOZXR3b3JrMSEwHwYDVQQDExhBZGRUcnVzdCBDbGFzcyAxIENB -IFJvb3SCAQEwDQYJKoZIhvcNAQEFBQADggEBACxtZBsfzQ3duQH6lmM0MkhHma6X -7f1yFqZzR1r0693p9db7RcwpiURdv0Y5PejuvE1Uhh4dbOMXJ0PhiVYrqW9yTkkz -43J8KiOavD7/KCrto/8cI7pDVwlnTUtiBi34/2ydYB7YHEt9tTEv2dB8Xfjea4MY -eDdXL+gzB2ffHsdrKpV2ro9Xo/D0UrSpUwjP4E/TelOL/bscVjby/rK25Xa71SJl -pz/+0WatC7xrmYbvP33zGDLKe8bjq2RGlfgmadlVg3sslgf/WSxEo8bl6ancoWOA -WiFeIc9TVPC6b4nbqKqVz4vjccweGyBECMB6tkD9xOQ14R0WHNC8K47Wcdk= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDTDCCAjSgAwIBAgIId3cGJyapsXwwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz -dCBDb21tZXJjaWFsMB4XDTEwMDEyOTE0MDYwNloXDTMwMTIzMTE0MDYwNlowRDEL -MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp -cm1UcnVzdCBDb21tZXJjaWFsMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEA9htPZwcroRX1BiLLHwGy43NFBkRJLLtJJRTWzsO3qyxPxkEylFf6EqdbDuKP -Hx6GGaeqtS25Xw2Kwq+FNXkyLbscYjfysVtKPcrNcV/pQr6U6Mje+SJIZMblq8Yr -ba0F8PrVC8+a5fBQpIs7R6UjW3p6+DM/uO+Zl+MgwdYoic+U+7lF7eNAFxHUdPAL -MeIrJmqbTFeurCA+ukV6BfO9m2kVrn1OIGPENXY6BwLJN/3HR+7o8XYdcxXyl6S1 -yHp52UKqK39c/s4mT6NmgTWvRLpUHhwwMmWd5jyTXlBOeuM61G7MGvv50jeuJCqr -VwMiKA1JdX+3KNp1v47j3A55MQIDAQABo0IwQDAdBgNVHQ4EFgQUnZPGU4teyq8/ -nx4P5ZmVvCT2lI8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ -KoZIhvcNAQELBQADggEBAFis9AQOzcAN/wr91LoWXym9e2iZWEnStB03TX8nfUYG -XUPGhi4+c7ImfU+TqbbEKpqrIZcUsd6M06uJFdhrJNTxFq7YpFzUf1GO7RgBsZNj -vbz4YYCanrHOQnDiqX0GJX0nof5v7LMeJNrjS1UaADs1tDvZ110w/YETifLCBivt -Z8SOyUOyXGsViQK8YvxO8rUzqrJv0wqiUOP2O+guRMLbZjipM1ZI8W0bM40NjD9g -N53Tym1+NH4Nn3J2ixufcv1SNUFFApYvHLKac0khsUlHRUe072o0EclNmsxZt9YC -nlpOZbWUrhvfKbAW8b8Angc6F2S1BLUjIZkKlTuXfO8= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDTDCCAjSgAwIBAgIIfE8EORzUmS0wDQYJKoZIhvcNAQEFBQAwRDELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZpcm1UcnVz -dCBOZXR3b3JraW5nMB4XDTEwMDEyOTE0MDgyNFoXDTMwMTIzMTE0MDgyNFowRDEL -MAkGA1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MR8wHQYDVQQDDBZBZmZp -cm1UcnVzdCBOZXR3b3JraW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEAtITMMxcua5Rsa2FSoOujz3mUTOWUgJnLVWREZY9nZOIG41w3SfYvm4SEHi3y -YJ0wTsyEheIszx6e/jarM3c1RNg1lho9Nuh6DtjVR6FqaYvZ/Ls6rnla1fTWcbua -kCNrmreIdIcMHl+5ni36q1Mr3Lt2PpNMCAiMHqIjHNRqrSK6mQEubWXLviRmVSRL -QESxG9fhwoXA3hA/Pe24/PHxI1Pcv2WXb9n5QHGNfb2V1M6+oF4nI979ptAmDgAp -6zxG8D1gvz9Q0twmQVGeFDdCBKNwV6gbh+0t+nvujArjqWaJGctB+d1ENmHP4ndG -yH329JKBNv3bNPFyfvMMFr20FQIDAQABo0IwQDAdBgNVHQ4EFgQUBx/S55zawm6i -QLSwelAQUHTEyL0wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwDQYJ -KoZIhvcNAQEFBQADggEBAIlXshZ6qML91tmbmzTCnLQyFE2npN/svqe++EPbkTfO -tDIuUFUaNU52Q3Eg75N3ThVwLofDwR1t3Mu1J9QsVtFSUzpE0nPIxBsFZVpikpzu -QY0x2+c06lkh1QF612S4ZDnNye2v7UsDSKegmQGA3GWjNq5lWUhPgkvIZfFXHeVZ -Lgo/bNjR9eUJtGxUAArgFU2HdW23WJZa3W3SAKD0m0i+wzekujbgfIeFlxoVot4u -olu9rxj5kFDNcFn4J2dHy8egBzp90SxdbBk6ZrV9/ZFvgrG+CJPbFEfxojfHRZ48 -x3evZKiT3/Zpg4Jg8klCNO1aAFSFHBY2kgxc+qatv9s= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFRjCCAy6gAwIBAgIIbYwURrGmCu4wDQYJKoZIhvcNAQEMBQAwQTELMAkGA1UE -BhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1UcnVz -dCBQcmVtaXVtMB4XDTEwMDEyOTE0MTAzNloXDTQwMTIzMTE0MTAzNlowQTELMAkG -A1UEBhMCVVMxFDASBgNVBAoMC0FmZmlybVRydXN0MRwwGgYDVQQDDBNBZmZpcm1U -cnVzdCBQcmVtaXVtMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxBLf -qV/+Qd3d9Z+K4/as4Tx4mrzY8H96oDMq3I0gW64tb+eT2TZwamjPjlGjhVtnBKAQ -JG9dKILBl1fYSCkTtuG+kU3fhQxTGJoeJKJPj/CihQvL9Cl/0qRY7iZNyaqoe5rZ -+jjeRFcV5fiMyNlI4g0WJx0eyIOFJbe6qlVBzAMiSy2RjYvmia9mx+n/K+k8rNrS -s8PhaJyJ+HoAVt70VZVs+7pk3WKL3wt3MutizCaam7uqYoNMtAZ6MMgpv+0GTZe5 -HMQxK9VfvFMSF5yZVylmd2EhMQcuJUmdGPLu8ytxjLW6OQdJd/zvLpKQBY0tL3d7 -70O/Nbua2Plzpyzy0FfuKE4mX4+QaAkvuPjcBukumj5Rp9EixAqnOEhss/n/fauG -V+O61oV4d7pD6kh/9ti+I20ev9E2bFhc8e6kGVQa9QPSdubhjL08s9NIS+LI+H+S -qHZGnEJlPqQewQcDWkYtuJfzt9WyVSHvutxMAJf7FJUnM7/oQ0dG0giZFmA7mn7S -5u046uwBHjxIVkkJx0w3AJ6IDsBz4W9m6XJHMD4Q5QsDyZpCAGzFlH5hxIrff4Ia -C1nEWTJ3s7xgaVY5/bQGeyzWZDbZvUjthB9+pSKPKrhC9IK31FOQeE4tGv2Bb0TX -OwF0lkLgAOIua+rF7nKsu7/+6qqo+Nz2snmKtmcCAwEAAaNCMEAwHQYDVR0OBBYE -FJ3AZ6YMItkm9UWrpmVSESfYRaxjMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ -BAQDAgEGMA0GCSqGSIb3DQEBDAUAA4ICAQCzV00QYk465KzquByvMiPIs0laUZx2 -KI15qldGF9X1Uva3ROgIRL8YhNILgM3FEv0AVQVhh0HctSSePMTYyPtwni94loMg -Nt58D2kTiKV1NpgIpsbfrM7jWNa3Pt668+s0QNiigfV4Py/VpfzZotReBA4Xrf5B -8OWycvpEgjNC6C1Y91aMYj+6QrCcDFx+LmUmXFNPALJ4fqENmS2NuB2OosSw/WDQ -MKSOyARiqcTtNd56l+0OOF6SL5Nwpamcb6d9Ex1+xghIsV5n61EIJenmJWtSKZGc -0jlzCFfemQa0W50QBuHCAKi4HEoCChTQwUHK+4w1IX2COPKpVJEZNZOUbWo6xbLQ -u4mGk+ibyQ86p3q4ofB4Rvr8Ny/lioTz3/4E2aFooC8k4gmVBtWVyuEklut89pMF -u+1z6S3RdTnX5yTb2E5fQ4+e0BQ5v1VwSJlXMbSc7kqYA5YwH2AG7hsj/oFgIxpH -YoWlzBk0gG+zrBrjn/B7SK3VAdlntqlyk+otZrWyuOQ9PLLvTIzq6we/qzWaVYa8 -GKa1qF60g2xraUDTn9zxw2lrueFtCfTxqlB2Cnp9ehehVZZCmTEJ3WARjQUwfuaO -RtGdFNrHF+QFlozEJLUbzxQHskD4o55BhrwE0GuWyCqANP2/7waj3VjFhT0+j/6e -KeC2uAloGRwYQw== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIB/jCCAYWgAwIBAgIIdJclisc/elQwCgYIKoZIzj0EAwMwRTELMAkGA1UEBhMC -VVMxFDASBgNVBAoMC0FmZmlybVRydXN0MSAwHgYDVQQDDBdBZmZpcm1UcnVzdCBQ -cmVtaXVtIEVDQzAeFw0xMDAxMjkxNDIwMjRaFw00MDEyMzExNDIwMjRaMEUxCzAJ -BgNVBAYTAlVTMRQwEgYDVQQKDAtBZmZpcm1UcnVzdDEgMB4GA1UEAwwXQWZmaXJt -VHJ1c3QgUHJlbWl1bSBFQ0MwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQNMF4bFZ0D -0KF5Nbc6PJJ6yhUczWLznCZcBz3lVPqj1swS6vQUX+iOGasvLkjmrBhDeKzQN8O9 -ss0s5kfiGuZjuD0uL3jET9v0D6RoTFVya5UdThhClXjMNzyR4ptlKymjQjBAMB0G -A1UdDgQWBBSaryl6wBE1NSZRMADDav5A1a7WPDAPBgNVHRMBAf8EBTADAQH/MA4G -A1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjAXCfOHiFBar8jAQr9HX/Vs -aobgxCd05DhT1wV/GzTjxi+zygk8N53X57hG8f2h4nECMEJZh0PUUd+60wkyWs6I -flc9nF9Ca/UHLbXwgpP5WW+uZPpY5Yse42O+tYHNbwKMeQ== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF -ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 -b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL -MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv -b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj -ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM -9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw -IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6 -VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L -93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm -jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA -A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI -U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs -N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv -o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU -5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy -rqXRfboQnoZsG4q5WTP468SQvvG5 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFQTCCAymgAwIBAgITBmyf0pY1hp8KD+WGePhbJruKNzANBgkqhkiG9w0BAQwF -ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6 -b24gUm9vdCBDQSAyMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTEL -MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv -b3QgQ0EgMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK2Wny2cSkxK -gXlRmeyKy2tgURO8TW0G/LAIjd0ZEGrHJgw12MBvIITplLGbhQPDW9tK6Mj4kHbZ -W0/jTOgGNk3Mmqw9DJArktQGGWCsN0R5hYGCrVo34A3MnaZMUnbqQ523BNFQ9lXg -1dKmSYXpN+nKfq5clU1Imj+uIFptiJXZNLhSGkOQsL9sBbm2eLfq0OQ6PBJTYv9K -8nu+NQWpEjTj82R0Yiw9AElaKP4yRLuH3WUnAnE72kr3H9rN9yFVkE8P7K6C4Z9r -2UXTu/Bfh+08LDmG2j/e7HJV63mjrdvdfLC6HM783k81ds8P+HgfajZRRidhW+me -z/CiVX18JYpvL7TFz4QuK/0NURBs+18bvBt+xa47mAExkv8LV/SasrlX6avvDXbR -8O70zoan4G7ptGmh32n2M8ZpLpcTnqWHsFcQgTfJU7O7f/aS0ZzQGPSSbtqDT6Zj -mUyl+17vIWR6IF9sZIUVyzfpYgwLKhbcAS4y2j5L9Z469hdAlO+ekQiG+r5jqFoz -7Mt0Q5X5bGlSNscpb/xVA1wf+5+9R+vnSUeVC06JIglJ4PVhHvG/LopyboBZ/1c6 -+XUyo05f7O0oYtlNc/LMgRdg7c3r3NunysV+Ar3yVAhU/bQtCSwXVEqY0VThUWcI -0u1ufm8/0i2BWSlmy5A5lREedCf+3euvAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMB -Af8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSwDPBMMPQFWAJI/TPlUq9LhONm -UjANBgkqhkiG9w0BAQwFAAOCAgEAqqiAjw54o+Ci1M3m9Zh6O+oAA7CXDpO8Wqj2 -LIxyh6mx/H9z/WNxeKWHWc8w4Q0QshNabYL1auaAn6AFC2jkR2vHat+2/XcycuUY -+gn0oJMsXdKMdYV2ZZAMA3m3MSNjrXiDCYZohMr/+c8mmpJ5581LxedhpxfL86kS -k5Nrp+gvU5LEYFiwzAJRGFuFjWJZY7attN6a+yb3ACfAXVU3dJnJUH/jWS5E4ywl -7uxMMne0nxrpS10gxdr9HIcWxkPo1LsmmkVwXqkLN1PiRnsn/eBG8om3zEK2yygm -btmlyTrIQRNg91CMFa6ybRoVGld45pIq2WWQgj9sAq+uEjonljYE1x2igGOpm/Hl -urR8FLBOybEfdF849lHqm/osohHUqS0nGkWxr7JOcQ3AWEbWaQbLU8uz/mtBzUF+ -fUwPfHJ5elnNXkoOrJupmHN5fLT0zLm4BwyydFy4x2+IoZCn9Kr5v2c69BoVYh63 -n749sSmvZ6ES8lgQGVMDMBu4Gon2nL2XA46jCfMdiyHxtN/kHNGfZQIG6lzWE7OE -76KlXIx3KadowGuuQNKotOrN8I1LOJwZmhsoVLiJkO/KdYE+HvJkJMcYr07/R54H -9jVlpNMKVv/1F2Rs76giJUmTtt8AF9pYfl3uxRuw0dFfIRDH+fO6AgonB8Xx1sfT -4PsJYGw= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIBtjCCAVugAwIBAgITBmyf1XSXNmY/Owua2eiedgPySjAKBggqhkjOPQQDAjA5 -MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g -Um9vdCBDQSAzMB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG -A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg -Q0EgMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABCmXp8ZBf8ANm+gBG1bG8lKl -ui2yEujSLtf6ycXYqm0fc4E7O5hrOXwzpcVOho6AF2hiRVd9RFgdszflZwjrZt6j -QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBSr -ttvXBp43rDCGB5Fwx5zEGbF4wDAKBggqhkjOPQQDAgNJADBGAiEA4IWSoxe3jfkr -BqWTrBqYaGFy+uGh0PsceGCmQ5nFuMQCIQCcAu/xlJyzlvnrxir4tiz+OpAUFteM -YyRIHN8wfdVoOw== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIB8jCCAXigAwIBAgITBmyf18G7EEwpQ+Vxe3ssyBrBDjAKBggqhkjOPQQDAzA5 -MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6b24g -Um9vdCBDQSA0MB4XDTE1MDUyNjAwMDAwMFoXDTQwMDUyNjAwMDAwMFowOTELMAkG -A1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJvb3Qg -Q0EgNDB2MBAGByqGSM49AgEGBSuBBAAiA2IABNKrijdPo1MN/sGKe0uoe0ZLY7Bi -9i0b2whxIdIA6GO9mif78DluXeo9pcmBqqNbIJhFXRbb/egQbeOc4OO9X4Ri83Bk -M6DLJC9wuoihKqB1+IGuYgbEgds5bimwHvouXKNCMEAwDwYDVR0TAQH/BAUwAwEB -/zAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0OBBYEFNPsxzplbszh2naaVvuc84ZtV+WB -MAoGCCqGSM49BAMDA2gAMGUCMDqLIfG9fhGt0O9Yli/W651+kI0rz2ZVwyzjKKlw -CkcO8DdZEv8tmZQoTipPNU0zWgIxAOp1AE47xDqUEpHJWEadIRNyp4iciuRMStuW -1KyLa2tJElMzrdfkviT8tQp21KW8EA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIIXDPLYixfszIwDQYJKoZIhvcNAQELBQAwPDEeMBwGA1UE -AwwVQXRvcyBUcnVzdGVkUm9vdCAyMDExMQ0wCwYDVQQKDARBdG9zMQswCQYDVQQG -EwJERTAeFw0xMTA3MDcxNDU4MzBaFw0zMDEyMzEyMzU5NTlaMDwxHjAcBgNVBAMM -FUF0b3MgVHJ1c3RlZFJvb3QgMjAxMTENMAsGA1UECgwEQXRvczELMAkGA1UEBhMC -REUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCVhTuXbyo7LjvPpvMp -Nb7PGKw+qtn4TaA+Gke5vJrf8v7MPkfoepbCJI419KkM/IL9bcFyYie96mvr54rM -VD6QUM+A1JX76LWC1BTFtqlVJVfbsVD2sGBkWXppzwO3bw2+yj5vdHLqqjAqc2K+ -SZFhyBH+DgMq92og3AIVDV4VavzjgsG1xZ1kCWyjWZgHJ8cblithdHFsQ/H3NYkQ -4J7sVaE3IqKHBAUsR320HLliKWYoyrfhk/WklAOZuXCFteZI6o1Q/NnezG8HDt0L -cp2AMBYHlT8oDv3FdU9T1nSatCQujgKRz3bFmx5VdJx4IbHwLfELn8LVlhgf8FQi -eowHAgMBAAGjfTB7MB0GA1UdDgQWBBSnpQaxLKYJYO7Rl+lwrrw7GWzbITAPBgNV -HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFKelBrEspglg7tGX6XCuvDsZbNshMBgG -A1UdIAQRMA8wDQYLKwYBBAGwLQMEAQEwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3 -DQEBCwUAA4IBAQAmdzTblEiGKkGdLD4GkGDEjKwLVLgfuXvTBznk+j57sj1O7Z8j -vZfza1zv7v1Apt+hk6EKhqzvINB5Ab149xnYJDE0BAGmuhWawyfc2E8PzBhj/5kP -DpFrdRbhIfzYJsdHt6bPWHJxfrrhTZVHO8mvbaG0weyJ9rQPOLXiZNwlz6bb65pc -maHFCN795trV1lpFDMS3wrUU77QR/w4VtfX128a961qn8FYiqTxlVMYVqL2Gns2D -lmh6cYGJ4Qvh6hEbaAjMaZ7snkGeRDImeuKHCnE96+RapNLbxc3G3mB/ufNPRJLv -KrcYPqcZ2Qt9sTdBQrC6YB3y/gkRsPCHe6ed ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGFDCCA/ygAwIBAgIIU+w77vuySF8wDQYJKoZIhvcNAQEFBQAwUTELMAkGA1UE -BhMCRVMxQjBABgNVBAMMOUF1dG9yaWRhZCBkZSBDZXJ0aWZpY2FjaW9uIEZpcm1h -cHJvZmVzaW9uYWwgQ0lGIEE2MjYzNDA2ODAeFw0wOTA1MjAwODM4MTVaFw0zMDEy -MzEwODM4MTVaMFExCzAJBgNVBAYTAkVTMUIwQAYDVQQDDDlBdXRvcmlkYWQgZGUg -Q2VydGlmaWNhY2lvbiBGaXJtYXByb2Zlc2lvbmFsIENJRiBBNjI2MzQwNjgwggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDKlmuO6vj78aI14H9M2uDDUtd9 -thDIAl6zQyrET2qyyhxdKJp4ERppWVevtSBC5IsP5t9bpgOSL/UR5GLXMnE42QQM -cas9UX4PB99jBVzpv5RvwSmCwLTaUbDBPLutN0pcyvFLNg4kq7/DhHf9qFD0sefG -L9ItWY16Ck6WaVICqjaY7Pz6FIMMNx/Jkjd/14Et5cS54D40/mf0PmbR0/RAz15i -NA9wBj4gGFrO93IbJWyTdBSTo3OxDqqHECNZXyAFGUftaI6SEspd/NYrspI8IM/h -X68gvqB2f3bl7BqGYTM+53u0P6APjqK5am+5hyZvQWyIplD9amML9ZMWGxmPsu2b -m8mQ9QEM3xk9Dz44I8kvjwzRAv4bVdZO0I08r0+k8/6vKtMFnXkIoctXMbScyJCy -Z/QYFpM6/EfY0XiWMR+6KwxfXZmtY4laJCB22N/9q06mIqqdXuYnin1oKaPnirja -EbsXLZmdEyRG98Xi2J+Of8ePdG1asuhy9azuJBCtLxTa/y2aRnFHvkLfuwHb9H/T -KI8xWVvTyQKmtFLKbpf7Q8UIJm+K9Lv9nyiqDdVF8xM6HdjAeI9BZzwelGSuewvF -6NkBiDkal4ZkQdU7hwxu+g/GvUgUvzlN1J5Bto+WHWOWk9mVBngxaJ43BjuAiUVh -OSPHG0SjFeUc+JIwuwIDAQABo4HvMIHsMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYD -VR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRlzeurNR4APn7VdMActHNHDhpkLzCBpgYD -VR0gBIGeMIGbMIGYBgRVHSAAMIGPMC8GCCsGAQUFBwIBFiNodHRwOi8vd3d3LmZp -cm1hcHJvZmVzaW9uYWwuY29tL2NwczBcBggrBgEFBQcCAjBQHk4AUABhAHMAZQBv -ACAAZABlACAAbABhACAAQgBvAG4AYQBuAG8AdgBhACAANAA3ACAAQgBhAHIAYwBl -AGwAbwBuAGEAIAAwADgAMAAxADcwDQYJKoZIhvcNAQEFBQADggIBABd9oPm03cXF -661LJLWhAqvdpYhKsg9VSytXjDvlMd3+xDLx51tkljYyGOylMnfX40S2wBEqgLk9 -am58m9Ot/MPWo+ZkKXzR4Tgegiv/J2Wv+xYVxC5xhOW1//qkR71kMrv2JYSiJ0L1 -ILDCExARzRAVukKQKtJE4ZYm6zFIEv0q2skGz3QeqUvVhyj5eTSSPi5E6PaPT481 -PyWzOdxjKpBrIF/EUhJOlywqrJ2X3kjyo2bbwtKDlaZmp54lD+kLM5FlClrD2VQS -3a/DTg4fJl4N3LON7NWBcN7STyQF82xO9UxJZo3R/9ILJUFI/lGExkKvgATP0H5k -SeTy36LssUzAKh3ntLFlosS88Zj0qnAHY7S42jtM+kAiMFsRpvAFDsYCA0irhpuF -3dvd6qJ2gHN99ZwExEWN57kci57q13XRcrHedUTnQn3iV2t93Jm8PYMo6oCTjcVM -ZcFwgbg4/EMxsvYDNEeyrPsiBsse3RdHHF9mudMaotoRsaS8I8nkvof/uZS2+F0g -StRf571oe2XyFR7SOqkt6dhrJKyXWERHrVkY8SFlcN7ONGCoQPHzPKTDKCOM/icz -Q0CgFzzr6juwcqajuUpLXhZI9LK8yIySxZ2frHI2vDSANGupi5LAuBft7HZT9SQB -jLMi6Et8Vcad+qMUu2WFbm5PEn4KPJ2V ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ -RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD -VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX -DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y -ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy -VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr -mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr -IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK -mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu -XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy -dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye -jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 -BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 -DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 -9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx -jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 -Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz -ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS -R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd -MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg -Q2xhc3MgMiBSb290IENBMB4XDTEwMTAyNjA4MzgwM1oXDTQwMTAyNjA4MzgwM1ow -TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw -HgYDVQQDDBdCdXlwYXNzIENsYXNzIDIgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB -BQADggIPADCCAgoCggIBANfHXvfBB9R3+0Mh9PT1aeTuMgHbo4Yf5FkNuud1g1Lr -6hxhFUi7HQfKjK6w3Jad6sNgkoaCKHOcVgb/S2TwDCo3SbXlzwx87vFKu3MwZfPV -L4O2fuPn9Z6rYPnT8Z2SdIrkHJasW4DptfQxh6NR/Md+oW+OU3fUl8FVM5I+GC91 -1K2GScuVr1QGbNgGE41b/+EmGVnAJLqBcXmQRFBoJJRfuLMR8SlBYaNByyM21cHx -MlAQTn/0hpPshNOOvEu/XAFOBz3cFIqUCqTqc/sLUegTBxj6DvEr0VQVfTzh97QZ -QmdiXnfgolXsttlpF9U6r0TtSsWe5HonfOV116rLJeffawrbD02TTqigzXsu8lkB -arcNuAeBfos4GzjmCleZPe4h6KP1DBbdi+w0jpwqHAAVF41og9JwnxgIzRFo1clr -Us3ERo/ctfPYV3Me6ZQ5BL/T3jjetFPsaRyifsSP5BtwrfKi+fv3FmRmaZ9JUaLi -FRhnBkp/1Wy1TbMz4GHrXb7pmA8y1x1LPC5aAVKRCfLf6o3YBkBjqhHk/sM3nhRS -P/TizPJhk9H9Z2vXUq6/aKtAQ6BXNVN48FP4YUIHZMbXb5tMOA1jrGKvNouicwoN -9SG9dKpN6nIDSdvHXx1iY8f93ZHsM+71bbRuMGjeyNYmsHVee7QHIJihdjK4TWxP -AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMmAd+BikoL1Rpzz -uvdMw964o605MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAU18h -9bqwOlI5LJKwbADJ784g7wbylp7ppHR/ehb8t/W2+xUbP6umwHJdELFx7rxP462s -A20ucS6vxOOto70MEae0/0qyexAQH6dXQbLArvQsWdZHEIjzIVEpMMpghq9Gqx3t -OluwlN5E40EIosHsHdb9T7bWR9AUC8rmyrV7d35BH16Dx7aMOZawP5aBQW9gkOLo -+fsicdl9sz1Gv7SEr5AcD48Saq/v7h56rgJKihcrdv6sVIkkLE8/trKnToyokZf7 -KcZ7XC25y2a2t6hbElGFtQl+Ynhw/qlqYLYdDnkM/crqJIByw5c/8nerQyIKx+u2 -DISCLIBrQYoIwOula9+ZEsuK1V6ADJHgJgg2SMX6OBE1/yWDLfJ6v9r9jv6ly0Us -H8SIU653DtmadsWOLB2jutXsMq7Aqqz30XpN69QH4kj3Io6wpJ9qzo6ysmD0oyLQ -I+uUWnpp3Q+/QFesa1lQ2aOZ4W7+jQF5JyMV3pKdewlNWudLSDBaGOYKbeaP4NK7 -5t98biGCwWg5TbSYWGZizEqQXsP6JwSxeRV0mcy+rSDeJmAc61ZRpqPq5KM/p/9h -3PFaTWwyI0PurKju7koSCTxdccK+efrCh2gdC/1cacwG0Jp9VJkqyTkaGa9LKkPz -Y11aWOIv4x3kqdbQCtCev9eBCfHJxyYNrJgWVqA= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFWTCCA0GgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJOTzEd -MBsGA1UECgwUQnV5cGFzcyBBUy05ODMxNjMzMjcxIDAeBgNVBAMMF0J1eXBhc3Mg -Q2xhc3MgMyBSb290IENBMB4XDTEwMTAyNjA4Mjg1OFoXDTQwMTAyNjA4Mjg1OFow -TjELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBhc3MgQVMtOTgzMTYzMzI3MSAw -HgYDVQQDDBdCdXlwYXNzIENsYXNzIDMgUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEB -BQADggIPADCCAgoCggIBAKXaCpUWUOOV8l6ddjEGMnqb8RB2uACatVI2zSRHsJ8Y -ZLya9vrVediQYkwiL944PdbgqOkcLNt4EemOaFEVcsfzM4fkoF0LXOBXByow9c3E -N3coTRiR5r/VUv1xLXA+58bEiuPwKAv0dpihi4dVsjoT/Lc+JzeOIuOoTyrvYLs9 -tznDDgFHmV0ST9tD+leh7fmdvhFHJlsTmKtdFoqwNxxXnUX/iJY2v7vKB3tvh2PX -0DJq1l1sDPGzbjniazEuOQAnFN44wOwZZoYS6J1yFhNkUsepNxz9gjDthBgd9K5c -/3ATAOux9TN6S9ZV+AWNS2mw9bMoNlwUxFFzTWsL8TQH2xc519woe2v1n/MuwU8X -KhDzzMro6/1rqy6any2CbgTUUgGTLT2G/H783+9CHaZr77kgxve9oKeV/afmiSTY -zIw0bOIjL9kSGiG5VZFvC5F5GQytQIgLcOJ60g7YaEi7ghM5EFjp2CoHxhLbWNvS -O1UQRwUVZ2J+GGOmRj8JDlQyXr8NYnon74Do29lLBlo3WiXQCBJ31G8JUJc9yB3D -34xFMFbG02SrZvPAXpacw8Tvw3xrizp5f7NJzz3iiZ+gMEuFuZyUJHmPfWupRWgP -K9Dx2hzLabjKSWJtyNBjYt1gD1iqj6G8BaVmos8bdrKEZLFMOVLAMLrwjEsCsLa3 -AgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEe4zf/lb+74suwv -Tg75JbCOPGvDMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAgEAACAj -QTUEkMJAYmDv4jVM1z+s4jSQuKFvdvoWFqRINyzpkMLyPPgKn9iB5btb2iUspKdV -cSQy9sgL8rxq+JOssgfCX5/bzMiKqr5qb+FJEMwx14C7u8jYog5kV+qi9cKpMRXS -IGrs/CIBKM+GuIAeqcwRpTzyFrNHnfzSgCHEy9BHcEGhyoMZCCxt8l13nIoUE9Q2 -HJLw5QY33KbmkJs4j1xrG0aGQ0JfPgEHU1RdZX33inOhmlRaHylDFCfChQ+1iHsa -O5S3HWCntZznKWlXWpuTekMwGwPXYshApqr8ZORK15FTAaggiG6cX0S5y2CBNOxv -033aSF/rtJC8LakcC6wc1aJoIIAE1vyxjy+7SjENSoYc6+I2KSb12tjE8nVhz36u -dmNKekBlk4f4HoCMhuWG1o8O/FMsYOgWYRqiPkN7zTlgVGr18okmAWiDSKIz6MkE -kbIRNBE+6tBDGR8Dk5AM/1E9V/RBbuHLoL7ryWPNbczk+DaqaJ3tvV2XcEQNtg41 -3OEMXbugUZTLfhbrES+jkkXITHHZvMmZUldGL1DPvTVp9D0VzgalLA8+9oG6lLvD -u79leNKGef9JOxqDDPDeeOzI8k1MGt6CKfjBWtrt7uYnXuhF0J0cUahoq0Tj0Itq -4/g7u9xN12TyUb7mqqta6THuBrxzvxNiCp/HuZc= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFaTCCA1GgAwIBAgIJAJK4iNuwisFjMA0GCSqGSIb3DQEBCwUAMFIxCzAJBgNV -BAYTAlNLMRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpEaXNpZyBhLnMu -MRkwFwYDVQQDExBDQSBEaXNpZyBSb290IFIyMB4XDTEyMDcxOTA5MTUzMFoXDTQy -MDcxOTA5MTUzMFowUjELMAkGA1UEBhMCU0sxEzARBgNVBAcTCkJyYXRpc2xhdmEx -EzARBgNVBAoTCkRpc2lnIGEucy4xGTAXBgNVBAMTEENBIERpc2lnIFJvb3QgUjIw -ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCio8QACdaFXS1tFPbCw3Oe -NcJxVX6B+6tGUODBfEl45qt5WDza/3wcn9iXAng+a0EE6UG9vgMsRfYvZNSrXaNH -PWSb6WiaxswbP7q+sos0Ai6YVRn8jG+qX9pMzk0DIaPY0jSTVpbLTAwAFjxfGs3I -x2ymrdMxp7zo5eFm1tL7A7RBZckQrg4FY8aAamkw/dLukO8NJ9+flXP04SXabBbe -QTg06ov80egEFGEtQX6sx3dOy1FU+16SGBsEWmjGycT6txOgmLcRK7fWV8x8nhfR -yyX+hk4kLlYMeE2eARKmK6cBZW58Yh2EhN/qwGu1pSqVg8NTEQxzHQuyRpDRQjrO -QG6Vrf/GlK1ul4SOfW+eioANSW1z4nuSHsPzwfPrLgVv2RvPN3YEyLRa5Beny912 -H9AZdugsBbPWnDTYltxhh5EF5EQIM8HauQhl1K6yNg3ruji6DOWbnuuNZt2Zz9aJ -QfYEkoopKW1rOhzndX0CcQ7zwOe9yxndnWCywmZgtrEE7snmhrmaZkCo5xHtgUUD -i/ZnWejBBhG93c+AAk9lQHhcR1DIm+YfgXvkRKhbhZri3lrVx/k6RGZL5DJUfORs -nLMOPReisjQS1n6yqEm70XooQL6iFh/f5DcfEXP7kAplQ6INfPgGAVUzfbANuPT1 -rqVCV3w2EYx7XsQDnYx5nQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1Ud -DwEB/wQEAwIBBjAdBgNVHQ4EFgQUtZn4r7CU9eMg1gqtzk5WpC5uQu0wDQYJKoZI -hvcNAQELBQADggIBACYGXnDnZTPIgm7ZnBc6G3pmsgH2eDtpXi/q/075KMOYKmFM -tCQSin1tERT3nLXK5ryeJ45MGcipvXrA1zYObYVybqjGom32+nNjf7xueQgcnYqf -GopTpti72TVVsRHFqQOzVju5hJMiXn7B9hJSi+osZ7z+Nkz1uM/Rs0mSO9MpDpkb -lvdhuDvEK7Z4bLQjb/D907JedR+Zlais9trhxTF7+9FGs9K8Z7RiVLoJ92Owk6Ka -+elSLotgEqv89WBW7xBci8QaQtyDW2QOy7W81k/BfDxujRNt+3vrMNDcTa/F1bal -TFtxyegxvug4BkihGuLq0t4SOVga/4AOgnXmt8kHbA7v/zjxmHHEt38OFdAlab0i -nSvtBfZGR6ztwPDUO+Ls7pZbkBNOHlY667DvlruWIxG68kOGdGSVyCh13x01utI3 -gzhTODY7z2zp+WsO0PsE6E9312UBeIYMej4hYvF/Y3EMyZ9E26gnonW+boE+18Dr -G5gPcFw0sorMwIUY6256s/daoQe/qUKS82Ail+QUoQebTnbAjn39pCXHR+3/H3Os -zMOl6W8KjptlwlCFtaOgUxLMVYdh84GuEEZhvUQhuMI9dM9+JDX6HAcOmz0iyu8x -L4ysEr3vQCj8KWefshNPZiTEUxnpHikV7+ZtsH8tZ/3zbBt1RqPlShfppNcL ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFjTCCA3WgAwIBAgIEGErM1jANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJD -TjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y -aXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJPT1QwHhcNMTIwODA4MDMwNzAxWhcNMjkx -MjMxMDMwNzAxWjBWMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5j -aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRUwEwYDVQQDDAxDRkNBIEVWIFJP -T1QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXXWvNED8fBVnVBU03 -sQ7smCuOFR36k0sXgiFxEFLXUWRwFsJVaU2OFW2fvwwbwuCjZ9YMrM8irq93VCpL -TIpTUnrD7i7es3ElweldPe6hL6P3KjzJIx1qqx2hp/Hz7KDVRM8Vz3IvHWOX6Jn5 -/ZOkVIBMUtRSqy5J35DNuF++P96hyk0g1CXohClTt7GIH//62pCfCqktQT+x8Rgp -7hZZLDRJGqgG16iI0gNyejLi6mhNbiyWZXvKWfry4t3uMCz7zEasxGPrb382KzRz -EpR/38wmnvFyXVBlWY9ps4deMm/DGIq1lY+wejfeWkU7xzbh72fROdOXW3NiGUgt -hxwG+3SYIElz8AXSG7Ggo7cbcNOIabla1jj0Ytwli3i/+Oh+uFzJlU9fpy25IGvP -a931DfSCt/SyZi4QKPaXWnuWFo8BGS1sbn85WAZkgwGDg8NNkt0yxoekN+kWzqot -aK8KgWU6cMGbrU1tVMoqLUuFG7OA5nBFDWteNfB/O7ic5ARwiRIlk9oKmSJgamNg -TnYGmE69g60dWIolhdLHZR4tjsbftsbhf4oEIRUpdPA+nJCdDC7xij5aqgwJHsfV -PKPtl8MeNPo4+QgO48BdK4PRVmrJtqhUUy54Mmc9gn900PvhtgVguXDbjgv5E1hv -cWAQUhC5wUEJ73IfZzF4/5YFjQIDAQABo2MwYTAfBgNVHSMEGDAWgBTj/i39KNAL -tbq2osS/BqoFjJP7LzAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAd -BgNVHQ4EFgQU4/4t/SjQC7W6tqLEvwaqBYyT+y8wDQYJKoZIhvcNAQELBQADggIB -ACXGumvrh8vegjmWPfBEp2uEcwPenStPuiB/vHiyz5ewG5zz13ku9Ui20vsXiObT -ej/tUxPQ4i9qecsAIyjmHjdXNYmEwnZPNDatZ8POQQaIxffu2Bq41gt/UP+TqhdL -jOztUmCypAbqTuv0axn96/Ua4CUqmtzHQTb3yHQFhDmVOdYLO6Qn+gjYXB74BGBS -ESgoA//vU2YApUo0FmZ8/Qmkrp5nGm9BC2sGE5uPhnEFtC+NiWYzKXZUmhH4J/qy -P5Hgzg0b8zAarb8iXRvTvyUFTeGSGn+ZnzxEk8rUQElsgIfXBDrDMlI1Dlb4pd19 -xIsNER9Tyx6yF7Zod1rg1MvIB671Oi6ON7fQAUtDKXeMOZePglr4UeWJoBjnaH9d -Ci77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN -5mydLIhyPDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe -/v5WOaHIz16eGWRGENoXkbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+Z -AAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3CekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ -5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEHTCCAwWgAwIBAgIQToEtioJl4AsC7j41AkblPTANBgkqhkiG9w0BAQUFADCB -gTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G -A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxJzAlBgNV -BAMTHkNPTU9ETyBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEyMDEwMDAw -MDBaFw0yOTEyMzEyMzU5NTlaMIGBMQswCQYDVQQGEwJHQjEbMBkGA1UECBMSR3Jl -YXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHEwdTYWxmb3JkMRowGAYDVQQKExFDT01P -RE8gQ0EgTGltaXRlZDEnMCUGA1UEAxMeQ09NT0RPIENlcnRpZmljYXRpb24gQXV0 -aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0ECLi3LjkRv3 -UcEbVASY06m/weaKXTuH+7uIzg3jLz8GlvCiKVCZrts7oVewdFFxze1CkU1B/qnI -2GqGd0S7WWaXUF601CxwRM/aN5VCaTwwxHGzUvAhTaHYujl8HJ6jJJ3ygxaYqhZ8 -Q5sVW7euNJH+1GImGEaaP+vB+fGQV+useg2L23IwambV4EajcNxo2f8ESIl33rXp -+2dtQem8Ob0y2WIC8bGoPW43nOIv4tOiJovGuFVDiOEjPqXSJDlqR6sA1KGzqSX+ -DT+nHbrTUcELpNqsOO9VUCQFZUaTNE8tja3G1CEZ0o7KBWFxB3NH5YoZEr0ETc5O -nKVIrLsm9wIDAQABo4GOMIGLMB0GA1UdDgQWBBQLWOWLxkwVN6RAqTCpIb5HNlpW -/zAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zBJBgNVHR8EQjBAMD6g -PKA6hjhodHRwOi8vY3JsLmNvbW9kb2NhLmNvbS9DT01PRE9DZXJ0aWZpY2F0aW9u -QXV0aG9yaXR5LmNybDANBgkqhkiG9w0BAQUFAAOCAQEAPpiem/Yb6dc5t3iuHXIY -SdOH5EOC6z/JqvWote9VfCFSZfnVDeFs9D6Mk3ORLgLETgdxb8CPOGEIqB6BCsAv -IC9Bi5HcSEW88cbeunZrM8gALTFGTO3nnc+IlP8zwFboJIYmuNg4ON8qa90SzMc/ -RxdMosIGlgnW2/4/PEZB31jiVg88O8EckzXZOFKs7sjsLjBOlDW0JB9LeGna8gI4 -zJVSk/BwJVmcIGfE7vmLV2H0knZ9P4SNVbfo5azV8fUZVqZa+5Acr5Pr5RzUZ5dd -BA6+C4OmF4O5MBKgxTMVBbkN+8cFduPYSo38NBejxiEovjBFMR7HeL5YYTisO+IB -ZQ== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICiTCCAg+gAwIBAgIQH0evqmIAcFBUTAGem2OZKjAKBggqhkjOPQQDAzCBhTEL -MAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE -BxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMT -IkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwMzA2MDAw -MDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdy -ZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09N -T0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBFQ0MgQ2VydGlmaWNhdGlv -biBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQDR3svdcmCFYX7deSR -FtSrYpn1PlILBs5BAH+X4QokPB0BBO490o0JlwzgdeT6+3eKKvUDYEs2ixYjFq0J -cfRK9ChQtP6IHG4/bC8vCVlbpVsLM5niwz2J+Wos77LTBumjQjBAMB0GA1UdDgQW -BBR1cacZSBm8nZ3qQUfflMRId5nTeTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ -BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjEA7wNbeqy3eApyt4jf/7VGFAkK+qDm -fQjGGoe9GKhzvSbKYAydzpmfz1wPMOG+FDHqAjAU9JM8SaczepBGR7NjfRObTrdv -GDeAU/7dIOA1mjbRxwG55tzd8/8dLDoWV9mSOdY= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB -hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G -A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV -BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5 -MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT -EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR -Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh -dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR -6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X -pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC -9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV -/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf -Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z -+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w -qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah -SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC -u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf -Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq -crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E -FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB -/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl -wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM -4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV -2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna -FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ -CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK -boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke -jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL -S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb -QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl -0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB -NVOFBkpdn627G190 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEvTCCA6WgAwIBAgIBADANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJFVTEn -MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL -ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEiMCAGA1UEAxMZQ2hhbWJlcnMg -b2YgQ29tbWVyY2UgUm9vdDAeFw0wMzA5MzAxNjEzNDNaFw0zNzA5MzAxNjEzNDRa -MH8xCzAJBgNVBAYTAkVVMScwJQYDVQQKEx5BQyBDYW1lcmZpcm1hIFNBIENJRiBB -ODI3NDMyODcxIzAhBgNVBAsTGmh0dHA6Ly93d3cuY2hhbWJlcnNpZ24ub3JnMSIw -IAYDVQQDExlDaGFtYmVycyBvZiBDb21tZXJjZSBSb290MIIBIDANBgkqhkiG9w0B -AQEFAAOCAQ0AMIIBCAKCAQEAtzZV5aVdGDDg2olUkfzIx1L4L1DZ77F1c2VHfRtb -unXF/KGIJPov7coISjlUxFF6tdpg6jg8gbLL8bvZkSM/SAFwdakFKq0fcfPJVD0d -BmpAPrMMhe5cG3nCYsS4No41XQEMIwRHNaqbYE6gZj3LJgqcQKH0XZi/caulAGgq -7YN6D6IUtdQis4CwPAxaUWktWBiP7Zme8a7ileb2R6jWDA+wWFjbw2Y3npuRVDM3 -0pQcakjJyfKl2qUMI/cjDpwyVV5xnIQFUZot/eZOKjRa3spAN2cMVCFVd9oKDMyX -roDclDZK9D7ONhMeU+SsTjoF7Nuucpw4i9A5O4kKPnf+dQIBA6OCAUQwggFAMBIG -A1UdEwEB/wQIMAYBAf8CAQwwPAYDVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybC5j -aGFtYmVyc2lnbi5vcmcvY2hhbWJlcnNyb290LmNybDAdBgNVHQ4EFgQU45T1sU3p -26EpW1eLTXYGduHRooowDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIA -BzAnBgNVHREEIDAegRxjaGFtYmVyc3Jvb3RAY2hhbWJlcnNpZ24ub3JnMCcGA1Ud -EgQgMB6BHGNoYW1iZXJzcm9vdEBjaGFtYmVyc2lnbi5vcmcwWAYDVR0gBFEwTzBN -BgsrBgEEAYGHLgoDATA+MDwGCCsGAQUFBwIBFjBodHRwOi8vY3BzLmNoYW1iZXJz -aWduLm9yZy9jcHMvY2hhbWJlcnNyb290Lmh0bWwwDQYJKoZIhvcNAQEFBQADggEB -AAxBl8IahsAifJ/7kPMa0QOx7xP5IV8EnNrJpY0nbJaHkb5BkAFyk+cefV/2icZd -p0AJPaxJRUXcLo0waLIJuvvDL8y6C98/d3tGfToSJI6WjzwFCm/SlCgdbQzALogi -1djPHRPH8EjX1wWnz8dHnjs8NMiAT9QUu/wNUPf6s+xCX6ndbcj0dc97wXImsQEc -XCz9ek60AcUFV7nnPKoF2YjpB0ZBzu9Bga5Y34OirsrXdx/nADydb47kMgkdTXg0 -eDQ8lJsm7U9xxhl6vSAiSFr+S30Dt+dYvsYyTnQeaN2oaFuzPu5ifdmA6Ap1erfu -tGWaIZDgqtCYvDi1czyL+Nw= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIExTCCA62gAwIBAgIBADANBgkqhkiG9w0BAQUFADB9MQswCQYDVQQGEwJFVTEn -MCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgyNzQzMjg3MSMwIQYDVQQL -ExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4GA1UEAxMXR2xvYmFsIENo -YW1iZXJzaWduIFJvb3QwHhcNMDMwOTMwMTYxNDE4WhcNMzcwOTMwMTYxNDE4WjB9 -MQswCQYDVQQGEwJFVTEnMCUGA1UEChMeQUMgQ2FtZXJmaXJtYSBTQSBDSUYgQTgy -NzQzMjg3MSMwIQYDVQQLExpodHRwOi8vd3d3LmNoYW1iZXJzaWduLm9yZzEgMB4G -A1UEAxMXR2xvYmFsIENoYW1iZXJzaWduIFJvb3QwggEgMA0GCSqGSIb3DQEBAQUA -A4IBDQAwggEIAoIBAQCicKLQn0KuWxfH2H3PFIP8T8mhtxOviteePgQKkotgVvq0 -Mi+ITaFgCPS3CU6gSS9J1tPfnZdan5QEcOw/Wdm3zGaLmFIoCQLfxS+EjXqXd7/s -QJ0lcqu1PzKY+7e3/HKE5TWH+VX6ox8Oby4o3Wmg2UIQxvi1RMLQQ3/bvOSiPGpV -eAp3qdjqGTK3L/5cPxvusZjsyq16aUXjlg9V9ubtdepl6DJWk0aJqCWKZQbua795 -B9Dxt6/tLE2Su8CoX6dnfQTyFQhwrJLWfQTSM/tMtgsL+xrJxI0DqX5c8lCrEqWh -z0hQpe/SyBoT+rB/sYIcd2oPX9wLlY/vQ37mRQklAgEDo4IBUDCCAUwwEgYDVR0T -AQH/BAgwBgEB/wIBDDA/BgNVHR8EODA2MDSgMqAwhi5odHRwOi8vY3JsLmNoYW1i -ZXJzaWduLm9yZy9jaGFtYmVyc2lnbnJvb3QuY3JsMB0GA1UdDgQWBBRDnDafsJ4w -TcbOX60Qq+UDpfqpFDAOBgNVHQ8BAf8EBAMCAQYwEQYJYIZIAYb4QgEBBAQDAgAH -MCoGA1UdEQQjMCGBH2NoYW1iZXJzaWducm9vdEBjaGFtYmVyc2lnbi5vcmcwKgYD -VR0SBCMwIYEfY2hhbWJlcnNpZ25yb290QGNoYW1iZXJzaWduLm9yZzBbBgNVHSAE -VDBSMFAGCysGAQQBgYcuCgEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly9jcHMuY2hh -bWJlcnNpZ24ub3JnL2Nwcy9jaGFtYmVyc2lnbnJvb3QuaHRtbDANBgkqhkiG9w0B -AQUFAAOCAQEAPDtwkfkEVCeR4e3t/mh/YV3lQWVPMvEYBZRqHN4fcNs+ezICNLUM -bKGKfKX0j//U2K0X1S0E0T9YgOKBWYi+wONGkyT+kL0mojAt6JcmVzWJdJYY9hXi -ryQZVgICsroPFOrGimbBhkVVi76SvpykBMdJPJ7oKXqJ1/6v/2j1pReQvayZzKWG -VwlnRtvWFsJG8eSpUPWP0ZIV018+xgBJOm5YstHRJw0lyDL4IBHNfTIzSJRUTN3c -ecQwn+uOuFW114hcxWokPbLTBQNRxgfvzBRydD1ucs4YKIxKoHflCStFREest2d/ -AYoFWpO+ocH/+OcOZ6RHSXZddZAa9SaP8A== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDqDCCApCgAwIBAgIJAP7c4wEPyUj/MA0GCSqGSIb3DQEBBQUAMDQxCzAJBgNV -BAYTAkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hMB4X -DTA3MDYyOTE1MTMwNVoXDTI3MDYyOTE1MTMwNVowNDELMAkGA1UEBhMCRlIxEjAQ -BgNVBAoMCURoaW15b3RpczERMA8GA1UEAwwIQ2VydGlnbmEwggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQDIaPHJ1tazNHUmgh7stL7qXOEm7RFHYeGifBZ4 -QCHkYJ5ayGPhxLGWkv8YbWkj4Sti993iNi+RB7lIzw7sebYs5zRLcAglozyHGxny -gQcPOJAZ0xH+hrTy0V4eHpbNgGzOOzGTtvKg0KmVEn2lmsxryIRWijOp5yIVUxbw -zBfsV1/pogqYCd7jX5xv3EjjhQsVWqa6n6xI4wmy9/Qy3l40vhx4XUJbzg4ij02Q -130yGLMLLGq/jj8UEYkgDncUtT2UCIf3JR7VsmAA7G8qKCVuKj4YYxclPz5EIBb2 -JsglrgVKtOdjLPOMFlN+XPsRGgjBRmKfIrjxwo1p3Po6WAbfAgMBAAGjgbwwgbkw -DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUGu3+QTmQtCRZvgHyUtVF9lo53BEw -ZAYDVR0jBF0wW4AUGu3+QTmQtCRZvgHyUtVF9lo53BGhOKQ2MDQxCzAJBgNVBAYT -AkZSMRIwEAYDVQQKDAlEaGlteW90aXMxETAPBgNVBAMMCENlcnRpZ25hggkA/tzj -AQ/JSP8wDgYDVR0PAQH/BAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIABzANBgkqhkiG -9w0BAQUFAAOCAQEAhQMeknH2Qq/ho2Ge6/PAD/Kl1NqV5ta+aDY9fm4fTIrv0Q8h -bV6lUmPOEvjvKtpv6zf+EwLHyzs+ImvaYS5/1HI93TDhHkxAGYwP15zRgzB7mFnc -fca5DClMoTOi62c6ZYTTluLtdkVwj7Ur3vkj1kluPBS1xp81HlDQwY9qcEQCYsuu -HWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY1gkIl2PlwS6w -t0QmwCbAr1UwnjvVNioZBPRcHv/PLLf/0P2HQBHVESO7SMAhqaQoLf0V+LBOK/Qw -WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGWzCCBEOgAwIBAgIRAMrpG4nxVQMNo+ZBbcTjpuEwDQYJKoZIhvcNAQELBQAw -WjELMAkGA1UEBhMCRlIxEjAQBgNVBAoMCURoaW15b3RpczEcMBoGA1UECwwTMDAw -MiA0ODE0NjMwODEwMDAzNjEZMBcGA1UEAwwQQ2VydGlnbmEgUm9vdCBDQTAeFw0x -MzEwMDEwODMyMjdaFw0zMzEwMDEwODMyMjdaMFoxCzAJBgNVBAYTAkZSMRIwEAYD -VQQKDAlEaGlteW90aXMxHDAaBgNVBAsMEzAwMDIgNDgxNDYzMDgxMDAwMzYxGTAX -BgNVBAMMEENlcnRpZ25hIFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw -ggIKAoICAQDNGDllGlmx6mQWDoyUJJV8g9PFOSbcDO8WV43X2KyjQn+Cyu3NW9sO -ty3tRQgXstmzy9YXUnIo245Onoq2C/mehJpNdt4iKVzSs9IGPjA5qXSjklYcoW9M -CiBtnyN6tMbaLOQdLNyzKNAT8kxOAkmhVECe5uUFoC2EyP+YbNDrihqECB63aCPu -I9Vwzm1RaRDuoXrC0SIxwoKF0vJVdlB8JXrJhFwLrN1CTivngqIkicuQstDuI7pm -TLtipPlTWmR7fJj6o0ieD5Wupxj0auwuA0Wv8HT4Ks16XdG+RCYyKfHx9WzMfgIh -C59vpD++nVPiz32pLHxYGpfhPTc3GGYo0kDFUYqMwy3OU4gkWGQwFsWq4NYKpkDf -ePb1BHxpE4S80dGnBs8B92jAqFe7OmGtBIyT46388NtEbVncSVmurJqZNjBBe3Yz -IoejwpKGbvlw7q6Hh5UbxHq9MfPU0uWZ/75I7HX1eBYdpnDBfzwboZL7z8g81sWT -Co/1VTp2lc5ZmIoJlXcymoO6LAQ6l73UL77XbJuiyn1tJslV1c/DeVIICZkHJC1k -JWumIWmbat10TWuXekG9qxf5kBdIjzb5LdXF2+6qhUVB+s06RbFo5jZMm5BX7CO5 -hwjCxAnxl4YqKE3idMDaxIzb3+KhF1nOJFl0Mdp//TBt2dzhauH8XwIDAQABo4IB -GjCCARYwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE -FBiHVuBud+4kNTxOc5of1uHieX4rMB8GA1UdIwQYMBaAFBiHVuBud+4kNTxOc5of -1uHieX4rMEQGA1UdIAQ9MDswOQYEVR0gADAxMC8GCCsGAQUFBwIBFiNodHRwczov -L3d3d3cuY2VydGlnbmEuZnIvYXV0b3JpdGVzLzBtBgNVHR8EZjBkMC+gLaArhilo -dHRwOi8vY3JsLmNlcnRpZ25hLmZyL2NlcnRpZ25hcm9vdGNhLmNybDAxoC+gLYYr -aHR0cDovL2NybC5kaGlteW90aXMuY29tL2NlcnRpZ25hcm9vdGNhLmNybDANBgkq -hkiG9w0BAQsFAAOCAgEAlLieT/DjlQgi581oQfccVdV8AOItOoldaDgvUSILSo3L -6btdPrtcPbEo/uRTVRPPoZAbAh1fZkYJMyjhDSSXcNMQH+pkV5a7XdrnxIxPTGRG -HVyH41neQtGbqH6mid2PHMkwgu07nM3A6RngatgCdTer9zQoKJHyBApPNeNgJgH6 -0BGM+RFq7q89w1DTj18zeTyGqHNFkIwgtnJzFyO+B2XleJINugHA64wcZr+shncB -lA2c5uk5jR+mUYyZDDl34bSb+hxnV29qao6pK0xXeXpXIs/NX2NGjVxZOob4Mkdi -o2cNGJHc+6Zr9UhhcyNZjgKnvETq9Emd8VRY+WCv2hikLyhF3HqgiIZd8zvn/yk1 -gPxkQ5Tm4xxvvq0OKmOZK8l+hfZx6AYDlf7ej0gcWtSS6Cvu5zHbugRqh5jnxV/v -faci9wHYTfmJ0A6aBVmknpjZbyvKcL5kwlWj9Omvw5Ip3IgWJJk8jSaYtlu3zM63 -Nwf9JtmYhST/WSMDmu2dnajkXjjO11INb9I/bbEFa0nOipFGc/T2L/Coc3cOZayh -jWZSaX5LaAzHHjcng6WMxwLkFM1JAbBzs/3GkDpv0mztO+7skb6iQ12LAEpmJURw -3kAP+HwV96LOPNdeE4yBFxgX0b3xdxA61GU5wSesVywlVP+i2k+KYTlerj1KjL0= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjET -MBEGA1UEChMKQ2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAb -BgNVBAMTFENlcnRpbm9taXMgLSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMz -MTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIxEzARBgNVBAoTCkNlcnRpbm9taXMx -FzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRDZXJ0aW5vbWlzIC0g -Um9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQosP5L2 -fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJfl -LieY6pOod5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQV -WZUKxkd8aRi5pwP5ynapz8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDF -TKWrteoB4owuZH9kb/2jJZOLyKIOSY008B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb -5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09xRLWtwHkziOC/7aOgFLSc -CbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE6OXWk6Ri -wsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJ -wx3tFvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SG -m/lg0h9tkQPTYKbVPZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4 -F2iw4lNVYC2vPsKD2NkJK/DAZNuHi5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZng -WVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIB -BjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I6tNxIqSSaHh0 -2TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF -AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/ -0KGRHCwPT5iVWVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWw -F6YSjNRieOpWauwK0kDDPAUwPk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZS -g081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAXlCOotQqSD7J6wWAsOMwaplv/8gzj -qh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJy29SWwNyhlCVCNSN -h4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9Iff/ -ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8V -btaw5BngDwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwj -Y/M50n92Uaf0yKHxDHYiI0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ -8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nMcyrDflOR1m749fPH0FFNjkulW+YZFzvW -gQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVrhkIGuUE= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDkjCCAnqgAwIBAgIRAIW9S/PY2uNp9pTXX8OlRCMwDQYJKoZIhvcNAQEFBQAw -PTELMAkGA1UEBhMCRlIxETAPBgNVBAoTCENlcnRwbHVzMRswGQYDVQQDExJDbGFz -cyAyIFByaW1hcnkgQ0EwHhcNOTkwNzA3MTcwNTAwWhcNMTkwNzA2MjM1OTU5WjA9 -MQswCQYDVQQGEwJGUjERMA8GA1UEChMIQ2VydHBsdXMxGzAZBgNVBAMTEkNsYXNz -IDIgUHJpbWFyeSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANxQ -ltAS+DXSCHh6tlJw/W/uz7kRy1134ezpfgSN1sxvc0NXYKwzCkTsA18cgCSR5aiR -VhKC9+Ar9NuuYS6JEI1rbLqzAr3VNsVINyPi8Fo3UjMXEuLRYE2+L0ER4/YXJQyL -kcAbmXuZVg2v7tK8R1fjeUl7NIknJITesezpWE7+Tt9avkGtrAjFGA7v0lPubNCd -EgETjdyAYveVqUSISnFOYFWe2yMZeVYHDD9jC1yw4r5+FfyUM1hBOHTE4Y+L3yas -H7WLO7dDWWuwJKZtkIvEcupdM5i3y95ee++U8Rs+yskhwcWYAqqi9lt3m/V+llU0 -HGdpwPFC40es/CgcZlUCAwEAAaOBjDCBiTAPBgNVHRMECDAGAQH/AgEKMAsGA1Ud -DwQEAwIBBjAdBgNVHQ4EFgQU43Mt38sOKAze3bOkynm4jrvoMIkwEQYJYIZIAYb4 -QgEBBAQDAgEGMDcGA1UdHwQwMC4wLKAqoCiGJmh0dHA6Ly93d3cuY2VydHBsdXMu -Y29tL0NSTC9jbGFzczIuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQCnVM+IRBnL39R/ -AN9WM2K191EBkOvDP9GIROkkXe/nFL0gt5o8AP5tn9uQ3Nf0YtaLcF3n5QRIqWh8 -yfFC82x/xXp8HVGIutIKPidd3i1RTtMTZGnkLuPT55sJmabglZvOGtd/vjzOUrMR -FcEPF80Du5wlFbqidon8BvEY0JNLDnyCt6X09l/+7UCmnYR0ObncHoUW2ikbhiMA -ybuJfm6AiB4vFLQDJKgybwOaRywwvlbGp0ICcBvqQNi6BQNwB6SW//1IMwrh3KWB -kJtN3X3n57LNXMhqlfil9o3EXXgIvnsG1knPGTZQIy4I5p4FTUcY1Rbpsda2ENW7 -l7+ijrRU ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDDDCCAfSgAwIBAgIDAQAgMA0GCSqGSIb3DQEBBQUAMD4xCzAJBgNVBAYTAlBM -MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD -QTAeFw0wMjA2MTExMDQ2MzlaFw0yNzA2MTExMDQ2MzlaMD4xCzAJBgNVBAYTAlBM -MRswGQYDVQQKExJVbml6ZXRvIFNwLiB6IG8uby4xEjAQBgNVBAMTCUNlcnR1bSBD -QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM6xwS7TT3zNJc4YPk/E -jG+AanPIW1H4m9LcuwBcsaD8dQPugfCI7iNS6eYVM42sLQnFdvkrOYCJ5JdLkKWo -ePhzQ3ukYbDYWMzhbGZ+nPMJXlVjhNWo7/OxLjBos8Q82KxujZlakE403Daaj4GI -ULdtlkIJ89eVgw1BS7Bqa/j8D35in2fE7SZfECYPCE/wpFcozo+47UX2bu4lXapu -Ob7kky/ZR6By6/qmW6/KUz/iDsaWVhFu9+lmqSbYf5VT7QqFiLpPKaVCjF62/IUg -AKpoC6EahQGcxEZjgoi2IrHu/qpGWX7PNSzVttpd90gzFFS269lvzs2I1qsb2pY7 -HVkCAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEA -uI3O7+cUus/usESSbLQ5PqKEbq24IXfS1HeCh+YgQYHu4vgRt2PRFze+GXYkHAQa -TOs9qmdvLdTN/mUxcMUbpgIKumB7bVjCmkn+YzILa+M6wKyrO7Do0wlRjBCDxjTg -xSvgGrZgFCdsMneMvLJymM/NzD+5yCRCFNZX/OYmQ6kd5YCQzgNUKD73P9P4Te1q -CjqTE5s7FCMTY5w/0YcneeVMUeMBrYVdGjux1XMQpNPyvG5k9VpWkKjHDkx0Dy5x -O/fIR/RpbxXyEV6DHpx8Uq79AtoSqFlnGNu8cN2bsWntgM6JQEhqDjXKKWYVIZQs -6GAqm4VKQPNriiTsBhYscw== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDuzCCAqOgAwIBAgIDBETAMA0GCSqGSIb3DQEBBQUAMH4xCzAJBgNVBAYTAlBM -MSIwIAYDVQQKExlVbml6ZXRvIFRlY2hub2xvZ2llcyBTLkEuMScwJQYDVQQLEx5D -ZXJ0dW0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxIjAgBgNVBAMTGUNlcnR1bSBU -cnVzdGVkIE5ldHdvcmsgQ0EwHhcNMDgxMDIyMTIwNzM3WhcNMjkxMjMxMTIwNzM3 -WjB+MQswCQYDVQQGEwJQTDEiMCAGA1UEChMZVW5pemV0byBUZWNobm9sb2dpZXMg -Uy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MSIw -IAYDVQQDExlDZXJ0dW0gVHJ1c3RlZCBOZXR3b3JrIENBMIIBIjANBgkqhkiG9w0B -AQEFAAOCAQ8AMIIBCgKCAQEA4/t9o3K6wvDJFIf1awFO4W5AB7ptJ11/91sts1rH -UV+rpDKmYYe2bg+G0jACl/jXaVehGDldamR5xgFZrDwxSjh80gTSSyjoIF87B6LM -TXPb865Px1bVWqeWifrzq2jUI4ZZJ88JJ7ysbnKDHDBy3+Ci6dLhdHUZvSqeexVU -BBvXQzmtVSjF4hq79MDkrjhJM8x2hZ85RdKknvISjFH4fOQtf/WsX+sWn7Et0brM -kUJ3TCXJkDhv2/DM+44el1k+1WBO5gUo7Ul5E0u6SNsv+XLTOcr+H9g0cvW0QM8x -AcPs3hEtF10fuFDRXhmnad4HMyjKUJX5p1TLVIZQRan5SQIDAQABo0IwQDAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBQIds3LB/8k9sXN7buQvOKEN0Z19zAOBgNV -HQ8BAf8EBAMCAQYwDQYJKoZIhvcNAQEFBQADggEBAKaorSLOAT2mo/9i0Eidi15y -sHhE49wcrwn9I0j6vSrEuVUEtRCjjSfeC4Jj0O7eDDd5QVsisrCaQVymcODU0HfL -I9MA4GxWL+FpDQ3Zqr8hgVDZBqWo/5U30Kr+4rP1mS1FhIrlQgnXdAIv94nYmem8 -J9RHjboNRhx3zxSkHLmkMcScKHQDNP8zGSal6Q10tz6XxnboJ5ajZt3hrvJBW8qY -VoNzcOSGGtIxQbovvi0TWnZvTuhOgQ4/WwMioBK+ZlgRSssDxLQqKi2WF+A5VLxI -03YnnZotBqbJ7DnSq9ufmgsnAjUpsUCV5/nonFWIGUbWtzT1fs45mtk48VH3Tyw= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIF0jCCA7qgAwIBAgIQIdbQSk8lD8kyN/yqXhKN6TANBgkqhkiG9w0BAQ0FADCB -gDELMAkGA1UEBhMCUEwxIjAgBgNVBAoTGVVuaXpldG8gVGVjaG5vbG9naWVzIFMu -QS4xJzAlBgNVBAsTHkNlcnR1bSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTEkMCIG -A1UEAxMbQ2VydHVtIFRydXN0ZWQgTmV0d29yayBDQSAyMCIYDzIwMTExMDA2MDgz -OTU2WhgPMjA0NjEwMDYwODM5NTZaMIGAMQswCQYDVQQGEwJQTDEiMCAGA1UEChMZ -VW5pemV0byBUZWNobm9sb2dpZXMgUy5BLjEnMCUGA1UECxMeQ2VydHVtIENlcnRp -ZmljYXRpb24gQXV0aG9yaXR5MSQwIgYDVQQDExtDZXJ0dW0gVHJ1c3RlZCBOZXR3 -b3JrIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC9+Xj45tWA -DGSdhhuWZGc/IjoedQF97/tcZ4zJzFxrqZHmuULlIEub2pt7uZld2ZuAS9eEQCsn -0+i6MLs+CRqnSZXvK0AkwpfHp+6bJe+oCgCXhVqqndwpyeI1B+twTUrWwbNWuKFB -OJvR+zF/j+Bf4bE/D44WSWDXBo0Y+aomEKsq09DRZ40bRr5HMNUuctHFY9rnY3lE -fktjJImGLjQ/KUxSiyqnwOKRKIm5wFv5HdnnJ63/mgKXwcZQkpsCLL2puTRZCr+E -Sv/f/rOf69me4Jgj7KZrdxYq28ytOxykh9xGc14ZYmhFV+SQgkK7QtbwYeDBoz1m -o130GO6IyY0XRSmZMnUCMe4pJshrAua1YkV/NxVaI2iJ1D7eTiew8EAMvE0Xy02i -sx7QBlrd9pPPV3WZ9fqGGmd4s7+W/jTcvedSVuWz5XV710GRBdxdaeOVDUO5/IOW -OZV7bIBaTxNyxtd9KXpEulKkKtVBRgkg/iKgtlswjbyJDNXXcPiHUv3a76xRLgez -Tv7QCdpw75j6VuZt27VXS9zlLCUVyJ4ueE742pyehizKV/Ma5ciSixqClnrDvFAS -adgOWkaLOusm+iPJtrCBvkIApPjW/jAux9JG9uWOdf3yzLnQh1vMBhBgu4M1t15n -3kfsmUjxpKEV/q2MYo45VU85FrmxY53/twIDAQABo0IwQDAPBgNVHRMBAf8EBTAD -AQH/MB0GA1UdDgQWBBS2oVQ5AsOgP46KvPrU+Bym0ToO/TAOBgNVHQ8BAf8EBAMC -AQYwDQYJKoZIhvcNAQENBQADggIBAHGlDs7k6b8/ONWJWsQCYftMxRQXLYtPU2sQ -F/xlhMcQSZDe28cmk4gmb3DWAl45oPePq5a1pRNcgRRtDoGCERuKTsZPpd1iHkTf -CVn0W3cLN+mLIMb4Ck4uWBzrM9DPhmDJ2vuAL55MYIR4PSFk1vtBHxgP58l1cb29 -XN40hz5BsA72udY/CROWFC/emh1auVbONTqwX3BNXuMp8SMoclm2q8KMZiYcdywm -djWLKKdpoPk79SPdhRB0yZADVpHnr7pH1BKXESLjokmUbOe3lEu6LaTaM4tMpkT/ -WjzGHWTYtTHkpjx6qFcL2+1hGsvxznN3Y6SHb0xRONbkX8eftoEq5IVIeVheO/jb -AoJnwTnbw3RLPTYe+SmTiGhbqEQZIfCn6IENLOiTNrQ3ssqwGyZ6miUfmpqAnksq -P/ujmv5zMnHCnsZy4YpoJ/HkD7TETKVhk/iXEAcqMCWpuchxuO9ozC1+9eB+D4Ko -b7a6bINDd82Kkhehnlt4Fj1F4jNy3eFmypnTycUm/Q1oBEauttmbjL4ZvrHG8hnj -XALKLNhvSgfZyTXaQHXyxKcZb55CEJh15pWLYLztxRLXis7VmFxWlgPF7ncGNf/P -5O4/E2Hu29othfDNrp2yGAlFw5Khchf8R7agCyzxxN5DaAhqXzvwdmP7zAYspsbi -DrW5viSP ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIHTzCCBTegAwIBAgIJAKPaQn6ksa7aMA0GCSqGSIb3DQEBBQUAMIGuMQswCQYD -VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 -IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 -MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xKTAnBgNVBAMTIENoYW1iZXJz -IG9mIENvbW1lcmNlIFJvb3QgLSAyMDA4MB4XDTA4MDgwMTEyMjk1MFoXDTM4MDcz -MTEyMjk1MFowga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpNYWRyaWQgKHNlZSBj -dXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29tL2FkZHJlc3MpMRIw -EAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVyZmlybWEgUy5BLjEp -MCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAtIDIwMDgwggIiMA0G -CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCvAMtwNyuAWko6bHiUfaN/Gh/2NdW9 -28sNRHI+JrKQUrpjOyhYb6WzbZSm891kDFX29ufyIiKAXuFixrYp4YFs8r/lfTJq -VKAyGVn+H4vXPWCGhSRv4xGzdz4gljUha7MI2XAuZPeEklPWDrCQiorjh40G072Q -DuKZoRuGDtqaCrsLYVAGUvGef3bsyw/QHg3PmTA9HMRFEFis1tPo1+XqxQEHd9ZR -5gN/ikilTWh1uem8nk4ZcfUyS5xtYBkL+8ydddy/Js2Pk3g5eXNeJQ7KXOt3EgfL -ZEFHcpOrUMPrCXZkNNI5t3YRCQ12RcSprj1qr7V9ZS+UWBDsXHyvfuK2GNnQm05a -Sd+pZgvMPMZ4fKecHePOjlO+Bd5gD2vlGts/4+EhySnB8esHnFIbAURRPHsl18Tl -UlRdJQfKFiC4reRB7noI/plvg6aRArBsNlVq5331lubKgdaX8ZSD6e2wsWsSaR6s -+12pxZjptFtYer49okQ6Y1nUCyXeG0+95QGezdIp1Z8XGQpvvwyQ0wlf2eOKNcx5 -Wk0ZN5K3xMGtr/R5JJqyAQuxr1yW84Ay+1w9mPGgP0revq+ULtlVmhduYJ1jbLhj -ya6BXBg14JC7vjxPNyK5fuvPnnchpj04gftI2jE9K+OJ9dC1vX7gUMQSibMjmhAx -hduub+84Mxh2EQIDAQABo4IBbDCCAWgwEgYDVR0TAQH/BAgwBgEB/wIBDDAdBgNV -HQ4EFgQU+SSsD7K1+HnA+mCIG8TZTQKeFxkwgeMGA1UdIwSB2zCB2IAU+SSsD7K1 -+HnA+mCIG8TZTQKeFxmhgbSkgbEwga4xCzAJBgNVBAYTAkVVMUMwQQYDVQQHEzpN -YWRyaWQgKHNlZSBjdXJyZW50IGFkZHJlc3MgYXQgd3d3LmNhbWVyZmlybWEuY29t -L2FkZHJlc3MpMRIwEAYDVQQFEwlBODI3NDMyODcxGzAZBgNVBAoTEkFDIENhbWVy -ZmlybWEgUy5BLjEpMCcGA1UEAxMgQ2hhbWJlcnMgb2YgQ29tbWVyY2UgUm9vdCAt -IDIwMDiCCQCj2kJ+pLGu2jAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRV -HSAAMCowKAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20w -DQYJKoZIhvcNAQEFBQADggIBAJASryI1wqM58C7e6bXpeHxIvj99RZJe6dqxGfwW -PJ+0W2aeaufDuV2I6A+tzyMP3iU6XsxPpcG1Lawk0lgH3qLPaYRgM+gQDROpI9CF -5Y57pp49chNyM/WqfcZjHwj0/gF/JM8rLFQJ3uIrbZLGOU8W6jx+ekbURWpGqOt1 -glanq6B8aBMz9p0w8G8nOSQjKpD9kCk18pPfNKXG9/jvjA9iSnyu0/VU+I22mlaH -FoI6M6taIgj3grrqLuBHmrS1RaMFO9ncLkVAO+rcf+g769HsJtg1pDDFOqxXnrN2 -pSB7+R5KBWIBpih1YJeSDW4+TTdDDZIVnBgizVGZoCkaPF+KMjNbMMeJL0eYD6MD -xvbxrN8y8NmBGuScvfaAFPDRLLmF9dijscilIeUcE5fuDr3fKanvNFNb0+RqE4QG -tjICxFKuItLcsiFCGtpA8CnJ7AoMXOLQusxI0zcKzBIKinmwPQN/aUv0NCB9szTq -jktk9T79syNnFQ0EuPAtwQlRPLJsFfClI9eDdOTlLsn+mCdCxqvGnrDQWzilm1De -fhiYtUU79nm06PcaewaD+9CL2rvHvRirCG88gGtAPxkZumWK5r7VXNM21+9AUiRg -OGcEMeyP84LG3rlV8zsxkVrctQgVrXYlCg17LofiDKYGvCYQbTed7N14jHyAxfDZ -d0jQ ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb -MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow -GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj -YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL -MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE -BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM -GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua -BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe -3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4 -YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR -rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm -ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU -oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF -MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v -QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t -b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF -AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q -GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz -Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2 -G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi -l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3 -smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDoTCCAomgAwIBAgILBAAAAAABD4WqLUgwDQYJKoZIhvcNAQEFBQAwOzEYMBYG -A1UEChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2Jh -bCBSb290MB4XDTA2MTIxNTA4MDAwMFoXDTIxMTIxNTA4MDAwMFowOzEYMBYGA1UE -ChMPQ3liZXJ0cnVzdCwgSW5jMR8wHQYDVQQDExZDeWJlcnRydXN0IEdsb2JhbCBS -b290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA+Mi8vRRQZhP/8NN5 -7CPytxrHjoXxEnOmGaoQ25yiZXRadz5RfVb23CO21O1fWLE3TdVJDm71aofW0ozS -J8bi/zafmGWgE07GKmSb1ZASzxQG9Dvj1Ci+6A74q05IlG2OlTEQXO2iLb3VOm2y -HLtgwEZLAfVJrn5GitB0jaEMAs7u/OePuGtm839EAL9mJRQr3RAwHQeWP032a7iP -t3sMpTjr3kfb1V05/Iin89cqdPHoWqI7n1C6poxFNcJQZZXcY4Lv3b93TZxiyWNz -FtApD0mpSPCzqrdsxacwOUBdrsTiXSZT8M4cIwhhqJQZugRiQOwfOHB3EgZxpzAY -XSUnpQIDAQABo4GlMIGiMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/ -MB0GA1UdDgQWBBS2CHsNesysIEyGVjJez6tuhS1wVzA/BgNVHR8EODA2MDSgMqAw -hi5odHRwOi8vd3d3Mi5wdWJsaWMtdHJ1c3QuY29tL2NybC9jdC9jdHJvb3QuY3Js -MB8GA1UdIwQYMBaAFLYIew16zKwgTIZWMl7Pq26FLXBXMA0GCSqGSIb3DQEBBQUA -A4IBAQBW7wojoFROlZfJ+InaRcHUowAl9B8Tq7ejhVhpwjCt2BWKLePJzYFa+HMj -Wqd8BfP9IjsO0QbE2zZMcwSO5bAi5MXzLqXZI+O4Tkogp24CJJ8iYGd7ix1yCcUx -XOl5n4BHPa2hCwcUPUf/A2kaDAtE52Mlp3+yybh2hO0j9n0Hq0V+09+zv+mKts2o -omcrUtW3ZfA5TGOgkXmTUg9U3YO7n9GPp1Nzw8v/MOx8BLjYRB+TX3EJIrduPuoc -A06dGiBh+4E37F78CkWr1+cXVdCg6mCbpvbjjFspwgZgFJ0tl0ypkxWdYcQBX0jW -WL1WMRJOEcgh4LMRkWXbtKaIOM5V ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEDjCCAvagAwIBAgIDD92sMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNVBAYTAkRF -MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxHzAdBgNVBAMMFkQtVFJVU1QgUm9vdCBD -QSAzIDIwMTMwHhcNMTMwOTIwMDgyNTUxWhcNMjgwOTIwMDgyNTUxWjBFMQswCQYD -VQQGEwJERTEVMBMGA1UECgwMRC1UcnVzdCBHbWJIMR8wHQYDVQQDDBZELVRSVVNU -IFJvb3QgQ0EgMyAyMDEzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA -xHtCkoIf7O1UmI4SwMoJ35NuOpNcG+QQd55OaYhs9uFp8vabomGxvQcgdJhl8Ywm -CM2oNcqANtFjbehEeoLDbF7eu+g20sRoNoyfMr2EIuDcwu4QRjltr5M5rofmw7wJ -ySxrZ1vZm3Z1TAvgu8XXvD558l++0ZBX+a72Zl8xv9Ntj6e6SvMjZbu376Ml1wrq -WLbviPr6ebJSWNXwrIyhUXQplapRO5AyA58ccnSQ3j3tYdLl4/1kR+W5t0qp9x+u -loYErC/jpIF3t1oW/9gPP/a3eMykr/pbPBJbqFKJcu+I89VEgYaVI5973bzZNO98 -lDyqwEHC451QGsDkGSL8swIDAQABo4IBBTCCAQEwDwYDVR0TAQH/BAUwAwEB/zAd -BgNVHQ4EFgQUP5DIfccVb/Mkj6nDL0uiDyGyL+cwDgYDVR0PAQH/BAQDAgEGMIG+ -BgNVHR8EgbYwgbMwdKByoHCGbmxkYXA6Ly9kaXJlY3RvcnkuZC10cnVzdC5uZXQv -Q049RC1UUlVTVCUyMFJvb3QlMjBDQSUyMDMlMjAyMDEzLE89RC1UcnVzdCUyMEdt -YkgsQz1ERT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MDugOaA3hjVodHRwOi8v -Y3JsLmQtdHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2FfM18yMDEzLmNybDAN -BgkqhkiG9w0BAQsFAAOCAQEADlkOWOR0SCNEzzQhtZwUGq2aS7eziG1cqRdw8Cqf -jXv5e4X6xznoEAiwNStfzwLS05zICx7uBVSuN5MECX1sj8J0vPgclL4xAUAt8yQg -t4RVLFzI9XRKEBmLo8ftNdYJSNMOwLo5qLBGArDbxohZwr78e7Erz35ih1WWzAFv -m2chlTWL+BD8cRu3SzdppjvW7IvuwbDzJcmPkn2h6sPKRL8mpXSSnON065102ctN -h9j8tGlsi6BDB2B4l+nZk3zCRrybN1Kj7Yo8E6l7U0tJmhEFLAtuVqwfLoJs4Gln -tQ5tLdnkwBXxP/oYcuEVbSdbLTAoK59ImmQrme/ydUlfXA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEMzCCAxugAwIBAgIDCYPzMA0GCSqGSIb3DQEBCwUAME0xCzAJBgNVBAYTAkRF -MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMMHkQtVFJVU1QgUm9vdCBD -bGFzcyAzIENBIDIgMjAwOTAeFw0wOTExMDUwODM1NThaFw0yOTExMDUwODM1NTha -ME0xCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxJzAlBgNVBAMM -HkQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgMjAwOTCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBANOySs96R+91myP6Oi/WUEWJNTrGa9v+2wBoqOADER03 -UAifTUpolDWzU9GUY6cgVq/eUXjsKj3zSEhQPgrfRlWLJ23DEE0NkVJD2IfgXU42 -tSHKXzlABF9bfsyjxiupQB7ZNoTWSPOSHjRGICTBpFGOShrvUD9pXRl/RcPHAY9R -ySPocq60vFYJfxLLHLGvKZAKyVXMD9O0Gu1HNVpK7ZxzBCHQqr0ME7UAyiZsxGsM -lFqVlNpQmvH/pStmMaTJOKDfHR+4CS7zp+hnUquVH+BGPtikw8paxTGA6Eian5Rp -/hnd2HN8gcqW3o7tszIFZYQ05ub9VxC1X3a/L7AQDcUCAwEAAaOCARowggEWMA8G -A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFP3aFMSfMN4hvR5COfyrYyNJ4PGEMA4G -A1UdDwEB/wQEAwIBBjCB0wYDVR0fBIHLMIHIMIGAoH6gfIZ6bGRhcDovL2RpcmVj -dG9yeS5kLXRydXN0Lm5ldC9DTj1ELVRSVVNUJTIwUm9vdCUyMENsYXNzJTIwMyUy -MENBJTIwMiUyMDIwMDksTz1ELVRydXN0JTIwR21iSCxDPURFP2NlcnRpZmljYXRl -cmV2b2NhdGlvbmxpc3QwQ6BBoD+GPWh0dHA6Ly93d3cuZC10cnVzdC5uZXQvY3Js -L2QtdHJ1c3Rfcm9vdF9jbGFzc18zX2NhXzJfMjAwOS5jcmwwDQYJKoZIhvcNAQEL -BQADggEBAH+X2zDI36ScfSF6gHDOFBJpiBSVYEQBrLLpME+bUMJm2H6NMLVwMeni -acfzcNsgFYbQDfC+rAF1hM5+n02/t2A7nPPKHeJeaNijnZflQGDSNiH+0LS4F9p0 -o3/U37CYAqxva2ssJSRyoWXuJVrl5jLn8t+rSfrzkGkj2wTZ51xY/GXUl77M/C4K -zCUqNQT4YJEVdT1B/yMfGchs64JTBKbkTCJNjYy6zltz7GRUUG3RnFX7acM2w4y8 -PIWmawomDeCTmGCufsYkl4phX5GOZpIJhzbNi5stPvZR1FDUWSi9g/LMKHtThm3Y -Johw1+qRzT65ysCQblrGXnRl11z+o+I= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEQzCCAyugAwIBAgIDCYP0MA0GCSqGSIb3DQEBCwUAMFAxCzAJBgNVBAYTAkRF -MRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNVBAMMIUQtVFJVU1QgUm9vdCBD -bGFzcyAzIENBIDIgRVYgMjAwOTAeFw0wOTExMDUwODUwNDZaFw0yOTExMDUwODUw -NDZaMFAxCzAJBgNVBAYTAkRFMRUwEwYDVQQKDAxELVRydXN0IEdtYkgxKjAoBgNV -BAMMIUQtVFJVU1QgUm9vdCBDbGFzcyAzIENBIDIgRVYgMjAwOTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAJnxhDRwui+3MKCOvXwEz75ivJn9gpfSegpn -ljgJ9hBOlSJzmY3aFS3nBfwZcyK3jpgAvDw9rKFs+9Z5JUut8Mxk2og+KbgPCdM0 -3TP1YtHhzRnp7hhPTFiu4h7WDFsVWtg6uMQYZB7jM7K1iXdODL/ZlGsTl28So/6Z -qQTMFexgaDbtCHu39b+T7WYxg4zGcTSHThfqr4uRjRxWQa4iN1438h3Z0S0NL2lR -p75mpoo6Kr3HGrHhFPC+Oh25z1uxav60sUYgovseO3Dvk5h9jHOW8sXvhXCtKSb8 -HgQ+HKDYD8tSg2J87otTlZCpV6LqYQXY+U3EJ/pure3511H3a6UCAwEAAaOCASQw -ggEgMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFNOUikxiEyoZLsyvcop9Ntea -HNxnMA4GA1UdDwEB/wQEAwIBBjCB3QYDVR0fBIHVMIHSMIGHoIGEoIGBhn9sZGFw -Oi8vZGlyZWN0b3J5LmQtdHJ1c3QubmV0L0NOPUQtVFJVU1QlMjBSb290JTIwQ2xh -c3MlMjAzJTIwQ0ElMjAyJTIwRVYlMjAyMDA5LE89RC1UcnVzdCUyMEdtYkgsQz1E -RT9jZXJ0aWZpY2F0ZXJldm9jYXRpb25saXN0MEagRKBChkBodHRwOi8vd3d3LmQt -dHJ1c3QubmV0L2NybC9kLXRydXN0X3Jvb3RfY2xhc3NfM19jYV8yX2V2XzIwMDku -Y3JsMA0GCSqGSIb3DQEBCwUAA4IBAQA07XtaPKSUiO8aEXUHL7P+PPoeUSbrh/Yp -3uDx1MYkCenBz1UbtDDZzhr+BlGmFaQt77JLvyAoJUnRpjZ3NOhk31KxEcdzes05 -nsKtjHEh8lprr988TlWvsoRlFIm5d8sqMb7Po23Pb0iUMkZv53GMoKaEGTcH8gNF -CSuGdXzfX2lXANtu2KZyIktQ1HWYVt+3GP9DQ1CuekR78HlR10M9p9OB0/DJT7na -xpeG0ILD5EJt/rDiZE4OJudANCa1CInXCGNjOCd1HjPqbqjdn5lPdE2BiYBL3ZqX -KVwvvoFBuYz/6n1gBp7N1z3TLqMVvKjmJuVvw9y4AyHqnxbxLFS1 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDSjCCAjKgAwIBAgIQRK+wgNajJ7qJMDmGLvhAazANBgkqhkiG9w0BAQUFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTAwMDkzMDIxMTIxOVoXDTIxMDkzMDE0MDExNVow -PzEkMCIGA1UEChMbRGlnaXRhbCBTaWduYXR1cmUgVHJ1c3QgQ28uMRcwFQYDVQQD -Ew5EU1QgUm9vdCBDQSBYMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB -AN+v6ZdQCINXtMxiZfaQguzH0yxrMMpb7NnDfcdAwRgUi+DoM3ZJKuM/IUmTrE4O -rz5Iy2Xu/NMhD2XSKtkyj4zl93ewEnu1lcCJo6m67XMuegwGMoOifooUMM0RoOEq -OLl5CjH9UL2AZd+3UWODyOKIYepLYYHsUmu5ouJLGiifSKOeDNoJjj4XLh7dIN9b -xiqKqy69cK3FCxolkHRyxXtqqzTWMIn/5WgTe1QLyNau7Fqckh49ZLOMxt+/yUFw -7BZy1SbsOFU5Q9D8/RhcQPGX69Wam40dutolucbY38EVAjqr2m7xPi71XAicPNaD -aeQQmxkqtilX4+U9m5/wAl0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNV -HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFMSnsaR7LHH62+FLkHX/xBVghYkQMA0GCSqG -SIb3DQEBBQUAA4IBAQCjGiybFwBcqR7uKGY3Or+Dxz9LwwmglSBd49lZRNI+DT69 -ikugdB/OEIKcdBodfpga3csTS7MgROSR6cz8faXbauX+5v3gTt23ADq1cEmv8uXr -AvHRAosZy5Q6XkjEGB5YGV8eAlrwDPGxrancWYaLbumR9YbK+rlmM6pZW87ipxZz -R8srzJmwN0jP41ZL9c8PDHIyh8bwRLtTcm1D9SZImlJnt1ir/md2cXjbDaJWFBM5 -JDGFoqgCWjBH4d1QB7wCCZAA62RjYJsWvIjJEubSfZGL+T0yjWW06XyxV3bqxbYo -Ob8VZRzI9neWagqNdwvYkQsEjgfbKbYK7p2CNTUQ ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDnzCCAoegAwIBAgIBJjANBgkqhkiG9w0BAQUFADBxMQswCQYDVQQGEwJERTEc -MBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxlU2Vj -IFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290IENB -IDIwHhcNOTkwNzA5MTIxMTAwWhcNMTkwNzA5MjM1OTAwWjBxMQswCQYDVQQGEwJE -RTEcMBoGA1UEChMTRGV1dHNjaGUgVGVsZWtvbSBBRzEfMB0GA1UECxMWVC1UZWxl -U2VjIFRydXN0IENlbnRlcjEjMCEGA1UEAxMaRGV1dHNjaGUgVGVsZWtvbSBSb290 -IENBIDIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrC6M14IspFLEU -ha88EOQ5bzVdSq7d6mGNlUn0b2SjGmBmpKlAIoTZ1KXleJMOaAGtuU1cOs7TuKhC -QN/Po7qCWWqSG6wcmtoIKyUn+WkjR/Hg6yx6m/UTAtB+NHzCnjwAWav12gz1Mjwr -rFDa1sPeg5TKqAyZMg4ISFZbavva4VhYAUlfckE8FQYBjl2tqriTtM2e66foai1S -NNs671x1Udrb8zH57nGYMsRUFUQM+ZtV7a3fGAigo4aKSe5TBY8ZTNXeWHmb0moc -QqvF1afPaA+W5OFhmHZhyJF81j4A4pFQh+GdCuatl9Idxjp9y7zaAzTVjlsB9WoH -txa2bkp/AgMBAAGjQjBAMB0GA1UdDgQWBBQxw3kbuvVT1xfgiXotF2wKsyudMzAP -BgNVHRMECDAGAQH/AgEFMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOC -AQEAlGRZrTlk5ynrE/5aw4sTV8gEJPB0d8Bg42f76Ymmg7+Wgnxu1MM9756Abrsp -tJh6sTtU6zkXR34ajgv8HzFZMQSyzhfzLMdiNlXiItiJVbSYSKpk+tYcNthEeFpa -IzpXl/V6ME+un2pMSyuOoAPjPuCp1NJ70rOo4nI8rZ7/gFnkm0W09juwzTkZmDLl -6iFhkOQxIY40sfcvNUqFENrnijchvllj4PKFiDFT1FQUhXB59C4Gdyd1Lx+4ivn+ -xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU -Cm26OWMohpLzGITY+9HPBVZkVw== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDtzCCAp+gAwIBAgIQDOfg5RfYRv6P5WD8G/AwOTANBgkqhkiG9w0BAQUFADBl -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv -b3QgQ0EwHhcNMDYxMTEwMDAwMDAwWhcNMzExMTEwMDAwMDAwWjBlMQswCQYDVQQG -EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl -cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCtDhXO5EOAXLGH87dg+XESpa7c -JpSIqvTO9SA5KFhgDPiA2qkVlTJhPLWxKISKityfCgyDF3qPkKyK53lTXDGEKvYP -mDI2dsze3Tyoou9q+yHyUmHfnyDXH+Kx2f4YZNISW1/5WBg1vEfNoTb5a3/UsDg+ -wRvDjDPZ2C8Y/igPs6eD1sNuRMBhNZYW/lmci3Zt1/GiSw0r/wty2p5g0I6QNcZ4 -VYcgoc/lbQrISXwxmDNsIumH0DJaoroTghHtORedmTpyoeb6pNnVFzF1roV9Iq4/ -AUaG9ih5yLHa5FcXxH4cDrC0kqZWs72yl+2qp/C3xag/lRbQ/6GW6whfGHdPAgMB -AAGjYzBhMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW -BBRF66Kv9JLLgjEtUYunpyGd823IDzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun -pyGd823IDzANBgkqhkiG9w0BAQUFAAOCAQEAog683+Lt8ONyc3pklL/3cmbYMuRC -dWKuh+vy1dneVrOfzM4UKLkNl2BcEkxY5NM9g0lFWJc1aRqoR+pWxnmrEthngYTf -fwk8lOa4JiwgvT2zKIn3X/8i4peEH+ll74fg38FnSbNd67IJKusm7Xi+fT8r87cm -NW1fiQG2SVufAQWbqz0lwcy2f8Lxb4bG+mRo64EtlOtCt/qMHt1i8b5QZ7dsvfPx -H2sMNgcWfzd8qVttevESRmCD1ycEvkvOl77DZypoEd+A5wwzZr8TDRRu838fYxAe -+o0bJW1sj6W3YQGx0qMmoRBxna3iw/nDmVG3KwcIzi7mULKn+gpFL6Lw8g== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDljCCAn6gAwIBAgIQC5McOtY5Z+pnI7/Dr5r0SzANBgkqhkiG9w0BAQsFADBl -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJv -b3QgRzIwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQG -EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl -cnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzIwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ5ygvUj82ckmIkzTz+GoeMVSA -n61UQbVH35ao1K+ALbkKz3X9iaV9JPrjIgwrvJUXCzO/GU1BBpAAvQxNEP4Htecc -biJVMWWXvdMX0h5i89vqbFCMP4QMls+3ywPgym2hFEwbid3tALBSfK+RbLE4E9Hp -EgjAALAcKxHad3A2m67OeYfcgnDmCXRwVWmvo2ifv922ebPynXApVfSr/5Vh88lA -bx3RvpO704gqu52/clpWcTs/1PPRCv4o76Pu2ZmvA9OPYLfykqGxvYmJHzDNw6Yu -YjOuFgJ3RFrngQo8p0Quebg/BLxcoIfhG69Rjs3sLPr4/m3wOnyqi+RnlTGNAgMB -AAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQW -BBTOw0q5mVXyuNtgv6l+vVa1lzan1jANBgkqhkiG9w0BAQsFAAOCAQEAyqVVjOPI -QW5pJ6d1Ee88hjZv0p3GeDgdaZaikmkuOGybfQTUiaWxMTeKySHMq2zNixya1r9I -0jJmwYrA8y8678Dj1JGG0VDjA9tzd29KOVPt3ibHtX2vK0LRdWLjSisCx1BL4Gni -lmwORGYQRI+tBev4eaymG+g3NJ1TyWGqolKvSnAWhsI6yLETcDbYz+70CjTVW0z9 -B5yiutkBclzzTcHdDrEcDcRjvq30FPuJ7KJBDkzMyFdA0G4Dqs0MjomZmWzwPDCv -ON9vvKO+KSAnq3T/EyJ43pdSVR6DtVQgA+6uwE9W3jfMw3+qBCe703e4YtsXfJwo -IhNzbM8m9Yop5w== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICRjCCAc2gAwIBAgIQC6Fa+h3foLVJRK/NJKBs7DAKBggqhkjOPQQDAzBlMQsw -CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu -ZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3Qg -RzMwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBlMQswCQYDVQQGEwJV -UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu -Y29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgRzMwdjAQBgcq -hkjOPQIBBgUrgQQAIgNiAAQZ57ysRGXtzbg/WPuNsVepRC0FFfLvC/8QdJ+1YlJf -Zn4f5dwbRXkLzMZTCp2NXQLZqVneAlr2lSoOjThKiknGvMYDOAdfVdp+CW7if17Q -RSAPWXYQ1qAk8C3eNvJsKTmjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/ -BAQDAgGGMB0GA1UdDgQWBBTL0L2p4ZgFUaFNN6KDec6NHSrkhDAKBggqhkjOPQQD -AwNnADBkAjAlpIFFAmsSS3V0T8gj43DydXLefInwz5FyYZ5eEJJZVrmDxxDnOOlY -JjZ91eQ0hjkCMHw2U/Aw5WJjOpnitqM7mzT6HtoQknFekROn3aRukswy1vUhZscv -6pZjamVFkpUBtA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD -QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB -CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97 -nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt -43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P -T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4 -gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO -BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR -TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw -DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr -hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg -06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF -PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls -YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk -CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDjjCCAnagAwIBAgIQAzrx5qcRqaC7KGSxHQn65TANBgkqhkiG9w0BAQsFADBh -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBH -MjAeFw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVT -MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j -b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEcyMIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzfNNNx7a8myaJCtSnX/RrohCgiN9RlUyfuI -2/Ou8jqJkTx65qsGGmvPrC3oXgkkRLpimn7Wo6h+4FR1IAWsULecYxpsMNzaHxmx -1x7e/dfgy5SDN67sH0NO3Xss0r0upS/kqbitOtSZpLYl6ZtrAGCSYP9PIUkY92eQ -q2EGnI/yuum06ZIya7XzV+hdG82MHauVBJVJ8zUtluNJbd134/tJS7SsVQepj5Wz -tCO7TG1F8PapspUwtP1MVYwnSlcUfIKdzXOS0xZKBgyMUNGPHgm+F6HmIcr9g+UQ -vIOlCsRnKPZzFBQ9RnbDhxSJITRNrw9FDKZJobq7nMWxM4MphQIDAQABo0IwQDAP -BgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAdBgNVHQ4EFgQUTiJUIBiV -5uNu5g/6+rkS7QYXjzkwDQYJKoZIhvcNAQELBQADggEBAGBnKJRvDkhj6zHd6mcY -1Yl9PMWLSn/pvtsrF9+wX3N3KjITOYFnQoQj8kVnNeyIv/iPsGEMNKSuIEyExtv4 -NeF22d+mQrvHRAiGfzZ0JFrabA0UWTW98kndth/Jsw1HKj2ZL7tcu7XUIOGZX1NG -Fdtom/DzMNU+MeKNhJ7jitralj41E6Vf8PlwUHBHQRFXGU7Aj64GxJUTFy8bJZ91 -8rGOmaFvE7FBcf6IKshPECBV1/MUReXgRPTqh5Uykw7+U0b6LJ3/iyK5S9kJRaTe -pLiaWN0bfVKfjllDiIGknibVb63dDcY3fe0Dkhvld1927jyNxF1WW6LZZm6zNTfl -MrY= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICPzCCAcWgAwIBAgIQBVVWvPJepDU1w6QP1atFcjAKBggqhkjOPQQDAzBhMQsw -CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu -ZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBHMzAe -Fw0xMzA4MDExMjAwMDBaFw0zODAxMTUxMjAwMDBaMGExCzAJBgNVBAYTAlVTMRUw -EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x -IDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IEczMHYwEAYHKoZIzj0CAQYF -K4EEACIDYgAE3afZu4q4C/sLfyHS8L6+c/MzXRq8NOrexpu80JX28MzQC7phW1FG -fp4tn+6OYwwX7Adw9c+ELkCDnOg/QW07rdOkFFk2eJ0DQ+4QE2xy3q6Ip6FrtUPO -Z9wj/wMco+I+o0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAd -BgNVHQ4EFgQUs9tIpPmhxdiuNkHMEWNpYim8S8YwCgYIKoZIzj0EAwMDaAAwZQIx -AK288mw/EkrRLTnDCgmXc/SINoyIJ7vmiI1Qhadj+Z4y3maTD/HMsQmP3Wyr+mt/ -oAIwOWZbwmSNuJ5Q3KjVSaLtx9zRSX8XAbjIho9OjIgrqJqpisXRAL34VOKa5Vt8 -sycX ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm -+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW -PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM -xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB -Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 -hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg -EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA -FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec -nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z -eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF -hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 -Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe -vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep -+OkuE6N36B9K ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFkDCCA3igAwIBAgIQBZsbV56OITLiOQe9p3d1XDANBgkqhkiG9w0BAQwFADBi -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg -RzQwHhcNMTMwODAxMTIwMDAwWhcNMzgwMTE1MTIwMDAwWjBiMQswCQYDVQQGEwJV -UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu -Y29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz7MKnJS7JIT3y -ithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS5F/WBTxSD1If -xp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7bXHiLQwb7iDV -ySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfISKhmV1efVFiO -DCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jHtrHEtWoYOAMQ -jdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14Ztk6MUSaM0C/ -CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2h4mXaXpI8OCi -EhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt6zPZxd9LBADM -fRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPRiQfhvbfmQ6QY -uKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ERElvlEFDrMcXK -chYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4KJpn15GkvmB0t -9dmpsh3lGwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB -hjAdBgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wDQYJKoZIhvcNAQEMBQAD -ggIBALth2X2pbL4XxJEbw6GiAI3jZGgPVs93rnD5/ZpKmbnJeFwMDF/k5hQpVgs2 -SV1EY+CtnJYYZhsjDT156W1r1lT40jzBQ0CuHVD1UvyQO7uYmWlrx8GnqGikJ9yd -+SeuMIW59mdNOj6PWTkiU0TryF0Dyu1Qen1iIQqAyHNm0aAFYF/opbSnr6j3bTWc -fFqK1qI4mfN4i/RN0iAL3gTujJtHgXINwBQy7zBZLq7gcfJW5GqXb5JQbZaNaHqa -sjYUegbyJLkJEVDXCLG4iXqEI2FCKeWjzaIgQdfRnGTZ6iahixTXTBmyUEFxPT9N -cCOGDErcgdLMMpSEDQgJlxxPwO5rIHQw0uA5NBCFIRUBCOhVMt5xSdkoF1BN5r5N -0XWs0Mr7QbhDparTwwVETyw2m+L64kW4I1NsBm9nVX9GtUw/bihaeSbSpKhil9Ie -4u1Ki7wb/UdKDd9nZn6yW0HQO+T0O/QEY+nvwlQAUaCKKsnOeMzV6ocEGLPOr0mI -r/OSmbaz5mEP0oUA51Aa5BuVnRmhuZyxm7EAHu/QD09CbMkKvO5D+jpxpchNJqU1 -/YldvIViHTLSoCtU7ZpXwdv6EM8Zt4tKG48BtieVU+i2iW1bvGjUI+iLUaJW+fCm -gKDWHrO8Dw9TdSmq6hN35N6MgSGtBxBHEa2HPQfRdbzP82Z+ ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGSzCCBDOgAwIBAgIIamg+nFGby1MwDQYJKoZIhvcNAQELBQAwgbIxCzAJBgNV -BAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+BgNVBAoMN0UtVHXEn3JhIEVCRyBC -aWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhpem1ldGxlcmkgQS7Fni4xJjAkBgNV -BAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBNZXJrZXppMSgwJgYDVQQDDB9FLVR1 -Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTEzMDMwNTEyMDk0OFoXDTIz -MDMwMzEyMDk0OFowgbIxCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExQDA+ -BgNVBAoMN0UtVHXEn3JhIEVCRyBCaWxpxZ9pbSBUZWtub2xvamlsZXJpIHZlIEhp -em1ldGxlcmkgQS7Fni4xJjAkBgNVBAsMHUUtVHVncmEgU2VydGlmaWthc3lvbiBN -ZXJrZXppMSgwJgYDVQQDDB9FLVR1Z3JhIENlcnRpZmljYXRpb24gQXV0aG9yaXR5 -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA4vU/kwVRHoViVF56C/UY -B4Oufq9899SKa6VjQzm5S/fDxmSJPZQuVIBSOTkHS0vdhQd2h8y/L5VMzH2nPbxH -D5hw+IyFHnSOkm0bQNGZDbt1bsipa5rAhDGvykPL6ys06I+XawGb1Q5KCKpbknSF -Q9OArqGIW66z6l7LFpp3RMih9lRozt6Plyu6W0ACDGQXwLWTzeHxE2bODHnv0ZEo -q1+gElIwcxmOj+GMB6LDu0rw6h8VqO4lzKRG+Bsi77MOQ7osJLjFLFzUHPhdZL3D -k14opz8n8Y4e0ypQBaNV2cvnOVPAmJ6MVGKLJrD3fY185MaeZkJVgkfnsliNZvcH -fC425lAcP9tDJMW/hkd5s3kc91r0E+xs+D/iWR+V7kI+ua2oMoVJl0b+SzGPWsut -dEcf6ZG33ygEIqDUD13ieU/qbIWGvaimzuT6w+Gzrt48Ue7LE3wBf4QOXVGUnhMM -ti6lTPk5cDZvlsouDERVxcr6XQKj39ZkjFqzAQqptQpHF//vkUAqjqFGOjGY5RH8 -zLtJVor8udBhmm9lbObDyz51Sf6Pp+KJxWfXnUYTTjF2OySznhFlhqt/7x3U+Lzn -rFpct1pHXFXOVbQicVtbC/DP3KBhZOqp12gKY6fgDT+gr9Oq0n7vUaDmUStVkhUX -U8u3Zg5mTPj5dUyQ5xJwx0UCAwEAAaNjMGEwHQYDVR0OBBYEFC7j27JJ0JxUeVz6 -Jyr+zE7S6E5UMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAULuPbsknQnFR5 -XPonKv7MTtLoTlQwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4ICAQAF -Nzr0TbdF4kV1JI+2d1LoHNgQk2Xz8lkGpD4eKexd0dCrfOAKkEh47U6YA5n+KGCR -HTAduGN8qOY1tfrTYXbm1gdLymmasoR6d5NFFxWfJNCYExL/u6Au/U5Mh/jOXKqY -GwXgAEZKgoClM4so3O0409/lPun++1ndYYRP0lSWE2ETPo+Aab6TR7U1Q9Jauz1c -77NCR807VRMGsAnb/WP2OogKmW9+4c4bU2pEZiNRCHu8W1Ki/QY3OEBhj0qWuJA3 -+GbHeJAAFS6LrVE1Uweoa2iu+U48BybNCAVwzDk/dr2l02cmAYamU9JgO3xDf1WK -vJUawSg5TB9D0pH0clmKuVb8P7Sd2nCcdlqMQ1DujjByTd//SffGqWfZbawCEeI6 -FiWnWAjLb1NBnEg4R2gz0dfHj9R0IdTDBZB6/86WiLEVKV0jq9BgoRJP3vQXzTLl -yb/IQ639Lo7xr+L0mPoSHyDYwKcMhcWQ9DstliaxLL5Mq+ux0orJ23gTDx4JnW2P -AJ8C2sH6H3p6CcRK5ogql5+Ji/03X186zjhZhkuvcQu02PJwT58yE+Owp1fl2tpD -y4Q08ijE6m30Ku/Ba3ba+367hTzSU8JNvnHhRdH9I2cNE3X7z2VnIp2usAnRCf8d -NL/+I5c30jn6PQ0GC7TbO6Orb1wdtn7os4I07QZcJA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB -8zELMAkGA1UEBhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2Vy -dGlmaWNhY2lvIChOSUYgUS0wODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1 -YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYDVQQLEyxWZWdldSBodHRwczovL3d3 -dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UECxMsSmVyYXJxdWlh -IEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMTBkVD -LUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQG -EwJFUzE7MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8g -KE5JRiBRLTA4MDExNzYtSSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBD -ZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZlZ2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQu -bmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJhcnF1aWEgRW50aXRhdHMg -ZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUNDMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R -85iKw5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm -4CgPukLjbo73FCeTae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaV -HMf5NLWUhdWZXqBIoH7nF2W4onW4HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNd -QlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0aE9jD2z3Il3rucO2n5nzbcc8t -lGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw0JDnJwIDAQAB -o4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4 -opvpXY0wfwYDVR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBo -dHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidW -ZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAwDQYJKoZIhvcN -AQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJlF7W2u++AVtd0x7Y -/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNaAl6k -SBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhy -Rp/7SNVel+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOS -Agu+TGbrIP65y7WZf+a2E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xl -nJ2lYJU6Un/10asIbvPuW/mIPX64b24D5EI= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEAzCCAuugAwIBAgIQVID5oHPtPwBMyonY43HmSjANBgkqhkiG9w0BAQUFADB1 -MQswCQYDVQQGEwJFRTEiMCAGA1UECgwZQVMgU2VydGlmaXRzZWVyaW1pc2tlc2t1 -czEoMCYGA1UEAwwfRUUgQ2VydGlmaWNhdGlvbiBDZW50cmUgUm9vdCBDQTEYMBYG -CSqGSIb3DQEJARYJcGtpQHNrLmVlMCIYDzIwMTAxMDMwMTAxMDMwWhgPMjAzMDEy -MTcyMzU5NTlaMHUxCzAJBgNVBAYTAkVFMSIwIAYDVQQKDBlBUyBTZXJ0aWZpdHNl -ZXJpbWlza2Vza3VzMSgwJgYDVQQDDB9FRSBDZXJ0aWZpY2F0aW9uIENlbnRyZSBS -b290IENBMRgwFgYJKoZIhvcNAQkBFglwa2lAc2suZWUwggEiMA0GCSqGSIb3DQEB -AQUAA4IBDwAwggEKAoIBAQDIIMDs4MVLqwd4lfNE7vsLDP90jmG7sWLqI9iroWUy -euuOF0+W2Ap7kaJjbMeMTC55v6kF/GlclY1i+blw7cNRfdCT5mzrMEvhvH2/UpvO -bntl8jixwKIy72KyaOBhU8E2lf/slLo2rpwcpzIP5Xy0xm90/XsY6KxX7QYgSzIw -WFv9zajmofxwvI6Sc9uXp3whrj3B9UiHbCe9nyV0gVWw93X2PaRka9ZP585ArQ/d -MtO8ihJTmMmJ+xAdTX7Nfh9WDSFwhfYggx/2uh8Ej+p3iDXE/+pOoYtNP2MbRMNE -1CV2yreN1x5KZmTNXMWcg+HCCIia7E6j8T4cLNlsHaFLAgMBAAGjgYowgYcwDwYD -VR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBLyWj7qVhy/ -zQas8fElyalL1BSZMEUGA1UdJQQ+MDwGCCsGAQUFBwMCBggrBgEFBQcDAQYIKwYB -BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDCAYIKwYBBQUHAwkwDQYJKoZIhvcNAQEF -BQADggEBAHv25MANqhlHt01Xo/6tu7Fq1Q+e2+RjxY6hUFaTlrg4wCQiZrxTFGGV -v9DHKpY5P30osxBAIWrEr7BSdxjhlthWXePdNl4dp1BUoMUq5KqMlIpPnTX/dqQG -E5Gion0ARD9V04I8GtVbvFZMIi5GQ4okQC3zErg7cBqklrkar4dBGmoYDQZPxz5u -uSlNDUmJEYcyW+ZLBMjkXOZ0c5RdFpgTlf7727FE5TpwrDdr5rMzcijJs1eg9gIW -iAYLtqZLICjU3j2LrTcFU3T+bsy8QxdxXvnFzBqpYe73dgzzcvRyrc9yAjYHR8/v -GVCJYMzpJJUPwssd8m92kMfMdcGWxZ0= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEKjCCAxKgAwIBAgIEOGPe+DANBgkqhkiG9w0BAQUFADCBtDEUMBIGA1UEChML -RW50cnVzdC5uZXQxQDA+BgNVBAsUN3d3dy5lbnRydXN0Lm5ldC9DUFNfMjA0OCBp -bmNvcnAuIGJ5IHJlZi4gKGxpbWl0cyBsaWFiLikxJTAjBgNVBAsTHChjKSAxOTk5 -IEVudHJ1c3QubmV0IExpbWl0ZWQxMzAxBgNVBAMTKkVudHJ1c3QubmV0IENlcnRp -ZmljYXRpb24gQXV0aG9yaXR5ICgyMDQ4KTAeFw05OTEyMjQxNzUwNTFaFw0yOTA3 -MjQxNDE1MTJaMIG0MRQwEgYDVQQKEwtFbnRydXN0Lm5ldDFAMD4GA1UECxQ3d3d3 -LmVudHJ1c3QubmV0L0NQU18yMDQ4IGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxp -YWIuKTElMCMGA1UECxMcKGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDEzMDEG -A1UEAxMqRW50cnVzdC5uZXQgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgKDIwNDgp -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArU1LqRKGsuqjIAcVFmQq -K0vRvwtKTY7tgHalZ7d4QMBzQshowNtTK91euHaYNZOLGp18EzoOH1u3Hs/lJBQe -sYGpjX24zGtLA/ECDNyrpUAkAH90lKGdCCmziAv1h3edVc3kw37XamSrhRSGlVuX -MlBvPci6Zgzj/L24ScF2iUkZ/cCovYmjZy/Gn7xxGWC4LeksyZB2ZnuU4q941mVT -XTzWnLLPKQP5L6RQstRIzgUyVYr9smRMDuSYB3Xbf9+5CFVghTAp+XtIpGmG4zU/ -HoZdenoVve8AjhUiVBcAkCaTvA5JaJG/+EfTnZVCwQ5N328mz8MYIWJmQ3DW1cAH -4QIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV -HQ4EFgQUVeSB0RGAvtiJuQijMfmhJAkWuXAwDQYJKoZIhvcNAQEFBQADggEBADub -j1abMOdTmXx6eadNl9cZlZD7Bh/KM3xGY4+WZiT6QBshJ8rmcnPyT/4xmf3IDExo -U8aAghOY+rat2l098c5u9hURlIIM7j+VrxGrD9cv3h8Dj1csHsm7mhpElesYT6Yf -zX1XEC+bBAlahLVu2B064dae0Wx5XnkcFMXj0EyTO2U87d89vqbllRrDtRnDvV5b -u/8j72gZyxKTJ1wDLW8w0B62GqzeWvfRqqgnpv55gcR5mTNXuhKwqeBCbJPKVt7+ -bYQLCIt+jerXmCHG8+c8eS9enNFMFY3h7CI3zJpDC5fcgJCNs2ebb0gIFVbPv/Er -fF6adulZkMV8gzURZVE= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEkTCCA3mgAwIBAgIERWtQVDANBgkqhkiG9w0BAQUFADCBsDELMAkGA1UEBhMC -VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xOTA3BgNVBAsTMHd3dy5lbnRydXN0 -Lm5ldC9DUFMgaXMgaW5jb3Jwb3JhdGVkIGJ5IHJlZmVyZW5jZTEfMB0GA1UECxMW -KGMpIDIwMDYgRW50cnVzdCwgSW5jLjEtMCsGA1UEAxMkRW50cnVzdCBSb290IENl -cnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA2MTEyNzIwMjM0MloXDTI2MTEyNzIw -NTM0MlowgbAxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1FbnRydXN0LCBJbmMuMTkw -NwYDVQQLEzB3d3cuZW50cnVzdC5uZXQvQ1BTIGlzIGluY29ycG9yYXRlZCBieSBy -ZWZlcmVuY2UxHzAdBgNVBAsTFihjKSAyMDA2IEVudHJ1c3QsIEluYy4xLTArBgNV -BAMTJEVudHJ1c3QgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASIwDQYJ -KoZIhvcNAQEBBQADggEPADCCAQoCggEBALaVtkNC+sZtKm9I35RMOVcF7sN5EUFo -Nu3s/poBj6E4KPz3EEZmLk0eGrEaTsbRwJWIsMn/MYszA9u3g3s+IIRe7bJWKKf4 -4LlAcTfFy0cOlypowCKVYhXbR9n10Cv/gkvJrT7eTNuQgFA/CYqEAOwwCj0Yzfv9 -KlmaI5UXLEWeH25DeW0MXJj+SKfFI0dcXv1u5x609mhF0YaDW6KKjbHjKYD+JXGI -rb68j6xSlkuqUY3kEzEZ6E5Nn9uss2rVvDlUccp6en+Q3X0dgNmBu1kmwhH+5pPi -94DkZfs0Nw4pgHBNrziGLp5/V6+eF67rHMsoIV+2HNjnogQi+dPa2MsCAwEAAaOB -sDCBrTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zArBgNVHRAEJDAi -gA8yMDA2MTEyNzIwMjM0MlqBDzIwMjYxMTI3MjA1MzQyWjAfBgNVHSMEGDAWgBRo -kORnpKZTgMeGZqTx90tD+4S9bTAdBgNVHQ4EFgQUaJDkZ6SmU4DHhmak8fdLQ/uE -vW0wHQYJKoZIhvZ9B0EABBAwDhsIVjcuMTo0LjADAgSQMA0GCSqGSIb3DQEBBQUA -A4IBAQCT1DCw1wMgKtD5Y+iRDAUgqV8ZyntyTtSx29CW+1RaGSwMCPeyvIWonX9t -O1KzKtvn1ISMY/YPyyYBkVBs9F8U4pN0wBOeMDpQ47RgxRzwIkSNcUesyBrJ6Zua -AGAT/3B+XxFNSRuzFVJ7yVTav52Vr2ua2J7p8eRDjeIRRDq/r72DQnNSi6q7pynP -9WQcCk3RvKqsnyrQ/39/2n3qse0wJcGE2jTSW3iDVuycNsMm4hH2Z0kdkquM++v/ -eu6FSqdQgPCnXEqULl8FmTxSQeDNtGPPAUO6nIPcj2A781q0tHuu2guQOHXvgR1m -0vdXcDazv/wor3ElhVsT/h5/WrQ8 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIC+TCCAoCgAwIBAgINAKaLeSkAAAAAUNCR+TAKBggqhkjOPQQDAzCBvzELMAkG -A1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3 -d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDEyIEVu -dHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEzMDEGA1UEAxMq -RW50cnVzdCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRUMxMB4XDTEy -MTIxODE1MjUzNloXDTM3MTIxODE1NTUzNlowgb8xCzAJBgNVBAYTAlVTMRYwFAYD -VQQKEw1FbnRydXN0LCBJbmMuMSgwJgYDVQQLEx9TZWUgd3d3LmVudHJ1c3QubmV0 -L2xlZ2FsLXRlcm1zMTkwNwYDVQQLEzAoYykgMjAxMiBFbnRydXN0LCBJbmMuIC0g -Zm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxMzAxBgNVBAMTKkVudHJ1c3QgUm9vdCBD -ZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEVDMTB2MBAGByqGSM49AgEGBSuBBAAi -A2IABIQTydC6bUF74mzQ61VfZgIaJPRbiWlH47jCffHyAsWfoPZb1YsGGYZPUxBt -ByQnoaD41UcZYUx9ypMn6nQM72+WCf5j7HBdNq1nd67JnXxVRDqiY1Ef9eNi1KlH -Bz7MIKNCMEAwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O -BBYEFLdj5xrdjekIplWDpOBqUEFlEUJJMAoGCCqGSM49BAMDA2cAMGQCMGF52OVC -R98crlOZF7ZvHH3hvxGU0QOIdeSNiaSKd0bebWHvAvX7td/M/k7//qnmpwIwW5nX -hTcGtXsI/esni0qU+eH6p44mCOh8kmhtc9hvJqwhAriZtyZBWyVgrtBIGu4G ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC -VVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50 -cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs -IEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz -dCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy -NTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu -dHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt -dGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0 -aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj -YXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK -AoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T -RU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN -cCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW -wcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1 -U1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0 -jaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP -BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN -BgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/ -jTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ -Rkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v -1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R -nAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH -VHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFiDCCA3CgAwIBAgIIfQmX/vBH6nowDQYJKoZIhvcNAQELBQAwYjELMAkGA1UE -BhMCQ04xMjAwBgNVBAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZ -IENPLixMVEQuMR8wHQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMB4XDTE0 -MTEyNjA1MTMxNVoXDTQwMTIzMTE1NTk1OVowYjELMAkGA1UEBhMCQ04xMjAwBgNV -BAoMKUdVQU5HIERPTkcgQ0VSVElGSUNBVEUgQVVUSE9SSVRZIENPLixMVEQuMR8w -HQYDVQQDDBZHRENBIFRydXN0QVVUSCBSNSBST09UMIICIjANBgkqhkiG9w0BAQEF -AAOCAg8AMIICCgKCAgEA2aMW8Mh0dHeb7zMNOwZ+Vfy1YI92hhJCfVZmPoiC7XJj -Dp6L3TQsAlFRwxn9WVSEyfFrs0yw6ehGXTjGoqcuEVe6ghWinI9tsJlKCvLriXBj -TnnEt1u9ol2x8kECK62pOqPseQrsXzrj/e+APK00mxqriCZ7VqKChh/rNYmDf1+u -KU49tm7srsHwJ5uu4/Ts765/94Y9cnrrpftZTqfrlYwiOXnhLQiPzLyRuEH3FMEj -qcOtmkVEs7LXLM3GKeJQEK5cy4KOFxg2fZfmiJqwTTQJ9Cy5WmYqsBebnh52nUpm -MUHfP/vFBu8btn4aRjb3ZGM74zkYI+dndRTVdVeSN72+ahsmUPI2JgaQxXABZG12 -ZuGR224HwGGALrIuL4xwp9E7PLOR5G62xDtw8mySlwnNR30YwPO7ng/Wi64HtloP -zgsMR6flPri9fcebNaBhlzpBdRfMK5Z3KpIhHtmVdiBnaM8Nvd/WHwlqmuLMc3Gk -L30SgLdTMEZeS1SZD2fJpcjyIMGC7J0R38IC+xo70e0gmu9lZJIQDSri3nDxGGeC -jGHeuLzRL5z7D9Ar7Rt2ueQ5Vfj4oR24qoAATILnsn8JuLwwoC8N9VKejveSswoA -HQBUlwbgsQfZxw9cZX08bVlX5O2ljelAU58VS6Bx9hoh49pwBiFYFIeFd3mqgnkC -AwEAAaNCMEAwHQYDVR0OBBYEFOLJQJ9NzuiaoXzPDj9lxSmIahlRMA8GA1UdEwEB -/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQDRSVfg -p8xoWLoBDysZzY2wYUWsEe1jUGn4H3++Fo/9nesLqjJHdtJnJO29fDMylyrHBYZm -DRd9FBUb1Ov9H5r2XpdptxolpAqzkT9fNqyL7FeoPueBihhXOYV0GkLH6VsTX4/5 -COmSdI31R9KrO9b7eGZONn356ZLpBN79SWP8bfsUcZNnL0dKt7n/HipzcEYwv1ry -L3ml4Y0M2fmyYzeMN2WFcGpcWwlyua1jPLHd+PwyvzeG5LuOmCd+uh8W4XAR8gPf -JWIyJyYYMoSf/wA6E7qaTfRPuBRwIrHKK5DOKcFw9C+df/KQHtZa37dG/OaG+svg -IHZ6uqbL9XzeYqWxi+7egmaKTjowHz+Ay60nugxe19CxVsp3cbK1daFQqUBDF8Io -2c9Si1vIY9RCPqAzekYu9wogRlR+ak8x8YF+QnQ4ZXMn7sZ8uI7XpTrXmKGcjBBV -09tL7ECQ8s1uV9JiDnxXk7Gnbc2dg7sq5+W2O3FYrf3RRbxake5TFW/TRQl1brqQ -XR4EzzffHqhmsYzmIGrv/EhOdJhCrylvLmrH+33RZjEizIYAfmaDDEL0vTSSwxrq -T8p+ck0LcIymSLumoRT2+1hEmRSuqguTaaApJUqlyyvdimYHFngVV3Eb7PVHhPOe -MTd61X8kreS8/f3MboPoDKi3QWwH3b08hpcv0g== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFWjCCA0KgAwIBAgIQbkepxUtHDA3sM9CJuRz04TANBgkqhkiG9w0BAQwFADBH -MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM -QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy -MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl -cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaM -f/vo27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vX -mX7wCl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7 -zUjwTcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0P -fyblqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtc -vfaHszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4 -Zor8Y/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUsp -zBmkMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOO -Rc92wO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYW -k70paDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+ -DVrNVjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgF -lQIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV -HQ4EFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBADiW -Cu49tJYeX++dnAsznyvgyv3SjgofQXSlfKqE1OXyHuY3UjKcC9FhHb8owbZEKTV1 -d5iyfNm9dKyKaOOpMQkpAWBz40d8U6iQSifvS9efk+eCNs6aaAyC58/UEBZvXw6Z -XPYfcX3v73svfuo21pdwCxXu11xWajOl40k4DLh9+42FpLFZXvRq4d2h9mREruZR -gyFmxhE+885H7pwoHyXa/6xmld01D1zvICxi/ZG6qcz8WpyTgYMpl0p8WnK0OdC3 -d8t5/Wk6kjftbjhlRn7pYL15iJdfOBL07q9bgsiG1eGZbYwE8na6SfZu6W0eX6Dv -J4J2QPim01hcDyxC2kLGe4g0x8HYRZvBPsVhHdljUEn2NIVq4BjFbkerQUIpm/Zg -DdIx02OYI5NaAIFItO/Nis3Jz5nu2Z6qNuFoS3FJFDYoOj0dzpqPJeaAcWErtXvM -+SUWgeExX6GjfhaknBZqlxi9dnKlC54dNuYvoS++cJEPqOba+MSSQGwlfnuzCdyy -F62ARPBopY+Udf90WuioAnwMCeKpSwughQtiue+hMZL77/ZRBIls6Kl0obsXs7X9 -SQ98POyDGCBDTtWTurQ0sR8WNh8M5mQ5Fkzc4P4dyKliPUDqysU0ArSuiYgzNdws -E3PYJ/HQcu51OyLemGhmW/HGY0dVHLqlCFF1pkgl ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFWjCCA0KgAwIBAgIQbkepxlqz5yDFMJo/aFLybzANBgkqhkiG9w0BAQwFADBH -MQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM -QzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIy -MDAwMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNl -cnZpY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjIwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQDO3v2m++zsFDQ8BwZabFn3GTXd98GdVarTzTukk3Lv -CvptnfbwhYBboUhSnznFt+4orO/LdmgUud+tAWyZH8QiHZ/+cnfgLFuv5AS/T3Kg -GjSY6Dlo7JUle3ah5mm5hRm9iYz+re026nO8/4Piy33B0s5Ks40FnotJk9/BW9Bu -XvAuMC6C/Pq8tBcKSOWIm8Wba96wyrQD8Nr0kLhlZPdcTK3ofmZemde4wj7I0BOd -re7kRXuJVfeKH2JShBKzwkCX44ofR5GmdFrS+LFjKBC4swm4VndAoiaYecb+3yXu -PuWgf9RhD1FLPD+M2uFwdNjCaKH5wQzpoeJ/u1U8dgbuak7MkogwTZq9TwtImoS1 -mKPV+3PBV2HdKFZ1E66HjucMUQkQdYhMvI35ezzUIkgfKtzra7tEscszcTJGr61K -8YzodDqs5xoic4DSMPclQsciOzsSrZYuxsN2B6ogtzVJV+mSSeh2FnIxZyuWfoqj -x5RWIr9qS34BIbIjMt/kmkRtWVtd9QCgHJvGeJeNkP+byKq0rxFROV7Z+2et1VsR -nTKaG73VululycslaVNVJ1zgyjbLiGH7HrfQy+4W+9OmTN6SpdTi3/UGVN4unUu0 -kzCqgc7dGtxRcw1PcOnlthYhGXmy5okLdWTK1au8CcEYof/UVKGFPP0UJAOyh9Ok -twIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV -HQ4EFgQUu//KjiOfT5nK2+JopqUVJxce2Q4wDQYJKoZIhvcNAQEMBQADggIBALZp -8KZ3/p7uC4Gt4cCpx/k1HUCCq+YEtN/L9x0Pg/B+E02NjO7jMyLDOfxA325BS0JT -vhaI8dI4XsRomRyYUpOM52jtG2pzegVATX9lO9ZY8c6DR2Dj/5epnGB3GFW1fgiT -z9D2PGcDFWEJ+YF59exTpJ/JjwGLc8R3dtyDovUMSRqodt6Sm2T4syzFJ9MHwAiA -pJiS4wGWAqoC7o87xdFtCjMwc3i5T1QWvwsHoaRc5svJXISPD+AVdyx+Jn7axEvb -pxZ3B7DNdehyQtaVhJ2Gg/LkkM0JR9SLA3DaWsYDQvTtN6LwG1BUSw7YhN4ZKJmB -R64JGz9I0cNv4rBgF/XuIwKl2gBbbZCr7qLpGzvpx0QnRY5rn/WkhLx3+WuXrD5R -RaIRpsyF7gpo8j5QOHokYh4XIDdtak23CZvJ/KRY9bb7nE4Yu5UC56GtmwfuNmsk -0jmGwZODUNKBRqhfYlcsu2xkiAhu7xNUX90txGdj08+JN7+dIPT7eoOboB6BAFDC -5AwiWVIQ7UNWhwD4FFKnHYuTjKJNRn8nxnGbJN7k2oaLDX5rIMHAnuFl2GqjpuiF -izoHCBy69Y9Vmhh1fuXsgWbRIXOhNUQLgD1bnF5vKheW0YMjiGZt5obicDIvUiLn -yOd/xCxgXS/Dr55FBcOEArf9LAhST4Ldo/DUhgkC ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICDDCCAZGgAwIBAgIQbkepx2ypcyRAiQ8DVd2NHTAKBggqhkjOPQQDAzBHMQsw -CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU -MBIGA1UEAxMLR1RTIFJvb3QgUjMwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw -MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp -Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjMwdjAQBgcqhkjOPQIBBgUrgQQA -IgNiAAQfTzOHMymKoYTey8chWEGJ6ladK0uFxh1MJ7x/JlFyb+Kf1qPKzEUURout -736GjOyxfi//qXGdGIRFBEFVbivqJn+7kAHjSxm65FSWRQmx1WyRRK2EE46ajA2A -DDL24CejQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud -DgQWBBTB8Sa6oC2uhYHP0/EqEr24Cmf9vDAKBggqhkjOPQQDAwNpADBmAjEAgFuk -fCPAlaUs3L6JbyO5o91lAFJekazInXJ0glMLfalAvWhgxeG4VDvBNhcl2MG9AjEA -njWSdIUlUfUk7GRSJFClH9voy8l27OyCbvWFGFPouOOaKaqW04MjyaR7YbPMAuhd ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICCjCCAZGgAwIBAgIQbkepyIuUtui7OyrYorLBmTAKBggqhkjOPQQDAzBHMQsw -CQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU -MBIGA1UEAxMLR1RTIFJvb3QgUjQwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw -MDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp -Y2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjQwdjAQBgcqhkjOPQIBBgUrgQQA -IgNiAATzdHOnaItgrkO4NcWBMHtLSZ37wWHO5t5GvWvVYRg1rkDdc/eJkTBa6zzu -hXyiQHY7qca4R9gq55KRanPpsXI5nymfopjTX15YhmUPoYRlBtHci8nHc8iMai/l -xKvRHYqjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud -DgQWBBSATNbrdP9JNqPV2Py1PsVq8JQdjDAKBggqhkjOPQQDAwNnADBkAjBqUFJ0 -CMRw3J5QdCHojXohw0+WbhXRIjVhLfoIN+4Zba3bssx9BzT1YBkstTTZbyACMANx -sbqjYAuG7ZoIapVon+Kz4ZNkfF6Tpt95LY2F45TPI11xzPKwTdb+mciUqXWi4w== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT -MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i -YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG -EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg -R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 -9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq -fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv -iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU -1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ -bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW -MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA -ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l -uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn -Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS -tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF -PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un -hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV -5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDfDCCAmSgAwIBAgIQGKy1av1pthU6Y2yv2vrEoTANBgkqhkiG9w0BAQUFADBY -MQswCQYDVQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjExMC8GA1UEAxMo -R2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEx -MjcwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMFgxCzAJBgNVBAYTAlVTMRYwFAYDVQQK -Ew1HZW9UcnVzdCBJbmMuMTEwLwYDVQQDEyhHZW9UcnVzdCBQcmltYXJ5IENlcnRp -ZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEAvrgVe//UfH1nrYNke8hCUy3f9oQIIGHWAVlqnEQRr+92/ZV+zmEwu3qDXwK9 -AWbK7hWNb6EwnL2hhZ6UOvNWiAAxz9juapYC2e0DjPt1befquFUWBRaa9OBesYjA -ZIVcFU2Ix7e64HXprQU9nceJSOC7KMgD4TCTZF5SwFlwIjVXiIrxlQqD17wxcwE0 -7e9GceBrAqg1cmuXm2bgyxx5X9gaBGgeRwLmnWDiNpcB3841kt++Z8dtd1k7j53W -kBWUvEI0EME5+bEnPn7WinXFsq+W06Lem+SYvn3h6YGttm/81w7a4DSwDRp35+MI -mO9Y+pyEtzavwt+s0vQQBnBxNQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4G -A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQULNVQQZcVi/CPNmFbSvtr2ZnJM5IwDQYJ -KoZIhvcNAQEFBQADggEBAFpwfyzdtzRP9YZRqSa+S7iq8XEN3GHHoOo0Hnp3DwQ1 -6CePbJC/kRYkRj5KTs4rFtULUh38H2eiAkUxT87z+gOneZ1TatnaYzr4gNfTmeGl -4b7UVXGYNTq+k+qurUKykG/g/CFNNWMziUnWm07Kx+dOCQD32sfvmWKZd7aVIl6K -oKv0uHiYyjgZmclynnjNS6yvGaBzEi38wkG6gZHaFloxt/m0cYASSJlyc1pZU8Fj -UjPtp8nSOQJw+uCxQmYpqptR7TBUIhRf2asdweSU8Pj1K/fqynhG1riR/aYNKxoU -AT6A8EKglQdebc3MS6RFjasS6LPeWuWgfOgPIh1a6Vk= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICrjCCAjWgAwIBAgIQPLL0SAoA4v7rJDteYD7DazAKBggqhkjOPQQDAzCBmDEL -MAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsTMChj -KSAyMDA3IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTE2 -MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 -eSAtIEcyMB4XDTA3MTEwNTAwMDAwMFoXDTM4MDExODIzNTk1OVowgZgxCzAJBgNV -BAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykgMjAw -NyBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0BgNV -BAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBH -MjB2MBAGByqGSM49AgEGBSuBBAAiA2IABBWx6P0DFUPlrOuHNxFi79KDNlJ9RVcL -So17VDs6bl8VAsBQps8lL33KSLjHUGMcKiEIfJo22Av+0SbFWDEwKCXzXV2juLal -tJLtbCyf691DiaI8S0iRHVDsJt/WYC69IaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO -BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFBVfNVdRVfslsq0DafwBo/q+EVXVMAoG -CCqGSM49BAMDA2cAMGQCMGSWWaboCd6LuvpaiIjwH5HTRqjySkwCY/tsXzjbLkGT -qQ7mndwxHLKgpxgceeHHNgIwOlavmnRs9vuD4DPTCF+hnMJbn0bWtsuRBmOiBucz -rD6ogRLQy7rQkgu2npaqBA+K ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID/jCCAuagAwIBAgIQFaxulBmyeUtB9iepwxgPHzANBgkqhkiG9w0BAQsFADCB -mDELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xOTA3BgNVBAsT -MChjKSAyMDA4IEdlb1RydXN0IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25s -eTE2MDQGA1UEAxMtR2VvVHJ1c3QgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhv -cml0eSAtIEczMB4XDTA4MDQwMjAwMDAwMFoXDTM3MTIwMTIzNTk1OVowgZgxCzAJ -BgNVBAYTAlVTMRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMTkwNwYDVQQLEzAoYykg -MjAwOCBHZW9UcnVzdCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxNjA0 -BgNVBAMTLUdlb1RydXN0IFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg -LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANziXmJYHTNXOTIz -+uvLh4yn1ErdBojqZI4xmKU4kB6Yzy5jK/BGvESyiaHAKAxJcCGVn2TAppMSAmUm -hsalifD614SgcK9PGpc/BkTVyetyEH3kMSj7HGHmKAdEc5IiaacDiGydY8hS2pgn -5whMcD60yRLBxWeDXTPzAxHsatBT4tG6NmCUgLthY2xbF37fQJQeqw3CIShwiP/W -JmxsYAQlTlV+fe+/lEjetx3dcI0FX4ilm/LC7urRQEFtYjgdVgbFA0dRIBn8exAL -DmKudlW/X3e+PkkBUz2YJQN2JFodtNuJ6nnltrM7P7pMKEF/BqxqjsHQ9gUdfeZC -huOl1UcCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw -HQYDVR0OBBYEFMR5yo6hTgMdHNxr2zFblD4/MH8tMA0GCSqGSIb3DQEBCwUAA4IB -AQAtxRPPVoB7eni9n64smefv2t+UXglpp+duaIy9cr5HqQ6XErhK8WTTOd8lNNTB -zU6B8A8ExCSzNJbGpqow32hhc9f5joWJ7w5elShKKiePEI4ufIbEAp7aDHdlDkQN -kv39sxY2+hENHYwOB4lqKVb3cvTdFZx3NWZXqxNT2I7BQMXXExZacse3aQHEerGD -AWh9jUGhlBjBJVz88P6DAod8DQ3PLghcSkANPuyBYeYk28rgDi0Hsj5W3I31QYUH -SJsMC8tJP33st/3LjWeJGqvtux6jAAgIFyqCXDFdRootD4abdNlF+9RAsXqqaC2G -spki4cErx5z481+oghLrGREt ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFaDCCA1CgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJVUzEW -MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEeMBwGA1UEAxMVR2VvVHJ1c3QgVW5pdmVy -c2FsIENBMB4XDTA0MDMwNDA1MDAwMFoXDTI5MDMwNDA1MDAwMFowRTELMAkGA1UE -BhMCVVMxFjAUBgNVBAoTDUdlb1RydXN0IEluYy4xHjAcBgNVBAMTFUdlb1RydXN0 -IFVuaXZlcnNhbCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKYV -VaCjxuAfjJ0hUNfBvitbtaSeodlyWL0AG0y/YckUHUWCq8YdgNY96xCcOq9tJPi8 -cQGeBvV8Xx7BDlXKg5pZMK4ZyzBIle0iN430SppyZj6tlcDgFgDgEB8rMQ7XlFTT -QjOgNB0eRXbdT8oYN+yFFXoZCPzVx5zw8qkuEKmS5j1YPakWaDwvdSEYfyh3peFh -F7em6fgemdtzbvQKoiFs7tqqhZJmr/Z6a4LauiIINQ/PQvE1+mrufislzDoR5G2v -c7J2Ha3QsnhnGqQ5HFELZ1aD/ThdDc7d8Lsrlh/eezJS/R27tQahsiFepdaVaH/w -mZ7cRQg+59IJDTWU3YBOU5fXtQlEIGQWFwMCTFMNaN7VqnJNk22CDtucvc+081xd -VHppCZbW2xHBjXWotM85yM48vCR85mLK4b19p71XZQvk/iXttmkQ3CgaRr0BHdCX -teGYO8A3ZNY9lO4L4fUorgtWv3GLIylBjobFS1J72HGrH4oVpjuDWtdYAVHGTEHZ -f9hBZ3KiKN9gg6meyHv8U3NyWfWTehd2Ds735VzZC1U0oqpbtWpU5xPKV+yXbfRe -Bi9Fi1jUIxaS5BZuKGNZMN9QAZxjiRqf2xeUgnA3wySemkfWWspOqGmJch+RbNt+ -nhutxx9z3SxPGWX9f5NAEC7S8O08ni4oPmkmM8V7AgMBAAGjYzBhMA8GA1UdEwEB -/wQFMAMBAf8wHQYDVR0OBBYEFNq7LqqwDLiIJlF0XG0D08DYj3rWMB8GA1UdIwQY -MBaAFNq7LqqwDLiIJlF0XG0D08DYj3rWMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG -9w0BAQUFAAOCAgEAMXjmx7XfuJRAyXHEqDXsRh3ChfMoWIawC/yOsjmPRFWrZIRc -aanQmjg8+uUfNeVE44B5lGiku8SfPeE0zTBGi1QrlaXv9z+ZhP015s8xxtxqv6fX -IwjhmF7DWgh2qaavdy+3YL1ERmrvl/9zlcGO6JP7/TG37FcREUWbMPEaiDnBTzyn -ANXH/KttgCJwpQzgXQQpAvvLoJHRfNbDflDVnVi+QTjruXU8FdmbyUqDWcDaU/0z -uzYYm4UPFd3uLax2k7nZAY1IEKj79TiG8dsKxr2EoyNB3tZ3b4XUhRxQ4K5RirqN -Pnbiucon8l+f725ZDQbYKxek0nxru18UGkiPGkzns0ccjkxFKyDuSN/n3QmOGKja -QI2SJhFTYXNd673nxE0pN2HrrDktZy4W1vUAg4WhzH92xH3kt0tm7wNFYGm2DFKW -koRepqO1pD4r2czYG0eq8kTaT/kD6PAUyz/zg97QwVTjt+gKN02LIFkDMBmhLMi9 -ER/frslKxfMnZmaGrGiR/9nmUxwPi1xpZQomyB40w11Re9epnAahNt3ViZS82eQt -DF4JbAiXfKM9fJP/P6EUp8+1Xevb2xzEdt+Iub1FBZUbrvxGakyvSOPOrg/Sfuvm -bJxPgWp6ZKy7PtXny3YuxadIwVyQD8vIP/rmMuGNG2+k5o7Y+SlIis5z/iw= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFbDCCA1SgAwIBAgIBATANBgkqhkiG9w0BAQUFADBHMQswCQYDVQQGEwJVUzEW -MBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1c3QgVW5pdmVy -c2FsIENBIDIwHhcNMDQwMzA0MDUwMDAwWhcNMjkwMzA0MDUwMDAwWjBHMQswCQYD -VQQGEwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEgMB4GA1UEAxMXR2VvVHJ1 -c3QgVW5pdmVyc2FsIENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC -AQCzVFLByT7y2dyxUxpZKeexw0Uo5dfR7cXFS6GqdHtXr0om/Nj1XqduGdt0DE81 -WzILAePb63p3NeqqWuDW6KFXlPCQo3RWlEQwAx5cTiuFJnSCegx2oG9NzkEtoBUG -FF+3Qs17j1hhNNwqCPkuwwGmIkQcTAeC5lvO0Ep8BNMZcyfwqph/Lq9O64ceJHdq -XbboW0W63MOhBW9Wjo8QJqVJwy7XQYci4E+GymC16qFjwAGXEHm9ADwSbSsVsaxL -se4YuU6W3Nx2/zu+z18DwPw76L5GG//aQMJS9/7jOvdqdzXQ2o3rXhhqMcceujwb -KNZrVMaqW9eiLBsZzKIC9ptZvTdrhrVtgrrY6slWvKk2WP0+GfPtDCapkzj4T8Fd -IgbQl+rhrcZV4IErKIM6+vR7IVEAvlI4zs1meaj0gVbi0IMJR1FbUGrP20gaXT73 -y/Zl92zxlfgCOzJWgjl6W70viRu/obTo/3+NjN8D8WBOWBFM66M/ECuDmgFz2ZRt -hAAnZqzwcEAJQpKtT5MNYQlRJNiS1QuUYbKHsu3/mjX/hVTK7URDrBs8FmtISgoc -QIgfksILAAX/8sgCSqSqqcyZlpwvWOB94b67B9xfBHJcMTTD7F8t4D1kkCLm0ey4 -Lt1ZrtmhN79UNdxzMk+MBB4zsslG8dhcyFVQyWi9qLo2CQIDAQABo2MwYTAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAfBgNV -HSMEGDAWgBR281Xh+qQ2+/CfXGJx7Tz0RzgQKzAOBgNVHQ8BAf8EBAMCAYYwDQYJ -KoZIhvcNAQEFBQADggIBAGbBxiPz2eAubl/oz66wsCVNK/g7WJtAJDday6sWSf+z -dXkzoS9tcBc0kf5nfo/sm+VegqlVHy/c1FEHEv6sFj4sNcZj/NwQ6w2jqtB8zNHQ -L1EuxBRa3ugZ4T7GzKQp5y6EqgYweHZUcyiYWTjgAA1i00J9IZ+uPTqM1fp3DRgr -Fg5fNuH8KrUwJM/gYwx7WBr+mbpCErGR9Hxo4sjoryzqyX6uuyo9DRXcNJW2GHSo -ag/HtPQTxORb7QrSpJdMKu0vbBKJPfEncKpqA1Ihn0CoZ1Dy81of398j9tx4TuaY -T1U6U+Pv8vSfx3zYWK8pIpe44L2RLrB27FcRz+8pRPPphXpgY+RdM4kX2TGq2tbz -GDVyz4crL2MjhF2EjD9XoIj8mZEoJmmZ1I+XRL6O1UixpCgp8RW04eWe3fiPpm8m -1wk8OhwRDqZsN/etRIcsKMfYdIKz0G9KV7s1KSegi+ghp4dkNl3M2Basx7InQJJV -OCiNUW7dFGdTbHFcJoRNdVq2fmBWqU2t+5sel/MN2dKXVHfaPRK34B7vCAas+YWH -6aLcr34YEoP9VhdBLtUpgn2Z9DH2canPLAEnpQW5qrJITirvn5NSUZU8UnOOVkwX -QMAJKOSLakhT2+zNVVXxxvjpoixMptEmX36vWkzaH6byHCx+rgIW0lbQL1dTR+iS ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIB4TCCAYegAwIBAgIRKjikHJYKBN5CsiilC+g0mAIwCgYIKoZIzj0EAwIwUDEk -MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI0MRMwEQYDVQQKEwpH -bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX -DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD -QSAtIFI0MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu -MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEuMZ5049sJQ6fLjkZHAOkrprlOQcJ -FspjsbmG+IpXwVfOQvpzofdlQv8ewQCybnMO/8ch5RikqtlxP6jUuc6MHaNCMEAw -DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFFSwe61F -uOJAf/sKbvu+M8k8o4TVMAoGCCqGSM49BAMCA0gAMEUCIQDckqGgE6bPA7DmxCGX -kPoUVy0D7O48027KqGx2vKLeuwIgJ6iFJzWbVsaj8kfSt24bAgAXqmemFZHe+pTs -ewv4n4Q= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICHjCCAaSgAwIBAgIRYFlJ4CYuu1X5CneKcflK2GwwCgYIKoZIzj0EAwMwUDEk -MCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBDQSAtIFI1MRMwEQYDVQQKEwpH -bG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWduMB4XDTEyMTExMzAwMDAwMFoX -DTM4MDExOTAzMTQwN1owUDEkMCIGA1UECxMbR2xvYmFsU2lnbiBFQ0MgUm9vdCBD -QSAtIFI1MRMwEQYDVQQKEwpHbG9iYWxTaWduMRMwEQYDVQQDEwpHbG9iYWxTaWdu -MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAER0UOlvt9Xb/pOdEh+J8LttV7HpI6SFkc -8GIxLcB6KP4ap1yztsyX50XUWPrRd21DosCHZTQKH3rd6zwzocWdTaRvQZU4f8ke -hOvRnkmSh5SHDDqFSmafnVmTTZdhBoZKo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYD -VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUPeYpSJvqB8ohREom3m7e0oPQn1kwCgYI -KoZIzj0EAwMDaAAwZQIxAOVpEslu28YxuglB4Zf4+/2a4n0Sye18ZNPLBSWLVtmg -515dTguDnFt2KaAJJiFqYgIwcdK1j1zqO+F4CYWodZI7yFz9SO8NdCKoCOJuxUnO -xwy8p2Fp8fc74SrL+SvzZpA3 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG -A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv -b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw -MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i -YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT -aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ -jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp -xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp -1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG -snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ -U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 -9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E -BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B -AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz -yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE -38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP -AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad -DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME -HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G -A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp -Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1 -MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG -A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL -v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8 -eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq -tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd -C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa -zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB -mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH -V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n -bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG -3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs -J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO -291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS -ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd -AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7 -TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDXzCCAkegAwIBAgILBAAAAAABIVhTCKIwDQYJKoZIhvcNAQELBQAwTDEgMB4G -A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjMxEzARBgNVBAoTCkdsb2JhbFNp -Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDkwMzE4MTAwMDAwWhcNMjkwMzE4 -MTAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMzETMBEG -A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAMwldpB5BngiFvXAg7aEyiie/QV2EcWtiHL8 -RgJDx7KKnQRfJMsuS+FggkbhUqsMgUdwbN1k0ev1LKMPgj0MK66X17YUhhB5uzsT -gHeMCOFJ0mpiLx9e+pZo34knlTifBtc+ycsmWQ1z3rDI6SYOgxXG71uL0gRgykmm -KPZpO/bLyCiR5Z2KYVc3rHQU3HTgOu5yLy6c+9C7v/U9AOEGM+iCK65TpjoWc4zd -QQ4gOsC0p6Hpsk+QLjJg6VfLuQSSaGjlOCZgdbKfd/+RFO+uIEn8rUAVSNECMWEZ -XriX7613t2Saer9fwRPvm2L7DWzgVGkWqQPabumDk3F2xmmFghcCAwEAAaNCMEAw -DgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFI/wS3+o -LkUkrk1Q+mOai97i3Ru8MA0GCSqGSIb3DQEBCwUAA4IBAQBLQNvAUKr+yAzv95ZU -RUm7lgAJQayzE4aGKAczymvmdLm6AC2upArT9fHxD4q/c2dKg8dEe3jgr25sbwMp -jjM5RcOO5LlXbKr8EpbsU8Yt5CRsuZRj+9xTaGdWPoO4zzUhw8lo/s7awlOqzJCK -6fBdRoyV3XpYKBovHd7NADdBj+1EbddTKJd+82cEHhXXipa0095MJ6RMG3NzdvQX -mcIfeg7jLQitChws/zyrVQ4PkX4268NXSb7hLi18YIvDQVETI53O9zJrlAGomecs -Mx86OyXShkDOOyyGeMlhLxS67ttVb9+E7gUJTb0o2HLO02JQZR7rkpeDMdmztcpH -WD9f ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFgzCCA2ugAwIBAgIORea7A4Mzw4VlSOb/RVEwDQYJKoZIhvcNAQEMBQAwTDEg -MB4GA1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjYxEzARBgNVBAoTCkdsb2Jh -bFNpZ24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMTQxMjEwMDAwMDAwWhcNMzQx -MjEwMDAwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSNjET -MBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCAiIwDQYJ -KoZIhvcNAQEBBQADggIPADCCAgoCggIBAJUH6HPKZvnsFMp7PPcNCPG0RQssgrRI -xutbPK6DuEGSMxSkb3/pKszGsIhrxbaJ0cay/xTOURQh7ErdG1rG1ofuTToVBu1k -ZguSgMpE3nOUTvOniX9PeGMIyBJQbUJmL025eShNUhqKGoC3GYEOfsSKvGRMIRxD -aNc9PIrFsmbVkJq3MQbFvuJtMgamHvm566qjuL++gmNQ0PAYid/kD3n16qIfKtJw -LnvnvJO7bVPiSHyMEAc4/2ayd2F+4OqMPKq0pPbzlUoSB239jLKJz9CgYXfIWHSw -1CM69106yqLbnQneXUQtkPGBzVeS+n68UARjNN9rkxi+azayOeSsJDa38O+2HBNX -k7besvjihbdzorg1qkXy4J02oW9UivFyVm4uiMVRQkQVlO6jxTiWm05OWgtH8wY2 -SXcwvHE35absIQh1/OZhFj931dmRl4QKbNQCTXTAFO39OfuD8l4UoQSwC+n+7o/h -bguyCLNhZglqsQY6ZZZZwPA1/cnaKI0aEYdwgQqomnUdnjqGBQCe24DWJfncBZ4n -WUx2OVvq+aWh2IMP0f/fMBH5hc8zSPXKbWQULHpYT9NLCEnFlWQaYw55PfWzjMpY -rZxCRXluDocZXFSxZba/jJvcE+kNb7gu3GduyYsRtYQUigAZcIN5kZeR1Bonvzce -MgfYFGM8KEyvAgMBAAGjYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTAD -AQH/MB0GA1UdDgQWBBSubAWjkxPioufi1xzWx/B/yGdToDAfBgNVHSMEGDAWgBSu -bAWjkxPioufi1xzWx/B/yGdToDANBgkqhkiG9w0BAQwFAAOCAgEAgyXt6NH9lVLN -nsAEoJFp5lzQhN7craJP6Ed41mWYqVuoPId8AorRbrcWc+ZfwFSY1XS+wc3iEZGt -Ixg93eFyRJa0lV7Ae46ZeBZDE1ZXs6KzO7V33EByrKPrmzU+sQghoefEQzd5Mr61 -55wsTLxDKZmOMNOsIeDjHfrYBzN2VAAiKrlNIC5waNrlU/yDXNOd8v9EDERm8tLj -vUYAGm0CuiVdjaExUd1URhxN25mW7xocBFymFe944Hn+Xds+qkxV/ZoVqW/hpvvf -cDDpw+5CRu3CkwWJ+n1jez/QcYF8AOiYrg54NMMl+68KnyBr3TsTjxKM4kEaSHpz -oHdpx7Zcf4LIHv5YGygrqGytXm3ABdJ7t+uA/iU3/gKbaKxCXcPu9czc8FB10jZp -nOZ7BN9uBmm23goJSFmH63sUYHpkqmlD75HHTOwY3WzvUy2MmeFe8nI+z1TIvWfs -pA9MRf/TuTAjB0yPEL+GltmZWrSZVxykzLsViVO6LAUP5MSeGbEYNNVMnbrt9x+v -JJUEeKgDu+6B5dpffItKoZB0JaezPkvILFa9x8jvOOJckvB595yEunQtYQEgfn7R -8k8HWV+LLUNS60YMlOH1Zkd5d9VUWx+tJDfLRVpOoERIyNiwmcUVhAn21klJwGW4 -5hpxbqCo8YLoRT5s1gLXCmeDBVrJpBA= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIHSTCCBTGgAwIBAgIJAMnN0+nVfSPOMA0GCSqGSIb3DQEBBQUAMIGsMQswCQYD -VQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3VycmVudCBhZGRyZXNzIGF0 -IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAGA1UEBRMJQTgyNzQzMjg3 -MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAlBgNVBAMTHkdsb2JhbCBD -aGFtYmVyc2lnbiBSb290IC0gMjAwODAeFw0wODA4MDExMjMxNDBaFw0zODA3MzEx -MjMxNDBaMIGsMQswCQYDVQQGEwJFVTFDMEEGA1UEBxM6TWFkcmlkIChzZWUgY3Vy -cmVudCBhZGRyZXNzIGF0IHd3dy5jYW1lcmZpcm1hLmNvbS9hZGRyZXNzKTESMBAG -A1UEBRMJQTgyNzQzMjg3MRswGQYDVQQKExJBQyBDYW1lcmZpcm1hIFMuQS4xJzAl -BgNVBAMTHkdsb2JhbCBDaGFtYmVyc2lnbiBSb290IC0gMjAwODCCAiIwDQYJKoZI -hvcNAQEBBQADggIPADCCAgoCggIBAMDfVtPkOpt2RbQT2//BthmLN0EYlVJH6xed -KYiONWwGMi5HYvNJBL99RDaxccy9Wglz1dmFRP+RVyXfXjaOcNFccUMd2drvXNL7 -G706tcuto8xEpw2uIRU/uXpbknXYpBI4iRmKt4DS4jJvVpyR1ogQC7N0ZJJ0YPP2 -zxhPYLIj0Mc7zmFLmY/CDNBAspjcDahOo7kKrmCgrUVSY7pmvWjg+b4aqIG7HkF4 -ddPB/gBVsIdU6CeQNR1MM62X/JcumIS/LMmjv9GYERTtY/jKmIhYF5ntRQOXfjyG -HoiMvvKRhI9lNNgATH23MRdaKXoKGCQwoze1eqkBfSbW+Q6OWfH9GzO1KTsXO0G2 -Id3UwD2ln58fQ1DJu7xsepeY7s2MH/ucUa6LcL0nn3HAa6x9kGbo1106DbDVwo3V -yJ2dwW3Q0L9R5OP4wzg2rtandeavhENdk5IMagfeOx2YItaswTXbo6Al/3K1dh3e -beksZixShNBFks4c5eUzHdwHU1SjqoI7mjcv3N2gZOnm3b2u/GSFHTynyQbehP9r -6GsaPMWis0L7iwk+XwhSx2LE1AVxv8Rk5Pihg+g+EpuoHtQ2TS9x9o0o9oOpE9Jh -wZG7SMA0j0GMS0zbaRL/UJScIINZc+18ofLx/d33SdNDWKBWY8o9PeU1VlnpDsog -zCtLkykPAgMBAAGjggFqMIIBZjASBgNVHRMBAf8ECDAGAQH/AgEMMB0GA1UdDgQW -BBS5CcqcHtvTbDprru1U8VuTBjUuXjCB4QYDVR0jBIHZMIHWgBS5CcqcHtvTbDpr -ru1U8VuTBjUuXqGBsqSBrzCBrDELMAkGA1UEBhMCRVUxQzBBBgNVBAcTOk1hZHJp -ZCAoc2VlIGN1cnJlbnQgYWRkcmVzcyBhdCB3d3cuY2FtZXJmaXJtYS5jb20vYWRk -cmVzcykxEjAQBgNVBAUTCUE4Mjc0MzI4NzEbMBkGA1UEChMSQUMgQ2FtZXJmaXJt -YSBTLkEuMScwJQYDVQQDEx5HbG9iYWwgQ2hhbWJlcnNpZ24gUm9vdCAtIDIwMDiC -CQDJzdPp1X0jzjAOBgNVHQ8BAf8EBAMCAQYwPQYDVR0gBDYwNDAyBgRVHSAAMCow -KAYIKwYBBQUHAgEWHGh0dHA6Ly9wb2xpY3kuY2FtZXJmaXJtYS5jb20wDQYJKoZI -hvcNAQEFBQADggIBAICIf3DekijZBZRG/5BXqfEv3xoNa/p8DhxJJHkn2EaqbylZ -UohwEurdPfWbU1Rv4WCiqAm57OtZfMY18dwY6fFn5a+6ReAJ3spED8IXDneRRXoz -X1+WLGiLwUePmJs9wOzL9dWCkoQ10b42OFZyMVtHLaoXpGNR6woBrX/sdZ7LoR/x -fxKxueRkf2fWIyr0uDldmOghp+G9PUIadJpwr2hsUF1Jz//7Dl3mLEfXgTpZALVz -a2Mg9jFFCDkO9HB+QHBaP9BrQql0PSgvAm11cpUJjUhjxsYjV5KTXjXBjfkK9yyd -Yhz2rXzdpjEetrHHfoUm+qRqtdpjMNHvkzeyZi99Bffnt0uYlDXA2TopwZ2yUDMd -SqlapskD7+3056huirRXhOukP9DuqqqHW2Pok+JrqNS4cnhrG+055F3Lm6qH1U9O -AP7Zap88MQ8oAgF9mOinsKJknnn4SPIVqczmyETrP3iZ8ntxPjzxmKfFGBI/5rso -M0LpRQp8bfKGeS/Fghl9CYl8slR2iK7ewfPM4W7bMdaTrpmg7yVqc5iJWzouE4ge -v8CSlDQb4ye3ix5vQv/n6TebUB0tovkC7stYWDpxvGjjqsGvHCgfotwjZT+B6q6Z -09gwzxMNTxXJhLynSC34MCN32EZLeW32jO06f2ARePTpm67VVMB0gNELQp/B ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEADCCAuigAwIBAgIBADANBgkqhkiG9w0BAQUFADBjMQswCQYDVQQGEwJVUzEh -MB8GA1UEChMYVGhlIEdvIERhZGR5IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBE -YWRkeSBDbGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTA0MDYyOTE3 -MDYyMFoXDTM0MDYyOTE3MDYyMFowYzELMAkGA1UEBhMCVVMxITAfBgNVBAoTGFRo -ZSBHbyBEYWRkeSBHcm91cCwgSW5jLjExMC8GA1UECxMoR28gRGFkZHkgQ2xhc3Mg -MiBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTCCASAwDQYJKoZIhvcNAQEBBQADggEN -ADCCAQgCggEBAN6d1+pXGEmhW+vXX0iG6r7d/+TvZxz0ZWizV3GgXne77ZtJ6XCA -PVYYYwhv2vLM0D9/AlQiVBDYsoHUwHU9S3/Hd8M+eKsaA7Ugay9qK7HFiH7Eux6w -wdhFJ2+qN1j3hybX2C32qRe3H3I2TqYXP2WYktsqbl2i/ojgC95/5Y0V4evLOtXi -EqITLdiOr18SPaAIBQi2XKVlOARFmR6jYGB0xUGlcmIbYsUfb18aQr4CUWWoriMY -avx4A6lNf4DD+qta/KFApMoZFv6yyO9ecw3ud72a9nmYvLEHZ6IVDd2gWMZEewo+ -YihfukEHU1jPEX44dMX4/7VpkI+EdOqXG68CAQOjgcAwgb0wHQYDVR0OBBYEFNLE -sNKR1EwRcbNhyz2h/t2oatTjMIGNBgNVHSMEgYUwgYKAFNLEsNKR1EwRcbNhyz2h -/t2oatTjoWekZTBjMQswCQYDVQQGEwJVUzEhMB8GA1UEChMYVGhlIEdvIERhZGR5 -IEdyb3VwLCBJbmMuMTEwLwYDVQQLEyhHbyBEYWRkeSBDbGFzcyAyIENlcnRpZmlj -YXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD -ggEBADJL87LKPpH8EsahB4yOd6AzBhRckB4Y9wimPQoZ+YeAEW5p5JYXMP80kWNy -OO7MHAGjHZQopDH2esRU1/blMVgDoszOYtuURXO1v0XJJLXVggKtI3lpjbi2Tc7P -TMozI+gciKqdi0FuFskg5YmezTvacPd+mSYgFFQlq25zheabIZ0KbIIOqPjCDPoQ -HmyW74cNxA9hi63ugyuV+I6ShHI56yDqg+2DzZduCLzrTia2cyvk0/ZM/iZx4mER -dEr/VxqHD3VILs9RaRegAhJhldXRQLIQTO7ErBBDpqWeCtWVYpoNz4iCxTIM5Cuf -ReYNnyicsbkqWletNw+vHX/bvZ8= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDxTCCAq2gAwIBAgIBADANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCVVMx -EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxGjAYBgNVBAoT -EUdvRGFkZHkuY29tLCBJbmMuMTEwLwYDVQQDEyhHbyBEYWRkeSBSb290IENlcnRp -ZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAwMFoXDTM3MTIzMTIz -NTk1OVowgYMxCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6b25hMRMwEQYDVQQH -EwpTY290dHNkYWxlMRowGAYDVQQKExFHb0RhZGR5LmNvbSwgSW5jLjExMC8GA1UE -AxMoR28gRGFkZHkgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIw -DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9xYgjx+lk09xvJGKP3gElY6SKD -E6bFIEMBO4Tx5oVJnyfq9oQbTqC023CYxzIBsQU+B07u9PpPL1kwIuerGVZr4oAH -/PMWdYA5UXvl+TW2dE6pjYIT5LY/qQOD+qK+ihVqf94Lw7YZFAXK6sOoBJQ7Rnwy -DfMAZiLIjWltNowRGLfTshxgtDj6AozO091GB94KPutdfMh8+7ArU6SSYmlRJQVh -GkSBjCypQ5Yj36w6gZoOKcUcqeldHraenjAKOc7xiID7S13MMuyFYkMlNAJWJwGR -tDtwKj9useiciAF9n9T521NtYJ2/LOdYq7hfRvzOxBsDPAnrSTFcaUaz4EcCAwEA -AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE -FDqahQcQZyi27/a9BUFuIMGU2g/eMA0GCSqGSIb3DQEBCwUAA4IBAQCZ21151fmX -WWcDYfF+OwYxdS2hII5PZYe096acvNjpL9DbWu7PdIxztDhC2gV7+AJ1uP2lsdeu -9tfeE8tTEH6KRtGX+rcuKxGrkLAngPnon1rpN5+r5N9ss4UXnT3ZJE95kTXWXwTr -gIOrmgIttRD02JDHBHNA7XIloKmf7J6raBKZV8aPEjoJpL1E/QYVN8Gb5DKj7Tjo -2GTzLH4U/ALqn83/B2gX2yKQOC16jdFU8WnjXzPKej17CuPKf1855eJ1usV2GDPO -LPAvTK33sefOT6jEm0pUBsV/fdUID+Ic/n4XuKxe9tQWskMJDE32p2u0mYRlynqI -4uJEvlz36hz1 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICwzCCAkqgAwIBAgIBADAKBggqhkjOPQQDAjCBqjELMAkGA1UEBhMCR1IxDzAN -BgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl -c2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxRDBCBgNVBAMTO0hl -bGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgRUNDIFJv -b3RDQSAyMDE1MB4XDTE1MDcwNzEwMzcxMloXDTQwMDYzMDEwMzcxMlowgaoxCzAJ -BgNVBAYTAkdSMQ8wDQYDVQQHEwZBdGhlbnMxRDBCBgNVBAoTO0hlbGxlbmljIEFj -YWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgQ2VydC4gQXV0aG9yaXR5 -MUQwQgYDVQQDEztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0 -dXRpb25zIEVDQyBSb290Q0EgMjAxNTB2MBAGByqGSM49AgEGBSuBBAAiA2IABJKg -QehLgoRc4vgxEZmGZE4JJS+dQS8KrjVPdJWyUWRrjWvmP3CV8AVER6ZyOFB2lQJa -jq4onvktTpnvLEhvTCUp6NFxW98dwXU3tNf6e3pCnGoKVlp8aQuqgAkkbH7BRqNC -MEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFLQi -C4KZJAEOnLvkDv2/+5cgk5kqMAoGCCqGSM49BAMCA2cAMGQCMGfOFmI4oqxiRaep -lSTAGiecMjvAwNW6qef4BENThe5SId6d9SWDPp5YSy/XZxMOIQIwBeF1Ad5o7Sof -TUwJCA3sS61kFyjndc5FZXIhF8siQQ6ME5g4mlRtm8rifOoCWCKR ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEMTCCAxmgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBlTELMAkGA1UEBhMCR1Ix -RDBCBgNVBAoTO0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 -dGlvbnMgQ2VydC4gQXV0aG9yaXR5MUAwPgYDVQQDEzdIZWxsZW5pYyBBY2FkZW1p -YyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIFJvb3RDQSAyMDExMB4XDTExMTIw -NjEzNDk1MloXDTMxMTIwMTEzNDk1MlowgZUxCzAJBgNVBAYTAkdSMUQwQgYDVQQK -EztIZWxsZW5pYyBBY2FkZW1pYyBhbmQgUmVzZWFyY2ggSW5zdGl0dXRpb25zIENl -cnQuIEF1dGhvcml0eTFAMD4GA1UEAxM3SGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl -c2VhcmNoIEluc3RpdHV0aW9ucyBSb290Q0EgMjAxMTCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBAKlTAOMupvaO+mDYLZU++CwqVE7NuYRhlFhPjz2L5EPz -dYmNUeTDN9KKiE15HrcS3UN4SoqS5tdI1Q+kOilENbgH9mgdVc04UfCMJDGFr4PJ -fel3r+0ae50X+bOdOFAPplp5kYCvN66m0zH7tSYJnTxa71HFK9+WXesyHgLacEns -bgzImjeN9/E2YEsmLIKe0HjzDQ9jpFEw4fkrJxIH2Oq9GGKYsFk3fb7u8yBRQlqD -75O6aRXxYp2fmTmCobd0LovUxQt7L/DICto9eQqakxylKHJzkUOap9FNhYS5qXSP -FEDH3N6sQWRstBmbAmNtJGSPRLIl6s5ddAxjMlyNh+UCAwEAAaOBiTCBhjAPBgNV -HRMBAf8EBTADAQH/MAsGA1UdDwQEAwIBBjAdBgNVHQ4EFgQUppFC/RNhSiOeCKQp -5dgTBCPuQSUwRwYDVR0eBEAwPqA8MAWCAy5ncjAFggMuZXUwBoIELmVkdTAGggQu -b3JnMAWBAy5ncjAFgQMuZXUwBoEELmVkdTAGgQQub3JnMA0GCSqGSIb3DQEBBQUA -A4IBAQAf73lB4XtuP7KMhjdCSk4cNx6NZrokgclPEg8hwAOXhiVtXdMiKahsog2p -6z0GW5k6x8zDmjR/qw7IThzh+uTczQ2+vyT+bOdrwg3IBp5OjWEopmr95fZi6hg8 -TqBTnbI6nOulnJEWtk2C4AwFSKls9cz4y51JtPACpf1wA+2KIaWuE4ZJwzNzvoc7 -dIsXRSZMFpGD/md9zU1jZ/rzAxKWeAaNsWftjj++n08C9bMJL/NMh98qy5V8Acys -Nnq/onN694/BtZqhFLKPM58N7yLcZnuEvUUXBj08yrl3NI/K6s8/MT7jiOOASSXI -l7WdmplNsDz4SgCbZN2fOUvRJ9e4 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGCzCCA/OgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBpjELMAkGA1UEBhMCR1Ix -DzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNhZGVtaWMgYW5k -IFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkxQDA+BgNVBAMT -N0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1dGlvbnMgUm9v -dENBIDIwMTUwHhcNMTUwNzA3MTAxMTIxWhcNNDAwNjMwMTAxMTIxWjCBpjELMAkG -A1UEBhMCR1IxDzANBgNVBAcTBkF0aGVuczFEMEIGA1UEChM7SGVsbGVuaWMgQWNh -ZGVtaWMgYW5kIFJlc2VhcmNoIEluc3RpdHV0aW9ucyBDZXJ0LiBBdXRob3JpdHkx -QDA+BgNVBAMTN0hlbGxlbmljIEFjYWRlbWljIGFuZCBSZXNlYXJjaCBJbnN0aXR1 -dGlvbnMgUm9vdENBIDIwMTUwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC -AQDC+Kk/G4n8PDwEXT2QNrCROnk8ZlrvbTkBSRq0t89/TSNTt5AA4xMqKKYx8ZEA -4yjsriFBzh/a/X0SWwGDD7mwX5nh8hKDgE0GPt+sr+ehiGsxr/CL0BgzuNtFajT0 -AoAkKAoCFZVedioNmToUW/bLy1O8E00BiDeUJRtCvCLYjqOWXjrZMts+6PAQZe10 -4S+nfK8nNLspfZu2zwnI5dMK/IhlZXQK3HMcXM1AsRzUtoSMTFDPaI6oWa7CJ06C -ojXdFPQf/7J31Ycvqm59JCfnxssm5uX+Zwdj2EUN3TpZZTlYepKZcj2chF6IIbjV -9Cz82XBST3i4vTwri5WY9bPRaM8gFH5MXF/ni+X1NYEZN9cRCLdmvtNKzoNXADrD -gfgXy5I2XdGj2HUb4Ysn6npIQf1FGQatJ5lOwXBH3bWfgVMS5bGMSF0xQxfjjMZ6 -Y5ZLKTBOhE5iGV48zpeQpX8B653g+IuJ3SWYPZK2fu/Z8VFRfS0myGlZYeCsargq -NhEEelC9MoS+L9xy1dcdFkfkR2YgP/SWxa+OAXqlD3pk9Q0Yh9muiNX6hME6wGko -LfINaFGq46V3xqSQDqE3izEjR8EJCOtu93ib14L8hCCZSRm2Ekax+0VVFqmjZayc -Bw/qa9wfLgZy7IaIEuQt218FL+TwA9MmM+eAws1CoRc0CwIDAQABo0IwQDAPBgNV -HRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUcRVnyMjJvXVd -ctA4GGqd83EkVAswDQYJKoZIhvcNAQELBQADggIBAHW7bVRLqhBYRjTyYtcWNl0I -XtVsyIe9tC5G8jH4fOpCtZMWVdyhDBKg2mF+D1hYc2Ryx+hFjtyp8iY/xnmMsVMI -M4GwVhO+5lFc2JsKT0ucVlMC6U/2DWDqTUJV6HwbISHTGzrMd/K4kPFox/la/vot -9L/J9UUbzjgQKjeKeaO04wlshYaT/4mWJ3iBj2fjRnRUjtkNaeJK9E10A/+yd+2V -Z5fkscWrv2oj6NSU4kQoYsRL4vDY4ilrGnB+JGGTe08DMiUNRSQrlrRGar9KC/ea -j8GsGsVn82800vpzY4zvFrCopEYq+OsS7HK07/grfoxSwIuEVPkvPuNVqNxmsdnh -X9izjFk0WaSrT2y7HxjbdavYy5LNlDhhDgcGH0tGEPEVvo2FXDtKK4F5D7Rpn0lQ -l033DlZdwJVqwjbDG2jJ9SrcR5q+ss7FJej6A7na+RZukYT1HCjI/CbM1xyQVqdf -bzoEvM14iQuODy+jqk+iGxI9FghAD/FGTNeqewjBCvVtJ94Cj8rDtSvK6evIIVM4 -pcw72Hc3MKJP2W/R8kCtQXoXxdZKNYm3QdV8hn9VTYNKpXMgwDqvkPGaJI7ZjnHK -e7iG2rKPmT4dEw0SEe7Uq/DpFXYC5ODfqiAeW2GFZECpkJcNrVPSWh2HagCXZWK0 -vm9qp/UsQu0yrbYhnr68 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDMDCCAhigAwIBAgICA+gwDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UEBhMCSEsx -FjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdrb25nIFBvc3Qg -Um9vdCBDQSAxMB4XDTAzMDUxNTA1MTMxNFoXDTIzMDUxNTA0NTIyOVowRzELMAkG -A1UEBhMCSEsxFjAUBgNVBAoTDUhvbmdrb25nIFBvc3QxIDAeBgNVBAMTF0hvbmdr -b25nIFBvc3QgUm9vdCBDQSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC -AQEArP84tulmAknjorThkPlAj3n54r15/gK97iSSHSL22oVyaf7XPwnU3ZG1ApzQ -jVrhVcNQhrkpJsLj2aDxaQMoIIBFIi1WpztUlVYiWR8o3x8gPW2iNr4joLFutbEn -PzlTCeqrauh0ssJlXI6/fMN4hM2eFvz1Lk8gKgifd/PFHsSaUmYeSF7jEAaPIpjh -ZY4bXSNmO7ilMlHIhqqhqZ5/dpTCpmy3QfDVyAY45tQM4vM7TG1QjMSDJ8EThFk9 -nnV0ttgCXjqQesBCNnLsak3c78QA3xMYV18meMjWCnl3v/evt3a5pQuEF10Q6m/h -q5URX208o1xNg1vysxmKgIsLhwIDAQABoyYwJDASBgNVHRMBAf8ECDAGAQH/AgED -MA4GA1UdDwEB/wQEAwIBxjANBgkqhkiG9w0BAQUFAAOCAQEADkbVPK7ih9legYsC -mEEIjEy82tvuJxuC52pF7BaLT4Wg87JwvVqWuspube5Gi27nKi6Wsxkz67SfqLI3 -7piol7Yutmcn1KZJ/RyTZXaeQi/cImyaT/JaFTmxcdcrUehtHJjA2Sr0oYJ71clB -oiMBdDhViw+5LmeiIAQ32pwL0xch4I+XeTRvhEgCIDMb5jREn5Fw9IBehEPCKdJs -EhTkYY2sEJCehFC78JZvRZ+K88psT/oROhUVRsPNH4NbLUES7VBnQRM9IauUiqpO -fMGx+6fWtScvl6tu4B3i0RwsH0Ti/L6RoZz71ilTc4afU9hDDl3WY4JxHYB0yvbi -AmvZWg== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw -TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh -cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4 -WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu -ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY -MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc -h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+ -0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U -A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW -T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH -B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC -B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv -KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn -OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn -jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw -qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI -rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq -hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL -ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ -3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK -NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5 -ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur -TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC -jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc -oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq -4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA -mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d -emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFYDCCA0igAwIBAgIQCgFCgAAAAUUjyES1AAAAAjANBgkqhkiG9w0BAQsFADBK -MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScwJQYDVQQDEx5JZGVu -VHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwHhcNMTQwMTE2MTgxMjIzWhcNMzQw -MTE2MTgxMjIzWjBKMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MScw -JQYDVQQDEx5JZGVuVHJ1c3QgQ29tbWVyY2lhbCBSb290IENBIDEwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQCnUBneP5k91DNG8W9RYYKyqU+PZ4ldhNlT -3Qwo2dfw/66VQ3KZ+bVdfIrBQuExUHTRgQ18zZshq0PirK1ehm7zCYofWjK9ouuU -+ehcCuz/mNKvcbO0U59Oh++SvL3sTzIwiEsXXlfEU8L2ApeN2WIrvyQfYo3fw7gp -S0l4PJNgiCL8mdo2yMKi1CxUAGc1bnO/AljwpN3lsKImesrgNqUZFvX9t++uP0D1 -bVoE/c40yiTcdCMbXTMTEl3EASX2MN0CXZ/g1Ue9tOsbobtJSdifWwLziuQkkORi -T0/Br4sOdBeo0XKIanoBScy0RnnGF7HamB4HWfp1IYVl3ZBWzvurpWCdxJ35UrCL -vYf5jysjCiN2O/cz4ckA82n5S6LgTrx+kzmEB/dEcH7+B1rlsazRGMzyNeVJSQjK -Vsk9+w8YfYs7wRPCTY/JTw436R+hDmrfYi7LNQZReSzIJTj0+kuniVyc0uMNOYZK -dHzVWYfCP04MXFL0PfdSgvHqo6z9STQaKPNBiDoT7uje/5kdX7rL6B7yuVBgwDHT -c+XvvqDtMwt0viAgxGds8AgDelWAf0ZOlqf0Hj7h9tgJ4TNkK2PXMl6f+cB7D3hv -l7yTmvmcEpB4eoCHFddydJxVdHixuuFucAS6T6C6aMN7/zHwcz09lCqxC0EOoP5N -iGVreTO01wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB -/zAdBgNVHQ4EFgQU7UQZwNPwBovupHu+QucmVMiONnYwDQYJKoZIhvcNAQELBQAD -ggIBAA2ukDL2pkt8RHYZYR4nKM1eVO8lvOMIkPkp165oCOGUAFjvLi5+U1KMtlwH -6oi6mYtQlNeCgN9hCQCTrQ0U5s7B8jeUeLBfnLOic7iPBZM4zY0+sLj7wM+x8uwt -LRvM7Kqas6pgghstO8OEPVeKlh6cdbjTMM1gCIOQ045U8U1mwF10A0Cj7oV+wh93 -nAbowacYXVKV7cndJZ5t+qntozo00Fl72u1Q8zW/7esUTTHHYPTa8Yec4kjixsU3 -+wYQ+nVZZjFHKdp2mhzpgq7vmrlR94gjmmmVYjzlVYA211QC//G5Xc7UI2/YRYRK -W2XviQzdFKcgyxilJbQN+QHwotL0AMh0jqEqSI5l2xPE4iUXfeu+h1sXIFRRk0pT -AwvsXcoz7WL9RccvW9xYoIA55vrX/hMUpu09lEpCdNTDd1lzzY9GvlU47/rokTLq -l1gEIt44w8y8bckzOmoKaT+gyOpyj4xjhiO9bTyWnpXgSUyqorkqG5w2gXjtw+hG -4iZZRHUe2XWJUc0QhJ1hYMtd+ZciTY6Y5uN/9lu7rs3KSoFrXgvzUeF0K+l+J6fZ -mUlO+KWA2yUPHGNiiskzZ2s8EIPGrd6ozRaOjfAHN3Gf8qv8QfXBi+wAN10J5U6A -7/qxXDgGpRtK4dw4LTzcqx+QGtVKnO7RcGzM7vRX+Bi6hG6H ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFZjCCA06gAwIBAgIQCgFCgAAAAUUjz0Z8AAAAAjANBgkqhkiG9w0BAQsFADBN -MQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0MSowKAYDVQQDEyFJZGVu -VHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwHhcNMTQwMTE2MTc1MzMyWhcN -MzQwMTE2MTc1MzMyWjBNMQswCQYDVQQGEwJVUzESMBAGA1UEChMJSWRlblRydXN0 -MSowKAYDVQQDEyFJZGVuVHJ1c3QgUHVibGljIFNlY3RvciBSb290IENBIDEwggIi -MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC2IpT8pEiv6EdrCvsnduTyP4o7 -ekosMSqMjbCpwzFrqHd2hCa2rIFCDQjrVVi7evi8ZX3yoG2LqEfpYnYeEe4IFNGy -RBb06tD6Hi9e28tzQa68ALBKK0CyrOE7S8ItneShm+waOh7wCLPQ5CQ1B5+ctMlS -bdsHyo+1W/CD80/HLaXIrcuVIKQxKFdYWuSNG5qrng0M8gozOSI5Cpcu81N3uURF -/YTLNiCBWS2ab21ISGHKTN9T0a9SvESfqy9rg3LvdYDaBjMbXcjaY8ZNzaxmMc3R -3j6HEDbhuaR672BQssvKplbgN6+rNBM5Jeg5ZuSYeqoSmJxZZoY+rfGwyj4GD3vw -EUs3oERte8uojHH01bWRNszwFcYr3lEXsZdMUD2xlVl8BX0tIdUAvwFnol57plzy -9yLxkA2T26pEUWbMfXYD62qoKjgZl3YNa4ph+bz27nb9cCvdKTz4Ch5bQhyLVi9V -GxyhLrXHFub4qjySjmm2AcG1hp2JDws4lFTo6tyePSW8Uybt1as5qsVATFSrsrTZ -2fjXctscvG29ZV/viDUqZi/u9rNl8DONfJhBaUYPQxxp+pu10GFqzcpL2UyQRqsV -WaFHVCkugyhfHMKiq3IXAAaOReyL4jM9f9oZRORicsPfIsbyVtTdX5Vy7W1f90gD -W/3FKqD2cyOEEBsB5wIDAQABo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/ -BAUwAwEB/zAdBgNVHQ4EFgQU43HgntinQtnbcZFrlJPrw6PRFKMwDQYJKoZIhvcN -AQELBQADggIBAEf63QqwEZE4rU1d9+UOl1QZgkiHVIyqZJnYWv6IAcVYpZmxI1Qj -t2odIFflAWJBF9MJ23XLblSQdf4an4EKwt3X9wnQW3IV5B4Jaj0z8yGa5hV+rVHV -DRDtfULAj+7AmgjVQdZcDiFpboBhDhXAuM/FSRJSzL46zNQuOAXeNf0fb7iAaJg9 -TaDKQGXSc3z1i9kKlT/YPyNtGtEqJBnZhbMX73huqVjRI9PHE+1yJX9dsXNw0H8G -lwmEKYBhHfpe/3OsoOOJuBxxFcbeMX8S3OFtm6/n6J91eEyrRjuazr8FGF1NFTwW -mhlQBJqymm9li1JfPFgEKCXAZmExfrngdbkaqIHWchezxQMxNRF4eKLg6TCMf4Df -WN88uieW4oA0beOY02QnrEh+KHdcxiVhJfiFDGX6xDIvpZgF5PgLZxYWxoK4Mhn5 -+bl53B/N66+rDt0b20XkeucC4pVd/GnwU2lhlXV5C15V5jgclKlZM57IcXR5f1GJ -tshquDDIajjDbp7hNxbqBWJMWxJH7ae0s1hWx0nzfxJoCTFx8G34Tkf71oXuxVhA -GaQdp/lLQzfcaFpPz+vCZHTetBXZ9FRUGi8c15dxVJCO2SCdUyt/q4/i6jC8UDfv -8Ue1fXwsBOxonbRJRBD0ckscZOf85muQ3Wl9af0AVqW3rLatt8o+Ae+c ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4 -MQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6 -ZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD -VQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j -b20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq -scIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO -xdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H -LmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX -uaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD -yCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+ -JrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q -rLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN -BjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L -hij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB -QFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+ -HMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu -Zm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg -QTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB -BgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx -MCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA -A4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb -laQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56 -awmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo -JNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw -LDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT -VyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk -LhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb -UjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/ -QnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+ -naM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls -QyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFwzCCA6ugAwIBAgIUCn6m30tEntpqJIWe5rgV0xZ/u7EwDQYJKoZIhvcNAQEL -BQAwRjELMAkGA1UEBhMCTFUxFjAUBgNVBAoMDUx1eFRydXN0IFMuQS4xHzAdBgNV -BAMMFkx1eFRydXN0IEdsb2JhbCBSb290IDIwHhcNMTUwMzA1MTMyMTU3WhcNMzUw -MzA1MTMyMTU3WjBGMQswCQYDVQQGEwJMVTEWMBQGA1UECgwNTHV4VHJ1c3QgUy5B -LjEfMB0GA1UEAwwWTHV4VHJ1c3QgR2xvYmFsIFJvb3QgMjCCAiIwDQYJKoZIhvcN -AQEBBQADggIPADCCAgoCggIBANeFl78RmOnwYoNMPIf5U2o3C/IPPIfOb9wmKb3F -ibrJgz337spbxm1Jc7TJRqMbNBM/wYlFV/TZsfs2ZUv7COJIcRHIbjuend+JZTem -hfY7RBi2xjcwYkSSl2l9QjAk5A0MiWtj3sXh306pFGxT4GHO9hcvHTy95iJMHZP1 -EMShduxq3sVs35a0VkBCwGKSMKEtFZSg0iAGCW5qbeXrt77U8PEVfIvmTroTzEsn -Xpk8F12PgX8zPU/TPxvsXD/wPEx1bvKm1Z3aLQdjAsZy6ZS8TEmVT4hSyNvoaYL4 -zDRbIvCGp4m9SAptZoFtyMhk+wHh9OHe2Z7d21vUKpkmFRseTJIpgp7VkoGSQXAZ -96Tlk0u8d2cx3Rz9MXANF5kM+Qw5GSoXtTBxVdUPrljhPS80m8+f9niFwpN6cj5m -j5wWEWCPnolvZ77gR1o7DJpni89Gxq44o/KnvObWhWszJHAiS8sIm7vI+AIpHb4g -DEa/a4ebsypmQjVGbKq6rfmYe+lQVRQxv7HaLe2ArWgk+2mr2HETMOZns4dA/Yl+ -8kPREd8vZS9kzl8UubG/Mb2HeFpZZYiq/FkySIbWTLkpS5XTdvN3JW1CHDiDTf2j -X5t/Lax5Gw5CMZdjpPuKadUiDTSQMC6otOBttpSsvItO13D8xTiOZCXhTTmQzsmH -hFhxAgMBAAGjgagwgaUwDwYDVR0TAQH/BAUwAwEB/zBCBgNVHSAEOzA5MDcGByuB -KwEBAQowLDAqBggrBgEFBQcCARYeaHR0cHM6Ly9yZXBvc2l0b3J5Lmx1eHRydXN0 -Lmx1MA4GA1UdDwEB/wQEAwIBBjAfBgNVHSMEGDAWgBT/GCh2+UgFLKGu8SsbK7JT -+Et8szAdBgNVHQ4EFgQU/xgodvlIBSyhrvErGyuyU/hLfLMwDQYJKoZIhvcNAQEL -BQADggIBAGoZFO1uecEsh9QNcH7X9njJCwROxLHOk3D+sFTAMs2ZMGQXvw/l4jP9 -BzZAcg4atmpZ1gDlaCDdLnINH2pkMSCEfUmmWjfrRcmF9dTHF5kH5ptV5AzoqbTO -jFu1EVzPig4N1qx3gf4ynCSecs5U89BvolbW7MM3LGVYvlcAGvI1+ut7MV3CwRI9 -loGIlonBWVx65n9wNOeD4rHh4bhY79SV5GCc8JaXcozrhAIuZY+kt9J/Z93I055c -qqmkoCUUBpvsT34tC38ddfEz2O3OuHVtPlu5mB0xDVbYQw8wkbIEa91WvpWAVWe+ -2M2D2RjuLg+GLZKecBPs3lHJQ3gCpU3I+V/EkVhGFndadKpAvAefMLmx9xIX3eP/ -JEAdemrRTxgKqpAd60Ae36EeRJIQmvKN4dFLRp7oRUKX6kWZ8+xm1QL68qZKJKre -zrnK+T+Tb/mjuuqlPpmt/f97mfVl7vBZKGfXkJWkE4SphMHozs51k2MavDzq1WQf -LSoSOcbDWjLtR5EWDrw4wVDej8oqkDQc7kGUnF4ZLvhFSZl0kbAEb+MEWrGrKqv+ -x9CWttrhSmQGbmBNvUJO/3jaJMobtNeWOWyu8Q6qp31IiyBMz2TWuJdGsE7RKlY6 -oJO9r4Ak4Ap+58rVyuiFVdw2KuGUaJPHZnJED4AhMmwlxyOAgwrr ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIECjCCAvKgAwIBAgIJAMJ+QwRORz8ZMA0GCSqGSIb3DQEBCwUAMIGCMQswCQYD -VQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFjAUBgNVBAoMDU1pY3Jvc2VjIEx0 -ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3ppZ25vIFJvb3QgQ0EgMjAwOTEfMB0G -CSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5odTAeFw0wOTA2MTYxMTMwMThaFw0y -OTEyMzAxMTMwMThaMIGCMQswCQYDVQQGEwJIVTERMA8GA1UEBwwIQnVkYXBlc3Qx -FjAUBgNVBAoMDU1pY3Jvc2VjIEx0ZC4xJzAlBgNVBAMMHk1pY3Jvc2VjIGUtU3pp -Z25vIFJvb3QgQ0EgMjAwOTEfMB0GCSqGSIb3DQEJARYQaW5mb0BlLXN6aWduby5o -dTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOn4j/NjrdqG2KfgQvvP -kd6mJviZpWNwrZuuyjNAfW2WbqEORO7hE52UQlKavXWFdCyoDh2Tthi3jCyoz/tc -cbna7P7ofo/kLx2yqHWH2Leh5TvPmUpG0IMZfcChEhyVbUr02MelTTMuhTlAdX4U -fIASmFDHQWe4oIBhVKZsTh/gnQ4H6cm6M+f+wFUoLAKApxn1ntxVUwOXewdI/5n7 -N4okxFnMUBBjjqqpGrCEGob5X7uxUG6k0QrM1XF+H6cbfPVTbiJfyyvm1HxdrtbC -xkzlBQHZ7Vf8wSN5/PrIJIOV87VqUQHQd9bpEqH5GoP7ghu5sJf0dgYzQ0mg/wu1 -+rUCAwEAAaOBgDB+MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G -A1UdDgQWBBTLD8bfQkPMPcu1SCOhGnqmKrs0aDAfBgNVHSMEGDAWgBTLD8bfQkPM -Pcu1SCOhGnqmKrs0aDAbBgNVHREEFDASgRBpbmZvQGUtc3ppZ25vLmh1MA0GCSqG -SIb3DQEBCwUAA4IBAQDJ0Q5eLtXMs3w+y/w9/w0olZMEyL/azXm4Q5DwpL7v8u8h -mLzU1F0G9u5C7DBsoKqpyvGvivo/C3NqPuouQH4frlRheesuCDfXI/OMn74dseGk -ddug4lQUsbocKaQY9hK6ohQU4zE1yED/t+AFdlfBHFny+L/k7SViXITwfn4fs775 -tyERzAMBVnCnEJIeGzSBHq2cGsMEPO0CYdYeBvNfOofyK/FFh+U9rNHHV4S9a67c -2Pm2G2JwCz02yULyMtd6YebS2z3PyKnJm9zbWETXbzivf3jTo60adbocwTZ8jx5t -HMN1Rq41Bab2XD0h7lbwyYIiLXpUq3DDfSJlgnCW ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEFTCCAv2gAwIBAgIGSUEs5AAQMA0GCSqGSIb3DQEBCwUAMIGnMQswCQYDVQQG -EwJIVTERMA8GA1UEBwwIQnVkYXBlc3QxFTATBgNVBAoMDE5ldExvY2sgS2Z0LjE3 -MDUGA1UECwwuVGFuw7pzw610dsOhbnlraWFkw7NrIChDZXJ0aWZpY2F0aW9uIFNl -cnZpY2VzKTE1MDMGA1UEAwwsTmV0TG9jayBBcmFueSAoQ2xhc3MgR29sZCkgRsWR -dGFuw7pzw610dsOhbnkwHhcNMDgxMjExMTUwODIxWhcNMjgxMjA2MTUwODIxWjCB -pzELMAkGA1UEBhMCSFUxETAPBgNVBAcMCEJ1ZGFwZXN0MRUwEwYDVQQKDAxOZXRM -b2NrIEtmdC4xNzA1BgNVBAsMLlRhbsO6c8OtdHbDoW55a2lhZMOzayAoQ2VydGlm -aWNhdGlvbiBTZXJ2aWNlcykxNTAzBgNVBAMMLE5ldExvY2sgQXJhbnkgKENsYXNz -IEdvbGQpIEbFkXRhbsO6c8OtdHbDoW55MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAxCRec75LbRTDofTjl5Bu0jBFHjzuZ9lk4BqKf8owyoPjIMHj9DrT -lF8afFttvzBPhCf2nx9JvMaZCpDyD/V/Q4Q3Y1GLeqVw/HpYzY6b7cNGbIRwXdrz -AZAj/E4wqX7hJ2Pn7WQ8oLjJM2P+FpD/sLj916jAwJRDC7bVWaaeVtAkH3B5r9s5 -VA1lddkVQZQBr17s9o3x/61k/iCa11zr/qYfCGSji3ZVrR47KGAuhyXoqq8fxmRG -ILdwfzzeSNuWU7c5d+Qa4scWhHaXWy+7GRWF+GmF9ZmnqfI0p6m2pgP8b4Y9VHx2 -BJtr+UBdADTHLpl1neWIA6pN+APSQnbAGwIDAKiLo0UwQzASBgNVHRMBAf8ECDAG -AQH/AgEEMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUzPpnk/C2uNClwB7zU/2M -U9+D15YwDQYJKoZIhvcNAQELBQADggEBAKt/7hwWqZw8UQCgwBEIBaeZ5m8BiFRh -bvG5GK1Krf6BQCOUL/t1fC8oS2IkgYIL9WHxHG64YTjrgfpioTtaYtOUZcTh5m2C -+C8lcLIhJsFyUR+MLMOEkMNaj7rP9KdlpeuY0fsFskZ1FSNqb4VjMIDw1Z4fKRzC -bLBQWV2QWzuoDTDPv31/zvGdg73JRm4gpvlhUbohL3u+pRVjodSVh/GeufOJ8z2F -uLjbvrW5KfnaNwUASZQDhETnv0Mxz3WLJdH0pmT1kvarBes96aULNmLazAZfNou2 -XjG4Kvte9nHfRCaexOYNkbQudZWAUWpLMKawYqGT8ZvYzsRjdT9ZR7E= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID5jCCAs6gAwIBAgIQV8szb8JcFuZHFhfjkDFo4DANBgkqhkiG9w0BAQUFADBi -MQswCQYDVQQGEwJVUzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMu -MTAwLgYDVQQDEydOZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3Jp -dHkwHhcNMDYxMjAxMDAwMDAwWhcNMjkxMjMxMjM1OTU5WjBiMQswCQYDVQQGEwJV -UzEhMB8GA1UEChMYTmV0d29yayBTb2x1dGlvbnMgTC5MLkMuMTAwLgYDVQQDEydO -ZXR3b3JrIFNvbHV0aW9ucyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDkvH6SMG3G2I4rC7xGzuAnlt7e+foS0zwz -c7MEL7xxjOWftiJgPl9dzgn/ggwbmlFQGiaJ3dVhXRncEg8tCqJDXRfQNJIg6nPP -OCwGJgl6cvf6UDL4wpPTaaIjzkGxzOTVHzbRijr4jGPiFFlp7Q3Tf2vouAPlT2rl -mGNpSAW+Lv8ztumXWWn4Zxmuk2GWRBXTcrA/vGp97Eh/jcOrqnErU2lBUzS1sLnF -BgrEsEX1QV1uiUV7PTsmjHTC5dLRfbIR1PtYMiKagMnc/Qzpf14Dl847ABSHJ3A4 -qY5usyd2mFHgBeMhqxrVhSI8KbWaFsWAqPS7azCPL0YCorEMIuDTAgMBAAGjgZcw -gZQwHQYDVR0OBBYEFCEwyfsA106Y2oeqKtCnLrFAMadMMA4GA1UdDwEB/wQEAwIB -BjAPBgNVHRMBAf8EBTADAQH/MFIGA1UdHwRLMEkwR6BFoEOGQWh0dHA6Ly9jcmwu -bmV0c29sc3NsLmNvbS9OZXR3b3JrU29sdXRpb25zQ2VydGlmaWNhdGVBdXRob3Jp -dHkuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQC7rkvnt1frf6ott3NHhWrB5KUd5Oc8 -6fRZZXe1eltajSU24HqXLjjAV2CDmAaDn7l2em5Q4LqILPxFzBiwmZVRDuwduIj/ -h1AcgsLj4DKAv6ALR8jDMe+ZZzKATxcheQxpXN5eNK4CtSbqUN9/GGUsyfJj4akH -/nxxH2szJGoeBfcFaMBqEssuXmHLrijTfsK0ZpEmXzwuJF/LWA/rKOyvEZbz3Htv -wKeI8lN3s2Berq4o2jUsbzRF0ybh3uxbTydrFny9RAQYgrOJeRcQcT16ohZO9QHN -pGxlaKFJdlxDydi8NmdspZS11My5vWo1ViHe2MPr+8ukYEywVaCge1ey ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID8TCCAtmgAwIBAgIQQT1yx/RrH4FDffHSKFTfmjANBgkqhkiG9w0BAQUFADCB -ijELMAkGA1UEBhMCQ0gxEDAOBgNVBAoTB1dJU2VLZXkxGzAZBgNVBAsTEkNvcHly -aWdodCAoYykgMjAwNTEiMCAGA1UECxMZT0lTVEUgRm91bmRhdGlvbiBFbmRvcnNl -ZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwgUm9vdCBHQSBDQTAeFw0w -NTEyMTExNjAzNDRaFw0zNzEyMTExNjA5NTFaMIGKMQswCQYDVQQGEwJDSDEQMA4G -A1UEChMHV0lTZUtleTEbMBkGA1UECxMSQ29weXJpZ2h0IChjKSAyMDA1MSIwIAYD -VQQLExlPSVNURSBGb3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBX -SVNlS2V5IEdsb2JhbCBSb290IEdBIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A -MIIBCgKCAQEAy0+zAJs9Nt350UlqaxBJH+zYK7LG+DKBKUOVTJoZIyEVRd7jyBxR -VVuuk+g3/ytr6dTqvirdqFEr12bDYVxgAsj1znJ7O7jyTmUIms2kahnBAbtzptf2 -w93NvKSLtZlhuAGio9RN1AU9ka34tAhxZK9w8RxrfvbDd50kc3vkDIzh2TbhmYsF -mQvtRTEJysIA2/dyoJaqlYfQjse2YXMNdmaM3Bu0Y6Kff5MTMPGhJ9vZ/yxViJGg -4E8HsChWjBgbl0SOid3gF27nKu+POQoxhILYQBRJLnpB5Kf+42TMwVlxSywhp1t9 -4B3RLoGbw9ho972WG6xwsRYUC9tguSYBBQIDAQABo1EwTzALBgNVHQ8EBAMCAYYw -DwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUswN+rja8sHnR3JQmthG+IbJphpQw -EAYJKwYBBAGCNxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBAEuh/wuHbrP5wUOx -SPMowB0uyQlB+pQAHKSkq0lPjz0e701vvbyk9vImMMkQyh2I+3QZH4VFvbBsUfk2 -ftv1TDI6QU9bR8/oCy22xBmddMVHxjtqD6wU2zz0c5ypBd8A3HR4+vg1YFkCExh8 -vPtNsCBtQ7tgMHpnM1zFmdH4LTlSc/uMqpclXHLZCB6rTjzjgTGfA6b7wP4piFXa -hNVQA7bihKOmNqoROgHhGEvWRGizPflTdISzRpFGlgC3gCy24eMQ4tui5yiPAZZi -Fj4A4xylNoEYokxSdsARo27mHbrjWr42U8U+dY+GaSlYU7Wcu2+fXMUY7N0v4ZjJ -/L7fCg0= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDtTCCAp2gAwIBAgIQdrEgUnTwhYdGs/gjGvbCwDANBgkqhkiG9w0BAQsFADBt -MQswCQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUg -Rm91bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9i -YWwgUm9vdCBHQiBDQTAeFw0xNDEyMDExNTAwMzJaFw0zOTEyMDExNTEwMzFaMG0x -CzAJBgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBG -b3VuZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2Jh -bCBSb290IEdCIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2Be3 -HEokKtaXscriHvt9OO+Y9bI5mE4nuBFde9IllIiCFSZqGzG7qFshISvYD06fWvGx -WuR51jIjK+FTzJlFXHtPrby/h0oLS5daqPZI7H17Dc0hBt+eFf1Biki3IPShehtX -1F1Q/7pn2COZH8g/497/b1t3sWtuuMlk9+HKQUYOKXHQuSP8yYFfTvdv37+ErXNk -u7dCjmn21HYdfp2nuFeKUWdy19SouJVUQHMD9ur06/4oQnc/nSMbsrY9gBQHTC5P -99UKFg29ZkM3fiNDecNAhvVMKdqOmq0NpQSHiB6F4+lT1ZvIiwNjeOvgGUpuuy9r -M2RYk61pv48b74JIxwIDAQABo1EwTzALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUw -AwEB/zAdBgNVHQ4EFgQUNQ/INmNe4qPs+TtmFc5RUuORmj0wEAYJKwYBBAGCNxUB -BAMCAQAwDQYJKoZIhvcNAQELBQADggEBAEBM+4eymYGQfp3FsLAmzYh7KzKNbrgh -cViXfa43FK8+5/ea4n32cZiZBKpDdHij40lhPnOMTZTg+XHEthYOU3gf1qKHLwI5 -gSk8rxWYITD+KJAAjNHhy/peyP34EEY7onhCkRd0VQreUGdNZtGn//3ZwLWoo4rO -ZvUPQ82nK1d7Y0Zqqi5S2PTt4W2tKZB4SLrhI6qjiey1q5bAtEuiHZeeevJuQHHf -aPFlTc58Bd9TZaml8LGXBHAVRgOY1NK/VLSgWH1Sb9pWJmLU2NuJMW8c8CLC02Ic -Nc1MaRVUGpCY3useX8p3x8uOPUNpnJpY0CQ73xtAln41rYHHTnG6iBM= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICaTCCAe+gAwIBAgIQISpWDK7aDKtARb8roi066jAKBggqhkjOPQQDAzBtMQsw -CQYDVQQGEwJDSDEQMA4GA1UEChMHV0lTZUtleTEiMCAGA1UECxMZT0lTVEUgRm91 -bmRhdGlvbiBFbmRvcnNlZDEoMCYGA1UEAxMfT0lTVEUgV0lTZUtleSBHbG9iYWwg -Um9vdCBHQyBDQTAeFw0xNzA1MDkwOTQ4MzRaFw00MjA1MDkwOTU4MzNaMG0xCzAJ -BgNVBAYTAkNIMRAwDgYDVQQKEwdXSVNlS2V5MSIwIAYDVQQLExlPSVNURSBGb3Vu -ZGF0aW9uIEVuZG9yc2VkMSgwJgYDVQQDEx9PSVNURSBXSVNlS2V5IEdsb2JhbCBS -b290IEdDIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAETOlQwMYPchi82PG6s4ni -eUqjFqdrVCTbUf/q9Akkwwsin8tqJ4KBDdLArzHkdIJuyiXZjHWd8dvQmqJLIX4W -p2OQ0jnUsYd4XxiWD1AbNTcPasbc2RNNpI6QN+a9WzGRo1QwUjAOBgNVHQ8BAf8E -BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUSIcUrOPDnpBgOtfKie7T -rYy0UGYwEAYJKwYBBAGCNxUBBAMCAQAwCgYIKoZIzj0EAwMDaAAwZQIwJsdpW9zV -57LnyAyMjMPdeYwbY9XJUpROTYJKcx6ygISpJcBMWm1JKWB4E+J+SOtkAjEA2zQg -Mgj/mkkCtojeFK9dbJlxjRo/i9fgojaGHAeCOnZT/cKi7e97sIBPWA9LUzm9 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIF0DCCBLigAwIBAgIEOrZQizANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJC -TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDElMCMGA1UECxMcUm9vdCBDZXJ0 -aWZpY2F0aW9uIEF1dGhvcml0eTEuMCwGA1UEAxMlUXVvVmFkaXMgUm9vdCBDZXJ0 -aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wMTAzMTkxODMzMzNaFw0yMTAzMTcxODMz -MzNaMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMSUw -IwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYDVQQDEyVR -dW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG -9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2G1lVO6V/z68mcLOhrfEYBklbTRvM16z/Yp -li4kVEAkOPcahdxYTMukJ0KX0J+DisPkBgNbAKVRHnAEdOLB1Dqr1607BxgFjv2D -rOpm2RgbaIr1VxqYuvXtdj182d6UajtLF8HVj71lODqV0D1VNk7feVcxKh7YWWVJ -WCCYfqtffp/p1k3sg3Spx2zY7ilKhSoGFPlU5tPaZQeLYzcS19Dsw3sgQUSj7cug -F+FxZc4dZjH3dgEZyH0DWLaVSR2mEiboxgx24ONmy+pdpibu5cxfvWenAScOospU -xbF6lR1xHkopigPcakXBpBlebzbNw6Kwt/5cOOJSvPhEQ+aQuwIDAQABo4ICUjCC -Ak4wPQYIKwYBBQUHAQEEMTAvMC0GCCsGAQUFBzABhiFodHRwczovL29jc3AucXVv -dmFkaXNvZmZzaG9yZS5jb20wDwYDVR0TAQH/BAUwAwEB/zCCARoGA1UdIASCAREw -ggENMIIBCQYJKwYBBAG+WAABMIH7MIHUBggrBgEFBQcCAjCBxxqBxFJlbGlhbmNl -IG9uIHRoZSBRdW9WYWRpcyBSb290IENlcnRpZmljYXRlIGJ5IGFueSBwYXJ0eSBh -c3N1bWVzIGFjY2VwdGFuY2Ugb2YgdGhlIHRoZW4gYXBwbGljYWJsZSBzdGFuZGFy -ZCB0ZXJtcyBhbmQgY29uZGl0aW9ucyBvZiB1c2UsIGNlcnRpZmljYXRpb24gcHJh -Y3RpY2VzLCBhbmQgdGhlIFF1b1ZhZGlzIENlcnRpZmljYXRlIFBvbGljeS4wIgYI -KwYBBQUHAgEWFmh0dHA6Ly93d3cucXVvdmFkaXMuYm0wHQYDVR0OBBYEFItLbe3T -KbkGGew5Oanwl4Rqy+/fMIGuBgNVHSMEgaYwgaOAFItLbe3TKbkGGew5Oanwl4Rq -y+/foYGEpIGBMH8xCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1p -dGVkMSUwIwYDVQQLExxSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MS4wLAYD -VQQDEyVRdW9WYWRpcyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ6tlCL -MA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQUFAAOCAQEAitQUtf70mpKnGdSk -fnIYj9lofFIk3WdvOXrEql494liwTXCYhGHoG+NpGA7O+0dQoE7/8CQfvbLO9Sf8 -7C9TqnN7Az10buYWnuulLsS/VidQK2K6vkscPFVcQR0kvoIgR13VRH56FmjffU1R -cHhXHTMe/QKZnAzNCgVPx7uOpHX6Sm2xgI4JVrmcGmD+XcHXetwReNDWXcG31a0y -mQM6isxUJTkxgXsTIlG6Rmyhu576BGxJJnSP0nPrzDCi5upZIof4l/UO/erMkqQW -xFIY6iHOsfHmhIHluqmGKPJDWl0Snawe2ajlCmqnf6CHKc/yiU3U7MXi5nrQNiOK -SnQ2+Q== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFYDCCA0igAwIBAgIUeFhfLq0sGUvjNwc1NBMotZbUZZMwDQYJKoZIhvcNAQEL -BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc -BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMSBHMzAeFw0xMjAxMTIxNzI3NDRaFw00 -MjAxMTIxNzI3NDRaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM -aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDEgRzMwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvlAQjunybEC0BJyFuTHK3C3kEakEPBtV -wedYMB0ktMPvhd6MLOHBPd+C5k+tR4ds7FtJwUrVu4/sh6x/gpqG7D0DmVIB0jWe -rNrwU8lmPNSsAgHaJNM7qAJGr6Qc4/hzWHa39g6QDbXwz8z6+cZM5cOGMAqNF341 -68Xfuw6cwI2H44g4hWf6Pser4BOcBRiYz5P1sZK0/CPTz9XEJ0ngnjybCKOLXSoh -4Pw5qlPafX7PGglTvF0FBM+hSo+LdoINofjSxxR3W5A2B4GbPgb6Ul5jxaYA/qXp -UhtStZI5cgMJYr2wYBZupt0lwgNm3fME0UDiTouG9G/lg6AnhF4EwfWQvTA9xO+o -abw4m6SkltFi2mnAAZauy8RRNOoMqv8hjlmPSlzkYZqn0ukqeI1RPToV7qJZjqlc -3sX5kCLliEVx3ZGZbHqfPT2YfF72vhZooF6uCyP8Wg+qInYtyaEQHeTTRCOQiJ/G -KubX9ZqzWB4vMIkIG1SitZgj7Ah3HJVdYdHLiZxfokqRmu8hqkkWCKi9YSgxyXSt -hfbZxbGL0eUQMk1fiyA6PEkfM4VZDdvLCXVDaXP7a3F98N/ETH3Goy7IlXnLc6KO -Tk0k+17kBL5yG6YnLUlamXrXXAkgt3+UuU/xDRxeiEIbEbfnkduebPRq34wGmAOt -zCjvpUfzUwIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB -BjAdBgNVHQ4EFgQUo5fW816iEOGrRZ88F2Q87gFwnMwwDQYJKoZIhvcNAQELBQAD -ggIBABj6W3X8PnrHX3fHyt/PX8MSxEBd1DKquGrX1RUVRpgjpeaQWxiZTOOtQqOC -MTaIzen7xASWSIsBx40Bz1szBpZGZnQdT+3Btrm0DWHMY37XLneMlhwqI2hrhVd2 -cDMT/uFPpiN3GPoajOi9ZcnPP/TJF9zrx7zABC4tRi9pZsMbj/7sPtPKlL92CiUN -qXsCHKnQO18LwIE6PWThv6ctTr1NxNgpxiIY0MWscgKCP6o6ojoilzHdCGPDdRS5 -YCgtW2jgFqlmgiNR9etT2DGbe+m3nUvriBbP+V04ikkwj+3x6xn0dxoxGE1nVGwv -b2X52z3sIexe9PSLymBlVNFxZPT5pqOBMzYzcfCkeF9OrYMh3jRJjehZrJ3ydlo2 -8hP0r+AJx2EqbPfgna67hkooby7utHnNkDPDs3b69fBsnQGQ+p6Q9pxyz0fawx/k -NSBT8lTR32GDpgLiJTjehTItXnOQUl1CxM49S+H5GYQd1aJQzEH7QRTDvdbJWqNj -ZgKAvQU6O0ec7AAmTPWIUb+oI38YB7AL7YsmoWTTYUrrXJ/es69nA7Mf3W1daWhp -q1467HxpvMc7hU6eFbm0FU/DlXpY18ls6Wy58yljXrQs8C097Vpl4KlbQMJImYFt -nh8GKjwStIsPm6Ik8KaN1nrgS7ZklmOVhMJKzRwuJIczYOXD ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFtzCCA5+gAwIBAgICBQkwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x -GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv -b3QgQ0EgMjAeFw0wNjExMjQxODI3MDBaFw0zMTExMjQxODIzMzNaMEUxCzAJBgNV -BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W -YWRpcyBSb290IENBIDIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCa -GMpLlA0ALa8DKYrwD4HIrkwZhR0In6spRIXzL4GtMh6QRr+jhiYaHv5+HBg6XJxg -Fyo6dIMzMH1hVBHL7avg5tKifvVrbxi3Cgst/ek+7wrGsxDp3MJGF/hd/aTa/55J -WpzmM+Yklvc/ulsrHHo1wtZn/qtmUIttKGAr79dgw8eTvI02kfN/+NsRE8Scd3bB -rrcCaoF6qUWD4gXmuVbBlDePSHFjIuwXZQeVikvfj8ZaCuWw419eaxGrDPmF60Tp -+ARz8un+XJiM9XOva7R+zdRcAitMOeGylZUtQofX1bOQQ7dsE/He3fbE+Ik/0XX1 -ksOR1YqI0JDs3G3eicJlcZaLDQP9nL9bFqyS2+r+eXyt66/3FsvbzSUr5R/7mp/i -Ucw6UwxI5g69ybR2BlLmEROFcmMDBOAENisgGQLodKcftslWZvB1JdxnwQ5hYIiz -PtGo/KPaHbDRsSNU30R2be1B2MGyIrZTHN81Hdyhdyox5C315eXbyOD/5YDXC2Og -/zOhD7osFRXql7PSorW+8oyWHhqPHWykYTe5hnMz15eWniN9gqRMgeKh0bpnX5UH -oycR7hYQe7xFSkyyBNKr79X9DFHOUGoIMfmR2gyPZFwDwzqLID9ujWc9Otb+fVuI -yV77zGHcizN300QyNQliBJIWENieJ0f7OyHj+OsdWwIDAQABo4GwMIGtMA8GA1Ud -EwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMB0GA1UdDgQWBBQahGK8SEwzJQTU7tD2 -A8QZRtGUazBuBgNVHSMEZzBlgBQahGK8SEwzJQTU7tD2A8QZRtGUa6FJpEcwRTEL -MAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMT -ElF1b1ZhZGlzIFJvb3QgQ0EgMoICBQkwDQYJKoZIhvcNAQEFBQADggIBAD4KFk2f -BluornFdLwUvZ+YTRYPENvbzwCYMDbVHZF34tHLJRqUDGCdViXh9duqWNIAXINzn -g/iN/Ae42l9NLmeyhP3ZRPx3UIHmfLTJDQtyU/h2BwdBR5YM++CCJpNVjP4iH2Bl -fF/nJrP3MpCYUNQ3cVX2kiF495V5+vgtJodmVjB3pjd4M1IQWK4/YY7yarHvGH5K -WWPKjaJW1acvvFYfzznB4vsKqBUsfU16Y8Zsl0Q80m/DShcK+JDSV6IZUaUtl0Ha -B0+pUNqQjZRG4T7wlP0QADj1O+hA4bRuVhogzG9Yje0uRY/W6ZM/57Es3zrWIozc -hLsib9D45MY56QSIPMO661V6bYCZJPVsAfv4l7CUW+v90m/xd2gNNWQjrLhVoQPR -TUIZ3Ph1WVaj+ahJefivDrkRoHy3au000LYmYjgahwz46P0u05B/B5EqHdZ+XIWD -mbA4CD/pXvk1B+TJYm5Xf6dQlfe6yJvmjqIBxdZmv3lh8zwc4bmCXF2gw+nYSL0Z -ohEUGW6yhhtoPkg3Goi3XZZenMfvJ2II4pEZXNLxId26F0KCl3GBUzGpn/Z9Yr9y -4aOTHcyKJloJONDO1w2AFrR4pTqHTI2KpdVGl/IsELm8VCLAAVBpQ570su9t+Oza -8eOx79+Rj1QqCyXBJhnEUhAFZdWCEOrCMc0u ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFYDCCA0igAwIBAgIURFc0JFuBiZs18s64KztbpybwdSgwDQYJKoZIhvcNAQEL -BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc -BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMiBHMzAeFw0xMjAxMTIxODU5MzJaFw00 -MjAxMTIxODU5MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM -aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDIgRzMwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQChriWyARjcV4g/Ruv5r+LrI3HimtFhZiFf -qq8nUeVuGxbULX1QsFN3vXg6YOJkApt8hpvWGo6t/x8Vf9WVHhLL5hSEBMHfNrMW -n4rjyduYNM7YMxcoRvynyfDStNVNCXJJ+fKH46nafaF9a7I6JaltUkSs+L5u+9ym -c5GQYaYDFCDy54ejiK2toIz/pgslUiXnFgHVy7g1gQyjO/Dh4fxaXc6AcW34Sas+ -O7q414AB+6XrW7PFXmAqMaCvN+ggOp+oMiwMzAkd056OXbxMmO7FGmh77FOm6RQ1 -o9/NgJ8MSPsc9PG/Srj61YxxSscfrf5BmrODXfKEVu+lV0POKa2Mq1W/xPtbAd0j -IaFYAI7D0GoT7RPjEiuA3GfmlbLNHiJuKvhB1PLKFAeNilUSxmn1uIZoL1NesNKq -IcGY5jDjZ1XHm26sGahVpkUG0CM62+tlXSoREfA7T8pt9DTEceT/AFr2XK4jYIVz -8eQQsSWu1ZK7E8EM4DnatDlXtas1qnIhO4M15zHfeiFuuDIIfR0ykRVKYnLP43eh -vNURG3YBZwjgQQvD6xVu+KQZ2aKrr+InUlYrAoosFCT5v0ICvybIxo/gbjh9Uy3l -7ZizlWNof/k19N+IxWA1ksB8aRxhlRbQ694Lrz4EEEVlWFA4r0jyWbYW8jwNkALG -cC4BrTwV1wIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB -BjAdBgNVHQ4EFgQU7edvdlq/YOxJW8ald7tyFnGbxD0wDQYJKoZIhvcNAQELBQAD -ggIBAJHfgD9DCX5xwvfrs4iP4VGyvD11+ShdyLyZm3tdquXK4Qr36LLTn91nMX66 -AarHakE7kNQIXLJgapDwyM4DYvmL7ftuKtwGTTwpD4kWilhMSA/ohGHqPHKmd+RC -roijQ1h5fq7KpVMNqT1wvSAZYaRsOPxDMuHBR//47PERIjKWnML2W2mWeyAMQ0Ga -W/ZZGYjeVYg3UQt4XAoeo0L9x52ID8DyeAIkVJOviYeIyUqAHerQbj5hLja7NQ4n -lv1mNDthcnPxFlxHBlRJAHpYErAK74X9sbgzdWqTHBLmYF5vHX/JHyPLhGGfHoJE -+V+tYlUkmlKY7VHnoX6XOuYvHxHaU4AshZ6rNRDbIl9qxV6XU/IyAgkwo1jwDQHV -csaxfGl7w/U2Rcxhbl5MlMVerugOXou/983g7aEOGzPuVBj+D77vfoRrQ+NwmNtd -dbINWQeFFSM51vHfqSYP1kjHs6Yi9TM3WpVHn3u6GBVv/9YUZINJ0gpnIdsPNWNg -KCLjsZWDzYWm3S8P52dSbrsvhXz1SnPnxT7AvSESBT/8twNJAlvIJebiVDj1eYeM -HVOyToV7BjjHLPj4sHKNJeV3UvQDHEimUF+IIDBu8oJDqz2XhOdT+yHBTw8imoa4 -WSr2Rz0ZiC3oheGe7IUIarFsNMkd7EgrO3jtZsSOeWmD3n+M ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGnTCCBIWgAwIBAgICBcYwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQk0x -GTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxGzAZBgNVBAMTElF1b1ZhZGlzIFJv -b3QgQ0EgMzAeFw0wNjExMjQxOTExMjNaFw0zMTExMjQxOTA2NDRaMEUxCzAJBgNV -BAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBMaW1pdGVkMRswGQYDVQQDExJRdW9W -YWRpcyBSb290IENBIDMwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDM -V0IWVJzmmNPTTe7+7cefQzlKZbPoFog02w1ZkXTPkrgEQK0CSzGrvI2RaNggDhoB -4hp7Thdd4oq3P5kazethq8Jlph+3t723j/z9cI8LoGe+AaJZz3HmDyl2/7FWeUUr -H556VOijKTVopAFPD6QuN+8bv+OPEKhyq1hX51SGyMnzW9os2l2ObjyjPtr7guXd -8lyyBTNvijbO0BNO/79KDDRMpsMhvVAEVeuxu537RR5kFd5VAYwCdrXLoT9Cabwv -vWhDFlaJKjdhkf2mrk7AyxRllDdLkgbvBNDInIjbC3uBr7E9KsRlOni27tyAsdLT -mZw67mtaa7ONt9XOnMK+pUsvFrGeaDsGb659n/je7Mwpp5ijJUMv7/FfJuGITfhe -btfZFG4ZM2mnO4SJk8RTVROhUXhA+LjJou57ulJCg54U7QVSWllWp5f8nT8KKdjc -T5EOE7zelaTfi5m+rJsziO+1ga8bxiJTyPbH7pcUsMV8eFLI8M5ud2CEpukqdiDt -WAEXMJPpGovgc2PZapKUSU60rUqFxKMiMPwJ7Wgic6aIDFUhWMXhOp8q3crhkODZ -c6tsgLjoC2SToJyMGf+z0gzskSaHirOi4XCPLArlzW1oUevaPwV/izLmE1xr/l9A -4iLItLRkT9a6fUg+qGkM17uGcclzuD87nSVL2v9A6wIDAQABo4IBlTCCAZEwDwYD -VR0TAQH/BAUwAwEB/zCB4QYDVR0gBIHZMIHWMIHTBgkrBgEEAb5YAAMwgcUwgZMG -CCsGAQUFBwICMIGGGoGDQW55IHVzZSBvZiB0aGlzIENlcnRpZmljYXRlIGNvbnN0 -aXR1dGVzIGFjY2VwdGFuY2Ugb2YgdGhlIFF1b1ZhZGlzIFJvb3QgQ0EgMyBDZXJ0 -aWZpY2F0ZSBQb2xpY3kgLyBDZXJ0aWZpY2F0aW9uIFByYWN0aWNlIFN0YXRlbWVu -dC4wLQYIKwYBBQUHAgEWIWh0dHA6Ly93d3cucXVvdmFkaXNnbG9iYWwuY29tL2Nw -czALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFPLAE+CCQz777i9nMpY1XNu4ywLQMG4G -A1UdIwRnMGWAFPLAE+CCQz777i9nMpY1XNu4ywLQoUmkRzBFMQswCQYDVQQGEwJC -TTEZMBcGA1UEChMQUXVvVmFkaXMgTGltaXRlZDEbMBkGA1UEAxMSUXVvVmFkaXMg -Um9vdCBDQSAzggIFxjANBgkqhkiG9w0BAQUFAAOCAgEAT62gLEz6wPJv92ZVqyM0 -7ucp2sNbtrCD2dDQ4iH782CnO11gUyeim/YIIirnv6By5ZwkajGxkHon24QRiSem -d1o417+shvzuXYO8BsbRd2sPbSQvS3pspweWyuOEn62Iix2rFo1bZhfZFvSLgNLd -+LJ2w/w4E6oM3kJpK27zPOuAJ9v1pkQNn1pVWQvVDVJIxa6f8i+AxeoyUDUSly7B -4f/xI4hROJ/yZlZ25w9Rl6VSDE1JUZU2Pb+iSwwQHYaZTKrzchGT5Or2m9qoXadN -t54CrnMAyNojA+j56hl0YgCUyyIgvpSnWbWCar6ZeXqp8kokUvd0/bpO5qgdAm6x -DYBEwa7TIzdfu4V8K5Iu6H6li92Z4b8nby1dqnuH/grdS/yO9SbkbnBCbjPsMZ57 -k8HkyWkaPcBrTiJt7qtYTcbQQcEr6k8Sh17rRdhs9ZgC06DYVYoGmRmioHfRMJ6s -zHXug/WwYjnPbFfiTNKRCw51KBuav/0aQ/HKd/s7j2G4aSgWQgRecCocIdiP4b0j -Wy10QJLZYxkNc91pvGJHvOB0K7Lrfb5BG7XARsWhIstfTsEokt4YutUqKLsRixeT -mJlglFwjz1onl14LBQaTNx47aTbrqZ5hHY8y2o4M1nQ+ewkk2gF3R8Q7zTSMmfXK -4SVhM7JZG+Ju1zdXtg2pEto= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFYDCCA0igAwIBAgIULvWbAiin23r/1aOp7r0DoM8Sah0wDQYJKoZIhvcNAQEL -BQAwSDELMAkGA1UEBhMCQk0xGTAXBgNVBAoTEFF1b1ZhZGlzIExpbWl0ZWQxHjAc -BgNVBAMTFVF1b1ZhZGlzIFJvb3QgQ0EgMyBHMzAeFw0xMjAxMTIyMDI2MzJaFw00 -MjAxMTIyMDI2MzJaMEgxCzAJBgNVBAYTAkJNMRkwFwYDVQQKExBRdW9WYWRpcyBM -aW1pdGVkMR4wHAYDVQQDExVRdW9WYWRpcyBSb290IENBIDMgRzMwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQCzyw4QZ47qFJenMioKVjZ/aEzHs286IxSR -/xl/pcqs7rN2nXrpixurazHb+gtTTK/FpRp5PIpM/6zfJd5O2YIyC0TeytuMrKNu -FoM7pmRLMon7FhY4futD4tN0SsJiCnMK3UmzV9KwCoWdcTzeo8vAMvMBOSBDGzXR -U7Ox7sWTaYI+FrUoRqHe6okJ7UO4BUaKhvVZR74bbwEhELn9qdIoyhA5CcoTNs+c -ra1AdHkrAj80//ogaX3T7mH1urPnMNA3I4ZyYUUpSFlob3emLoG+B01vr87ERROR -FHAGjx+f+IdpsQ7vw4kZ6+ocYfx6bIrc1gMLnia6Et3UVDmrJqMz6nWB2i3ND0/k -A9HvFZcba5DFApCTZgIhsUfei5pKgLlVj7WiL8DWM2fafsSntARE60f75li59wzw -eyuxwHApw0BiLTtIadwjPEjrewl5qW3aqDCYz4ByA4imW0aucnl8CAMhZa634Ryl -sSqiMd5mBPfAdOhx3v89WcyWJhKLhZVXGqtrdQtEPREoPHtht+KPZ0/l7DxMYIBp -VzgeAVuNVejH38DMdyM0SXV89pgR6y3e7UEuFAUCf+D+IOs15xGsIs5XPd7JMG0Q -A4XN8f+MFrXBsj6IbGB/kE+V9/YtrQE5BwT6dYB9v0lQ7e/JxHwc64B+27bQ3RP+ -ydOc17KXqQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIB -BjAdBgNVHQ4EFgQUxhfQvKjqAkPyGwaZXSuQILnXnOQwDQYJKoZIhvcNAQELBQAD -ggIBADRh2Va1EodVTd2jNTFGu6QHcrxfYWLopfsLN7E8trP6KZ1/AvWkyaiTt3px -KGmPc+FSkNrVvjrlt3ZqVoAh313m6Tqe5T72omnHKgqwGEfcIHB9UqM+WXzBusnI -FUBhynLWcKzSt/Ac5IYp8M7vaGPQtSCKFWGafoaYtMnCdvvMujAWzKNhxnQT5Wvv -oxXqA/4Ti2Tk08HS6IT7SdEQTXlm66r99I0xHnAUrdzeZxNMgRVhvLfZkXdxGYFg -u/BYpbWcC/ePIlUnwEsBbTuZDdQdm2NnL9DuDcpmvJRPpq3t/O5jrFc/ZSXPsoaP -0Aj/uHYUbt7lJ+yreLVTubY/6CD50qi+YUbKh4yE8/nxoGibIh6BJpsQBJFxwAYf -3KDTuVan45gtf4Od34wrnDKOMpTwATwiKp9Dwi7DmDkHOHv8XgBCH/MyJnmDhPbl -8MFREsALHgQjDFSlTC9JxUrRtm5gDWv8a4uFJGS3iQ6rJUdbPM9+Sb3H6QrG2vd+ -DhcI00iX0HGS8A85PjRqHH3Y8iKuu2n0M7SmSFXRDw4m6Oy2Cy2nhTXN/VnIn9HN -PlopNLk9hM6xZdRZkZFWdSHBd575euFgndOtBBj0fOtek49TSiIp+EgrPk2GrFt/ -ywaZWWDYWGWVjUTR939+J399roD1B0y2PpxxVJkES/1Y+Zj0 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIClDCCAhqgAwIBAgIILCmcWxbtBZUwCgYIKoZIzj0EAwIwfzELMAkGA1UEBhMC -VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T -U0wgQ29ycG9yYXRpb24xNDAyBgNVBAMMK1NTTC5jb20gRVYgUm9vdCBDZXJ0aWZp -Y2F0aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNTIzWhcNNDEwMjEyMTgx -NTIzWjB/MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv -dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjE0MDIGA1UEAwwrU1NMLmNv -bSBFViBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49 -AgEGBSuBBAAiA2IABKoSR5CYG/vvw0AHgyBO8TCCogbR8pKGYfL2IWjKAMTH6kMA -VIbc/R/fALhBYlzccBYy3h+Z1MzFB8gIH2EWB1E9fVwHU+M1OIzfzZ/ZLg1Kthku -WnBaBu2+8KGwytAJKaNjMGEwHQYDVR0OBBYEFFvKXuXe0oGqzagtZFG22XKbl+ZP -MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUW8pe5d7SgarNqC1kUbbZcpuX -5k8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2gAMGUCMQCK5kCJN+vp1RPZ -ytRrJPOwPYdGWBrssd9v+1a6cGvHOMzosYxPD/fxZ3YOg9AeUY8CMD32IygmTMZg -h5Mmm7I1HrrW9zzRHM76JTymGoEVW/MSD2zuZYrJh6j5B+BimoxcSg== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIF6zCCA9OgAwIBAgIIVrYpzTS8ePYwDQYJKoZIhvcNAQELBQAwgYIxCzAJBgNV -BAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3RvbjEYMBYGA1UE -CgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQDDC5TU0wuY29tIEVWIFJvb3QgQ2Vy -dGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIyMB4XDTE3MDUzMTE4MTQzN1oXDTQy -MDUzMDE4MTQzN1owgYIxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4G -A1UEBwwHSG91c3RvbjEYMBYGA1UECgwPU1NMIENvcnBvcmF0aW9uMTcwNQYDVQQD -DC5TU0wuY29tIEVWIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgUlNBIFIy -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAjzZlQOHWTcDXtOlG2mvq -M0fNTPl9fb69LT3w23jhhqXZuglXaO1XPqDQCEGD5yhBJB/jchXQARr7XnAjssuf -OePPxU7Gkm0mxnu7s9onnQqG6YE3Bf7wcXHswxzpY6IXFJ3vG2fThVUCAtZJycxa -4bH3bzKfydQ7iEGonL3Lq9ttewkfokxykNorCPzPPFTOZw+oz12WGQvE43LrrdF9 -HSfvkusQv1vrO6/PgN3B0pYEW3p+pKk8OHakYo6gOV7qd89dAFmPZiw+B6KjBSYR -aZfqhbcPlgtLyEDhULouisv3D5oi53+aNxPN8k0TayHRwMwi8qFG9kRpnMphNQcA -b9ZhCBHqurj26bNg5U257J8UZslXWNvNh2n4ioYSA0e/ZhN2rHd9NCSFg83XqpyQ -Gp8hLH94t2S42Oim9HizVcuE0jLEeK6jj2HdzghTreyI/BXkmg3mnxp3zkyPuBQV -PWKchjgGAGYS5Fl2WlPAApiiECtoRHuOec4zSnaqW4EWG7WK2NAAe15itAnWhmMO -pgWVSbooi4iTsjQc2KRVbrcc0N6ZVTsj9CLg+SlmJuwgUHfbSguPvuUCYHBBXtSu -UDkiFCbLsjtzdFVHB3mBOagwE0TlBIqulhMlQg+5U8Sb/M3kHN48+qvWBkofZ6aY -MBzdLNvcGJVXZsb/XItW9XcCAwEAAaNjMGEwDwYDVR0TAQH/BAUwAwEB/zAfBgNV -HSMEGDAWgBT5YLvU49U09rj1BoAlp3PbRmmonjAdBgNVHQ4EFgQU+WC71OPVNPa4 -9QaAJadz20ZpqJ4wDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBCwUAA4ICAQBW -s47LCp1Jjr+kxJG7ZhcFUZh1++VQLHqe8RT6q9OKPv+RKY9ji9i0qVQBDb6Thi/5 -Sm3HXvVX+cpVHBK+Rw82xd9qt9t1wkclf7nxY/hoLVUE0fKNsKTPvDxeH3jnpaAg -cLAExbf3cqfeIg29MyVGjGSSJuM+LmOW2puMPfgYCdcDzH2GguDKBAdRUNf/ktUM -79qGn5nX67evaOI5JpS6aLe/g9Pqemc9YmeuJeVy6OLk7K4S9ksrPJ/psEDzOFSz -/bdoyNrGj1E8svuR3Bznm53htw1yj+KkxKl4+esUrMZDBcJlOSgYAsOCsp0FvmXt -ll9ldDz7CTUue5wT/RsPXcdtgTpWD8w74a8CLyKsRspGPKAcTNZEtF4uXBVmCeEm -Kf7GUmG6sXP/wwyc5WxqlD8UykAWlYTzWamsX0xhk23RO8yilQwipmdnRC652dKK -QbNmC1r7fSOl8hqw/96bg5Qu0T/fkreRrwU7ZcegbLHNYhLDkBvjJc40vG93drEQ -w/cFGsDWr3RiSBd3kmmQYRzelYB0VI8YHMPzA9C/pEN1hlMYegouCRw2n5H9gooi -S9EOUCXdywMMF8mDAAhONU2Ki+3wApRmLER/y5UnlhetCTCstnEXbosX9hwJ1C07 -mKVx01QT2WDz9UtmT/rx7iASjbSsV7FFY6GsdqnC+w== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICjTCCAhSgAwIBAgIIdebfy8FoW6gwCgYIKoZIzj0EAwIwfDELMAkGA1UEBhMC -VVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQKDA9T -U0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZpY2F0 -aW9uIEF1dGhvcml0eSBFQ0MwHhcNMTYwMjEyMTgxNDAzWhcNNDEwMjEyMTgxNDAz -WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0 -b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNvbSBS -b290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IEVDQzB2MBAGByqGSM49AgEGBSuB -BAAiA2IABEVuqVDEpiM2nl8ojRfLliJkP9x6jh3MCLOicSS6jkm5BBtHllirLZXI -7Z4INcgn64mMU1jrYor+8FsPazFSY0E7ic3s7LaNGdM0B9y7xgZ/wkWV7Mt/qCPg -CemB+vNH06NjMGEwHQYDVR0OBBYEFILRhXMw5zUE044CkvvlpNHEIejNMA8GA1Ud -EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUgtGFczDnNQTTjgKS++Wk0cQh6M0wDgYD -VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA2cAMGQCMG/n61kRpGDPYbCWe+0F+S8T -kdzt5fxQaxFGRrMcIQBiu77D5+jNB5n5DQtdcj7EqgIwH7y6C+IwJPt8bYBVCpk+ -gA0z5Wajs6O7pdWLjwkspl1+4vAHCGht0nxpbl/f5Wpl ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIF3TCCA8WgAwIBAgIIeyyb0xaAMpkwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE -BhMCVVMxDjAMBgNVBAgMBVRleGFzMRAwDgYDVQQHDAdIb3VzdG9uMRgwFgYDVQQK -DA9TU0wgQ29ycG9yYXRpb24xMTAvBgNVBAMMKFNTTC5jb20gUm9vdCBDZXJ0aWZp -Y2F0aW9uIEF1dGhvcml0eSBSU0EwHhcNMTYwMjEyMTczOTM5WhcNNDEwMjEyMTcz -OTM5WjB8MQswCQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hv -dXN0b24xGDAWBgNVBAoMD1NTTCBDb3Jwb3JhdGlvbjExMC8GA1UEAwwoU1NMLmNv -bSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFJTQTCCAiIwDQYJKoZIhvcN -AQEBBQADggIPADCCAgoCggIBAPkP3aMrfcvQKv7sZ4Wm5y4bunfh4/WvpOz6Sl2R -xFdHaxh3a3by/ZPkPQ/CFp4LZsNWlJ4Xg4XOVu/yFv0AYvUiCVToZRdOQbngT0aX -qhvIuG5iXmmxX9sqAn78bMrzQdjt0Oj8P2FI7bADFB0QDksZ4LtO7IZl/zbzXmcC -C52GVWH9ejjt/uIZALdvoVBidXQ8oPrIJZK0bnoix/geoeOy3ZExqysdBP+lSgQ3 -6YWkMyv94tZVNHwZpEpox7Ko07fKoZOI68GXvIz5HdkihCR0xwQ9aqkpk8zruFvh -/l8lqjRYyMEjVJ0bmBHDOJx+PYZspQ9AhnwC9FwCTyjLrnGfDzrIM/4RJTXq/LrF -YD3ZfBjVsqnTdXgDciLKOsMf7yzlLqn6niy2UUb9rwPW6mBo6oUWNmuF6R7As93E -JNyAKoFBbZQ+yODJgUEAnl6/f8UImKIYLEJAs/lvOCdLToD0PYFH4Ih86hzOtXVc -US4cK38acijnALXRdMbX5J+tB5O2UzU1/Dfkw/ZdFr4hc96SCvigY2q8lpJqPvi8 -ZVWb3vUNiSYE/CUapiVpy8JtynziWV+XrOvvLsi81xtZPCvM8hnIk2snYxnP/Okm -+Mpxm3+T/jRnhE6Z6/yzeAkzcLpmpnbtG3PrGqUNxCITIJRWCk4sbE6x/c+cCbqi -M+2HAgMBAAGjYzBhMB0GA1UdDgQWBBTdBAkHovV6fVJTEpKV7jiAJQ2mWTAPBgNV -HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFN0ECQei9Xp9UlMSkpXuOIAlDaZZMA4G -A1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAgEAIBgRlCn7Jp0cHh5wYfGV -cpNxJK1ok1iOMq8bs3AD/CUrdIWQPXhq9LmLpZc7tRiRux6n+UBbkflVma8eEdBc -Hadm47GUBwwyOabqG7B52B2ccETjit3E+ZUfijhDPwGFpUenPUayvOUiaPd7nNgs -PgohyC0zrL/FgZkxdMF1ccW+sfAjRfSda/wZY52jvATGGAslu1OJD7OAUN5F7kR/ -q5R4ZJjT9ijdh9hwZXT7DrkT66cPYakylszeu+1jTBi7qUD3oFRuIIhxdRjqerQ0 -cuAjJ3dctpDqhiVAq+8zD8ufgr6iIPv2tS0a5sKFsXQP+8hlAqRSAUfdSSLBv9jr -a6x+3uxjMxW3IwiPxg+NQVrdjsW5j+VFP3jbutIbQLH+cU0/4IGiul607BXgk90I -H37hVZkLId6Tngr75qNJvTYw/ud3sqB1l7UtgYgXZSD32pAAn8lSzDLKNXz1PQ/Y -K9f1JmzJBjSWFupwWRoyeXkLtoh/D1JIPb9s2KJELtFOt3JY04kTlf5Eq/jXixtu -nLwsoFvVagCvXzfh1foQC5ichucmj87w7G6KVwuA406ywKBjYZC6VWg3dGq2ktuf -oYYitmUnDuy2n0Jg5GfCtdpBC8TTi2EbvPofkSvXRAdeuims2cXp71NIWuuA8ShY -Ic2wBlX7Jz9TkHCpBB5XJ7k= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDcjCCAlqgAwIBAgIUPopdB+xV0jLVt+O2XwHrLdzk1uQwDQYJKoZIhvcNAQEL -BQAwUTELMAkGA1UEBhMCUEwxKDAmBgNVBAoMH0tyYWpvd2EgSXpiYSBSb3psaWN6 -ZW5pb3dhIFMuQS4xGDAWBgNVBAMMD1NaQUZJUiBST09UIENBMjAeFw0xNTEwMTkw -NzQzMzBaFw0zNTEwMTkwNzQzMzBaMFExCzAJBgNVBAYTAlBMMSgwJgYDVQQKDB9L -cmFqb3dhIEl6YmEgUm96bGljemVuaW93YSBTLkEuMRgwFgYDVQQDDA9TWkFGSVIg -Uk9PVCBDQTIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC3vD5QqEvN -QLXOYeeWyrSh2gwisPq1e3YAd4wLz32ohswmUeQgPYUM1ljj5/QqGJ3a0a4m7utT -3PSQ1hNKDJA8w/Ta0o4NkjrcsbH/ON7Dui1fgLkCvUqdGw+0w8LBZwPd3BucPbOw -3gAeqDRHu5rr/gsUvTaE2g0gv/pby6kWIK05YO4vdbbnl5z5Pv1+TW9NL++IDWr6 -3fE9biCloBK0TXC5ztdyO4mTp4CEHCdJckm1/zuVnsHMyAHs6A6KCpbns6aH5db5 -BSsNl0BwPLqsdVqc1U2dAgrSS5tmS0YHF2Wtn2yIANwiieDhZNRnvDF5YTy7ykHN -XGoAyDw4jlivAgMBAAGjQjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD -AgEGMB0GA1UdDgQWBBQuFqlKGLXLzPVvUPMjX/hd56zwyDANBgkqhkiG9w0BAQsF -AAOCAQEAtXP4A9xZWx126aMqe5Aosk3AM0+qmrHUuOQn/6mWmc5G4G18TKI4pAZw -8PRBEew/R40/cof5O/2kbytTAOD/OblqBw7rHRz2onKQy4I9EYKL0rufKq8h5mOG -nXkZ7/e7DDWQw4rtTw/1zBLZpD67oPwglV9PJi8RI4NOdQcPv5vRtB3pEAT+ymCP -oky4rc/hkA/NrgrHXXu3UNLUYfrVFdvXn4dRVOul4+vJhaAlIDf7js4MNIThPIGy -d05DpYhfhmehPea0XGG2Ptv+tyjFogeutcrKjSoS75ftwjCkySp6+/NNIxuZMzSg -LvWpCz/UXeHPhJ/iGcJfitYgHuNztw== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDbTCCAlWgAwIBAgIBATANBgkqhkiG9w0BAQUFADBYMQswCQYDVQQGEwJKUDEr -MCkGA1UEChMiSmFwYW4gQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcywgSW5jLjEcMBoG -A1UEAxMTU2VjdXJlU2lnbiBSb290Q0ExMTAeFw0wOTA0MDgwNDU2NDdaFw0yOTA0 -MDgwNDU2NDdaMFgxCzAJBgNVBAYTAkpQMSswKQYDVQQKEyJKYXBhbiBDZXJ0aWZp -Y2F0aW9uIFNlcnZpY2VzLCBJbmMuMRwwGgYDVQQDExNTZWN1cmVTaWduIFJvb3RD -QTExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/XeqpRyQBTvLTJsz -i1oURaTnkBbR31fSIRCkF/3frNYfp+TbfPfs37gD2pRY/V1yfIw/XwFndBWW4wI8 -h9uuywGOwvNmxoVF9ALGOrVisq/6nL+k5tSAMJjzDbaTj6nU2DbysPyKyiyhFTOV -MdrAG/LuYpmGYz+/3ZMqg6h2uRMft85OQoWPIucuGvKVCbIFtUROd6EgvanyTgp9 -UK31BQ1FT0Zx/Sg+U/sE2C3XZR1KG/rPO7AxmjVuyIsG0wCR8pQIZUyxNAYAeoni -8McDWc/V1uinMrPmmECGxc0nEovMe863ETxiYAcjPitAbpSACW22s293bzUIUPsC -h8U+iQIDAQABo0IwQDAdBgNVHQ4EFgQUW/hNT7KlhtQ60vFjmqC+CfZXt94wDgYD -VR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB -AKChOBZmLqdWHyGcBvod7bkixTgm2E5P7KN/ed5GIaGHd48HCJqypMWvDzKYC3xm -KbabfSVSSUOrTC4rbnpwrxYO4wJs+0LmGJ1F2FXI6Dvd5+H0LgscNFxsWEr7jIhQ -X5Ucv+2rIrVls4W6ng+4reV6G4pQOh29Dbx7VFALuUKvVaAYga1lme++5Jy/xIWr -QbJUb9wlze144o4MjQlJ3WN7WmmWAiGovVJZ6X01y8hSyn+B/tlr0/cR7SXf+Of5 -pPpyl4RTDaXQMhhRdlkUbA/r7F+AjHVDg8OFmP9Mni0N5HeDk061lgeLKBObjBmN -QSdJQO7e5iNEOdyhIta6A/I= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDuDCCAqCgAwIBAgIQDPCOXAgWpa1Cf/DrJxhZ0DANBgkqhkiG9w0BAQUFADBI -MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x -FzAVBgNVBAMTDlNlY3VyZVRydXN0IENBMB4XDTA2MTEwNzE5MzExOFoXDTI5MTIz -MTE5NDA1NVowSDELMAkGA1UEBhMCVVMxIDAeBgNVBAoTF1NlY3VyZVRydXN0IENv -cnBvcmF0aW9uMRcwFQYDVQQDEw5TZWN1cmVUcnVzdCBDQTCCASIwDQYJKoZIhvcN -AQEBBQADggEPADCCAQoCggEBAKukgeWVzfX2FI7CT8rU4niVWJxB4Q2ZQCQXOZEz -Zum+4YOvYlyJ0fwkW2Gz4BERQRwdbvC4u/jep4G6pkjGnx29vo6pQT64lO0pGtSO -0gMdA+9tDWccV9cGrcrI9f4Or2YlSASWC12juhbDCE/RRvgUXPLIXgGZbf2IzIao -wW8xQmxSPmjL8xk037uHGFaAJsTQ3MBv396gwpEWoGQRS0S8Hvbn+mPeZqx2pHGj -7DaUaHp3pLHnDi+BeuK1cobvomuL8A/b01k/unK8RCSc43Oz969XL0Imnal0ugBS -8kvNU3xHCzaFDmapCJcWNFfBZveA4+1wVMeT4C4oFVmHursCAwEAAaOBnTCBmjAT -BgkrBgEEAYI3FAIEBh4EAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB -/zAdBgNVHQ4EFgQUQjK2FvoE/f5dS3rD/fdMQB1aQ68wNAYDVR0fBC0wKzApoCeg -JYYjaHR0cDovL2NybC5zZWN1cmV0cnVzdC5jb20vU1RDQS5jcmwwEAYJKwYBBAGC -NxUBBAMCAQAwDQYJKoZIhvcNAQEFBQADggEBADDtT0rhWDpSclu1pqNlGKa7UTt3 -6Z3q059c4EVlew3KW+JwULKUBRSuSceNQQcSc5R+DCMh/bwQf2AQWnL1mA6s7Ll/ -3XpvXdMc9P+IBWlCqQVxyLesJugutIxq/3HcuLHfmbx8IVQr5Fiiu1cprp6poxkm -D5kuCLDv/WnPmRoJjeOnnyvJNjR7JLN4TJUXpAYmHrZkUjZfYGfZnMUFdAvnZyPS -CPyI6a6Lf+Ew9Dd+/cYy2i2eRDAwbO4H3tI0/NL/QPZL9GZGBlSm8jIKYyYwa5vR -3ItHuuG51WLQoqD0ZwV4KWMabwTW+MZMo5qxN7SN5ShLHZ4swrhovO0C7jE= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDvDCCAqSgAwIBAgIQB1YipOjUiolN9BPI8PjqpTANBgkqhkiG9w0BAQUFADBK -MQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3QgQ29ycG9yYXRpb24x -GTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwHhcNMDYxMTA3MTk0MjI4WhcNMjkx -MjMxMTk1MjA2WjBKMQswCQYDVQQGEwJVUzEgMB4GA1UEChMXU2VjdXJlVHJ1c3Qg -Q29ycG9yYXRpb24xGTAXBgNVBAMTEFNlY3VyZSBHbG9iYWwgQ0EwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvNS7YrGxVaQZx5RNoJLNP2MwhR/jxYDiJ -iQPpvepeRlMJ3Fz1Wuj3RSoC6zFh1ykzTM7HfAo3fg+6MpjhHZevj8fcyTiW89sa -/FHtaMbQbqR8JNGuQsiWUGMu4P51/pinX0kuleM5M2SOHqRfkNJnPLLZ/kG5VacJ -jnIFHovdRIWCQtBJwB1g8NEXLJXr9qXBkqPFwqcIYA1gBBCWeZ4WNOaptvolRTnI -HmX5k/Wq8VLcmZg9pYYaDDUz+kulBAYVHDGA76oYa8J719rO+TMg1fW9ajMtgQT7 -sFzUnKPiXB3jqUJ1XnvUd+85VLrJChgbEplJL4hL/VBi0XPnj3pDAgMBAAGjgZ0w -gZowEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF -MAMBAf8wHQYDVR0OBBYEFK9EBMJBfkiD2045AuzshHrmzsmkMDQGA1UdHwQtMCsw -KaAnoCWGI2h0dHA6Ly9jcmwuc2VjdXJldHJ1c3QuY29tL1NHQ0EuY3JsMBAGCSsG -AQQBgjcVAQQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQBjGghAfaReUw132HquHw0L -URYD7xh8yOOvaliTFGCRsoTciE6+OYo68+aCiV0BN7OrJKQVDpI1WkpEXk5X+nXO -H0jOZvQ8QCaSmGwb7iRGDBezUqXbpZGRzzfTb+cnCDpOGR86p1hcF895P4vkp9Mm -I50mD1hp/Ed+stCNi5O/KU9DaXR2Z0vPB4zmAve14bRDtUstFJ/53CYNv6ZHdAbY -iNE6KTCEztI5gGIbqMdXSbxqVVFnFUq+NQfk1XWYN3kwFNspnWzFacxHVaIw98xc -f8LDmBxrThaA63p4ZUWiABqvDA1VZDRIuJK58bRQKfJPIx/abKwfROHdI3hRW8cW ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDdzCCAl+gAwIBAgIBADANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJKUDEl -MCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UECxMe -U2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EyMB4XDTA5MDUyOTA1MDAzOVoX -DTI5MDUyOTA1MDAzOVowXTELMAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRy -dXN0IFN5c3RlbXMgQ08uLExURC4xJzAlBgNVBAsTHlNlY3VyaXR5IENvbW11bmlj -YXRpb24gUm9vdENBMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAV -OVKxUrO6xVmCxF1SrjpDZYBLx/KWvNs2l9amZIyoXvDjChz335c9S672XewhtUGr -zbl+dp+++T42NKA7wfYxEUV0kz1XgMX5iZnK5atq1LXaQZAQwdbWQonCv/Q4EpVM -VAX3NuRFg3sUZdbcDE3R3n4MqzvEFb46VqZab3ZpUql6ucjrappdUtAtCms1FgkQ -hNBqyjoGADdH5H5XTz+L62e4iKrFvlNVspHEfbmwhRkGeC7bYRr6hfVKkaHnFtWO -ojnflLhwHyg/i/xAXmODPIMqGplrz95Zajv8bxbXH/1KEOtOghY6rCcMU/Gt1SSw -awNQwS08Ft1ENCcadfsCAwEAAaNCMEAwHQYDVR0OBBYEFAqFqXdlBZh8QIH4D5cs -OPEK7DzPMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3 -DQEBCwUAA4IBAQBMOqNErLlFsceTfsgLCkLfZOoc7llsCLqJX2rKSpWeeo8HxdpF -coJxDjrSzG+ntKEju/Ykn8sX/oymzsLS28yN/HH8AynBbF0zX2S2ZTuJbxh2ePXc -okgfGT+Ok+vx+hfuzU7jBBJV1uXk3fs+BXziHV7Gp7yXT2g69ekuCkO2r1dcYmh8 -t/2jioSgrGK+KwmHNPBqAbubKVY8/gA3zyNs8U6qtnRGEmyR7jTV7JqR50S+kDFy -1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29mvVXIwAHIRc/ -SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDWjCCAkKgAwIBAgIBADANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJKUDEY -MBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYDVQQLEx5TZWN1cml0eSBDb21t -dW5pY2F0aW9uIFJvb3RDQTEwHhcNMDMwOTMwMDQyMDQ5WhcNMjMwOTMwMDQyMDQ5 -WjBQMQswCQYDVQQGEwJKUDEYMBYGA1UEChMPU0VDT00gVHJ1c3QubmV0MScwJQYD -VQQLEx5TZWN1cml0eSBDb21tdW5pY2F0aW9uIFJvb3RDQTEwggEiMA0GCSqGSIb3 -DQEBAQUAA4IBDwAwggEKAoIBAQCzs/5/022x7xZ8V6UMbXaKL0u/ZPtM7orw8yl8 -9f/uKuDp6bpbZCKamm8sOiZpUQWZJtzVHGpxxpp9Hp3dfGzGjGdnSj74cbAZJ6kJ -DKaVv0uMDPpVmDvY6CKhS3E4eayXkmmziX7qIWgGmBSWh9JhNrxtJ1aeV+7AwFb9 -Ms+k2Y7CI9eNqPPYJayX5HA49LY6tJ07lyZDo6G8SVlyTCMwhwFY9k6+HGhWZq/N -QV3Is00qVUarH9oe4kA92819uZKAnDfdDJZkndwi92SL32HeFZRSFaB9UslLqCHJ -xrHty8OVYNEP8Ktw+N/LTX7s1vqr2b1/VPKl6Xn62dZ2JChzAgMBAAGjPzA9MB0G -A1UdDgQWBBSgc0mZaNyFW2XjmygvV5+9M7wHSDALBgNVHQ8EBAMCAQYwDwYDVR0T -AQH/BAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAaECpqLvkT115swW1F7NgE+vG -kl3g0dNq/vu+m22/xwVtWSDEHPC32oRYAmP6SBbvT6UL90qY8j+eG61Ha2POCEfr -Uj94nK9NrvjVT8+amCoQQTlSxN3Zmw7vkwGusi7KaEIkQmywszo+zenaSMQVy+n5 -Bw+SUEmK3TGXX8npN6o7WWWXlDLJs58+OmJYxUmtYg5xpTKqL8aJdkNAExNnPaJU -JRDL8Try2frbSVa7pv6nQTXD4IhhyYjH3zYQIphZ6rBK+1YWc26sTfcioU+tHXot -RSflMMFe8toTyyVCUZVHA4xsIcx0Qu1T/zOLjw9XARYvz6buyXAiFL39vmwLAw== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDIDCCAgigAwIBAgIBHTANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGSTEP -MA0GA1UEChMGU29uZXJhMRkwFwYDVQQDExBTb25lcmEgQ2xhc3MyIENBMB4XDTAx -MDQwNjA3Mjk0MFoXDTIxMDQwNjA3Mjk0MFowOTELMAkGA1UEBhMCRkkxDzANBgNV -BAoTBlNvbmVyYTEZMBcGA1UEAxMQU29uZXJhIENsYXNzMiBDQTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAJAXSjWdyvANlsdE+hY3/Ei9vX+ALTU74W+o -Z6m/AxxNjG8yR9VBaKQTBME1DJqEQ/xcHf+Js+gXGM2RX/uJ4+q/Tl18GybTdXnt -5oTjV+WtKcT0OijnpXuENmmz/V52vaMtmdOQTiMofRhj8VQ7Jp12W5dCsv+u8E7s -3TmVToMGf+dJQMjFAbJUWmYdPfz56TwKnoG4cPABi+QjVHzIrviQHgCWctRUz2Ej -vOr7nQKV0ba5cTppCD8PtOFCx4j1P5iop7oc4HFx71hXgVB6XGt0Rg6DA5jDjqhu -8nYybieDwnPz3BjotJPqdURrBGAgcVeHnfO+oJAjPYok4doh28MCAwEAAaMzMDEw -DwYDVR0TAQH/BAUwAwEB/zARBgNVHQ4ECgQISqCqWITTXjwwCwYDVR0PBAQDAgEG -MA0GCSqGSIb3DQEBBQUAA4IBAQBazof5FnIVV0sd2ZvnoiYw7JNn39Yt0jSv9zil -zqsWuasvfDXLrNAPtEwr/IDva4yRXzZ299uzGxnq9LIR/WFxRL8oszodv7ND6J+/ -3DEIcbCdjdY0RzKQxmUk96BKfARzjzlvF4xytb1LyHr4e4PDKE6cCepnP7JnBBvD -FNr450kkkdAdavphOe9r5yF1BgfYErQhIHBCcYHaPJo2vqZbDWpsmh+Re/n570K6 -Tk6ezAyNlNzZRZxe7EJQY670XcSxEtzKO6gunRRaBXW37Ndj4ro1tgQIkejanZz2 -ZrUYrAqmVCY0M9IbwdR/GjqOC6oybtv8TyWf2TLHllpwrN9M ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFcDCCA1igAwIBAgIEAJiWjTANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJO -TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSkwJwYDVQQDDCBTdGFh -dCBkZXIgTmVkZXJsYW5kZW4gRVYgUm9vdCBDQTAeFw0xMDEyMDgxMTE5MjlaFw0y -MjEyMDgxMTEwMjhaMFgxCzAJBgNVBAYTAk5MMR4wHAYDVQQKDBVTdGFhdCBkZXIg -TmVkZXJsYW5kZW4xKTAnBgNVBAMMIFN0YWF0IGRlciBOZWRlcmxhbmRlbiBFViBS -b290IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA48d+ifkkSzrS -M4M1LGns3Amk41GoJSt5uAg94JG6hIXGhaTK5skuU6TJJB79VWZxXSzFYGgEt9nC -UiY4iKTWO0Cmws0/zZiTs1QUWJZV1VD+hq2kY39ch/aO5ieSZxeSAgMs3NZmdO3d -Z//BYY1jTw+bbRcwJu+r0h8QoPnFfxZpgQNH7R5ojXKhTbImxrpsX23Wr9GxE46p -rfNeaXUmGD5BKyF/7otdBwadQ8QpCiv8Kj6GyzyDOvnJDdrFmeK8eEEzduG/L13l -pJhQDBXd4Pqcfzho0LKmeqfRMb1+ilgnQ7O6M5HTp5gVXJrm0w912fxBmJc+qiXb -j5IusHsMX/FjqTf5m3VpTCgmJdrV8hJwRVXj33NeN/UhbJCONVrJ0yPr08C+eKxC -KFhmpUZtcALXEPlLVPxdhkqHz3/KRawRWrUgUY0viEeXOcDPusBCAUCZSCELa6fS -/ZbV0b5GnUngC6agIk440ME8MLxwjyx1zNDFjFE7PZQIZCZhfbnDZY8UnCHQqv0X -cgOPvZuM5l5Tnrmd74K74bzickFbIZTTRTeU0d8JOV3nI6qaHcptqAqGhYqCvkIH -1vI4gnPah1vlPNOePqc7nvQDs/nxfRN0Av+7oeX6AHkcpmZBiFxgV6YuCcS6/ZrP -px9Aw7vMWgpVSzs4dlG4Y4uElBbmVvMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB -/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFP6rAJCYniT8qcwaivsnuL8wbqg7 -MA0GCSqGSIb3DQEBCwUAA4ICAQDPdyxuVr5Os7aEAJSrR8kN0nbHhp8dB9O2tLsI -eK9p0gtJ3jPFrK3CiAJ9Brc1AsFgyb/E6JTe1NOpEyVa/m6irn0F3H3zbPB+po3u -2dfOWBfoqSmuc0iH55vKbimhZF8ZE/euBhD/UcabTVUlT5OZEAFTdfETzsemQUHS -v4ilf0X8rLiltTMMgsT7B/Zq5SWEXwbKwYY5EdtYzXc7LMJMD16a4/CrPmEbUCTC -wPTxGfARKbalGAKb12NMcIxHowNDXLldRqANb/9Zjr7dn3LDWyvfjFvO5QxGbJKy -CqNMVEIYFRIYvdr8unRu/8G2oGTYqV9Vrp9canaW2HNnh/tNf1zuacpzEPuKqf2e -vTY4SUmH9A4U8OmHuD+nT3pajnnUk+S7aFKErGzp85hwVXIy+TSrK0m1zSBi5Dp6 -Z2Orltxtrpfs/J92VoguZs9btsmksNcFuuEnL5O7Jiqik7Ab846+HUCjuTaPPoIa -Gl6I6lD4WeKDRikL40Rc4ZW2aZCaFG+XroHPaO+Zmr615+F/+PoTRxZMzG0IQOeL -eG9QgkRQP2YGiqtDhFZKDyAthg710tvSeopLzaXoTvFeJiUBWSOgftL2fiFX1ye8 -FVdMpEbB4IMeDExNH08GGeL5qPQ6gqGyeUN51q1veieQA6TqJIc/2b3Z6fJfUEkc -7uzXLg== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFyjCCA7KgAwIBAgIEAJiWjDANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO -TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh -dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEcyMB4XDTA4MDMyNjExMTgxN1oX -DTIwMDMyNTExMDMxMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl -ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv -b3QgQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMVZ5291 -qj5LnLW4rJ4L5PnZyqtdj7U5EILXr1HgO+EASGrP2uEGQxGZqhQlEq0i6ABtQ8Sp -uOUfiUtnvWFI7/3S4GCI5bkYYCjDdyutsDeqN95kWSpGV+RLufg3fNU254DBtvPU -Z5uW6M7XxgpT0GtJlvOjCwV3SPcl5XCsMBQgJeN/dVrlSPhOewMHBPqCYYdu8DvE -pMfQ9XQ+pV0aCPKbJdL2rAQmPlU6Yiile7Iwr/g3wtG61jj99O9JMDeZJiFIhQGp -5Rbn3JBV3w/oOM2ZNyFPXfUib2rFEhZgF1XyZWampzCROME4HYYEhLoaJXhena/M -UGDWE4dS7WMfbWV9whUYdMrhfmQpjHLYFhN9C0lK8SgbIHRrxT3dsKpICT0ugpTN -GmXZK4iambwYfp/ufWZ8Pr2UuIHOzZgweMFvZ9C+X+Bo7d7iscksWXiSqt8rYGPy -5V6548r6f1CGPqI0GAwJaCgRHOThuVw+R7oyPxjMW4T182t0xHJ04eOLoEq9jWYv -6q012iDTiIJh8BIitrzQ1aTsr1SIJSQ8p22xcik/Plemf1WvbibG/ufMQFxRRIEK -eN5KzlW/HdXZt1bv8Hb/C3m1r737qWmRRpdogBQ2HbN/uymYNqUg+oJgYjOk7Na6 -B6duxc8UpufWkjTYgfX8HV2qXB72o007uPc5AgMBAAGjgZcwgZQwDwYDVR0TAQH/ -BAUwAwEB/zBSBgNVHSAESzBJMEcGBFUdIAAwPzA9BggrBgEFBQcCARYxaHR0cDov -L3d3dy5wa2lvdmVyaGVpZC5ubC9wb2xpY2llcy9yb290LXBvbGljeS1HMjAOBgNV -HQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJFoMocVHYnitfGsNig0jQt8YojrMA0GCSqG -SIb3DQEBCwUAA4ICAQCoQUpnKpKBglBu4dfYszk78wIVCVBR7y29JHuIhjv5tLyS -CZa59sCrI2AGeYwRTlHSeYAz+51IvuxBQ4EffkdAHOV6CMqqi3WtFMTC6GY8ggen -5ieCWxjmD27ZUD6KQhgpxrRW/FYQoAUXvQwjf/ST7ZwaUb7dRUG/kSS0H4zpX897 -IZmflZ85OkYcbPnNe5yQzSipx6lVu6xiNGI1E0sUOlWDuYaNkqbG9AclVMwWVxJK -gnjIFNkXgiYtXSAfea7+1HAWFpWD2DU5/1JddRwWxRNVz0fMdWVSSt7wsKfkCpYL -+63C4iWEst3kvX5ZbJvw8NjnyvLplzh+ib7M+zkXYT9y2zqR2GUBGR2tUKRXCnxL -vJxxcypFURmFzI79R6d0lR2o0a9OF7FpJsKqeFdbxU2n5Z4FF5TKsl+gSRiNNOkm -bEgeqmiSBeGCc1qb3AdbCG19ndeNIdn8FCCqwkXfP+cAslHkwvgFuXkajDTznlvk -N1trSt8sV4pAWja63XVECDdCcAz+3F4hoKOKwJCcaNpQ5kUQR3i2TtJlycM33+FC -Y7BXN0Ute4qcvwXqZVUz9zkQxSgqIXobisQk+T8VyJoVIPVVYpbtbZNQvOSqeK3Z -ywplh6ZmwcSBo3c6WB4L7oOLnR7SUqTMHW+wmG2UMbX4cQrcufx9MmDm66+KAQ== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFdDCCA1ygAwIBAgIEAJiiOTANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJO -TDEeMBwGA1UECgwVU3RhYXQgZGVyIE5lZGVybGFuZGVuMSswKQYDVQQDDCJTdGFh -dCBkZXIgTmVkZXJsYW5kZW4gUm9vdCBDQSAtIEczMB4XDTEzMTExNDExMjg0MloX -DTI4MTExMzIzMDAwMFowWjELMAkGA1UEBhMCTkwxHjAcBgNVBAoMFVN0YWF0IGRl -ciBOZWRlcmxhbmRlbjErMCkGA1UEAwwiU3RhYXQgZGVyIE5lZGVybGFuZGVuIFJv -b3QgQ0EgLSBHMzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAL4yolQP -cPssXFnrbMSkUeiFKrPMSjTysF/zDsccPVMeiAho2G89rcKezIJnByeHaHE6n3WW -IkYFsO2tx1ueKt6c/DrGlaf1F2cY5y9JCAxcz+bMNO14+1Cx3Gsy8KL+tjzk7FqX -xz8ecAgwoNzFs21v0IJyEavSgWhZghe3eJJg+szeP4TrjTgzkApyI/o1zCZxMdFy -KJLZWyNtZrVtB0LrpjPOktvA9mxjeM3KTj215VKb8b475lRgsGYeCasH/lSJEULR -9yS6YHgamPfJEf0WwTUaVHXvQ9Plrk7O53vDxk5hUUurmkVLoR9BvUhTFXFkC4az -5S6+zqQbwSmEorXLCCN2QyIkHxcE1G6cxvx/K2Ya7Irl1s9N9WMJtxU51nus6+N8 -6U78dULI7ViVDAZCopz35HCz33JvWjdAidiFpNfxC95DGdRKWCyMijmev4SH8RY7 -Ngzp07TKbBlBUgmhHbBqv4LvcFEhMtwFdozL92TkA1CvjJFnq8Xy7ljY3r735zHP -bMk7ccHViLVlvMDoFxcHErVc0qsgk7TmgoNwNsXNo42ti+yjwUOH5kPiNL6VizXt -BznaqB16nzaeErAMZRKQFWDZJkBE41ZgpRDUajz9QdwOWke275dhdU/Z/seyHdTt -XUmzqWrLZoQT1Vyg3N9udwbRcXXIV2+vD3dbAgMBAAGjQjBAMA8GA1UdEwEB/wQF -MAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBRUrfrHkleuyjWcLhL75Lpd -INyUVzANBgkqhkiG9w0BAQsFAAOCAgEAMJmdBTLIXg47mAE6iqTnB/d6+Oea31BD -U5cqPco8R5gu4RV78ZLzYdqQJRZlwJ9UXQ4DO1t3ApyEtg2YXzTdO2PCwyiBwpwp -LiniyMMB8jPqKqrMCQj3ZWfGzd/TtiunvczRDnBfuCPRy5FOCvTIeuXZYzbB1N/8 -Ipf3YF3qKS9Ysr1YvY2WTxB1v0h7PVGHoTx0IsL8B3+A3MSs/mrBcDCw6Y5p4ixp -gZQJut3+TcCDjJRYwEYgr5wfAvg1VUkvRtTA8KCWAg8zxXHzniN9lLf9OtMJgwYh -/WA9rjLA0u6NpvDntIJ8CsxwyXmA+P5M9zWEGYox+wrZ13+b8KKaa8MFSu1BYBQw -0aoRQm7TIwIEC8Zl3d1Sd9qBa7Ko+gE4uZbqKmxnl4mUnrzhVNXkanjvSr0rmj1A -fsbAddJu+2gw7OyLnflJNZoaLNmzlTnVHpL3prllL+U9bTpITAjc5CgSKL59NVzq -4BZ+Extq1z7XnvwtdbLBFNUjA9tbbws+eC8N3jONFrdI54OagQ97wUNNVQQXOEpR -1VmiiXTTn74eS9fGbbeIJG9gkaSChVtWQbzQRKtqE77RLFi3EjNYsjdj3BP1lB0/ -QFH1T/U67cjF68IeHRaVesd+QnGTbksVtzDfqu1XhUisHWrdOWnk4Xl4vs4Fv6EM -94B7IWcnMFk= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEDzCCAvegAwIBAgIBADANBgkqhkiG9w0BAQUFADBoMQswCQYDVQQGEwJVUzEl -MCMGA1UEChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMp -U3RhcmZpZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQw -NjI5MTczOTE2WhcNMzQwNjI5MTczOTE2WjBoMQswCQYDVQQGEwJVUzElMCMGA1UE -ChMcU3RhcmZpZWxkIFRlY2hub2xvZ2llcywgSW5jLjEyMDAGA1UECxMpU3RhcmZp -ZWxkIENsYXNzIDIgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwggEgMA0GCSqGSIb3 -DQEBAQUAA4IBDQAwggEIAoIBAQC3Msj+6XGmBIWtDBFk385N78gDGIc/oav7PKaf -8MOh2tTYbitTkPskpD6E8J7oX+zlJ0T1KKY/e97gKvDIr1MvnsoFAZMej2YcOadN -+lq2cwQlZut3f+dZxkqZJRRU6ybH838Z1TBwj6+wRir/resp7defqgSHo9T5iaU0 -X9tDkYI22WY8sbi5gv2cOj4QyDvvBmVmepsZGD3/cVE8MC5fvj13c7JdBmzDI1aa -K4UmkhynArPkPw2vCHmCuDY96pzTNbO8acr1zJ3o/WSNF4Azbl5KXZnJHoe0nRrA -1W4TNSNe35tfPe/W93bC6j67eA0cQmdrBNj41tpvi/JEoAGrAgEDo4HFMIHCMB0G -A1UdDgQWBBS/X7fRzt0fhvRbVazc1xDCDqmI5zCBkgYDVR0jBIGKMIGHgBS/X7fR -zt0fhvRbVazc1xDCDqmI56FspGowaDELMAkGA1UEBhMCVVMxJTAjBgNVBAoTHFN0 -YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAsTKVN0YXJmaWVsZCBD -bGFzcyAyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggEAMAwGA1UdEwQFMAMBAf8w -DQYJKoZIhvcNAQEFBQADggEBAAWdP4id0ckaVaGsafPzWdqbAYcaT1epoXkJKtv3 -L7IezMdeatiDh6GX70k1PncGQVhiv45YuApnP+yz3SFmH8lU+nLMPUxA2IGvd56D -eruix/U0F47ZEUD0/CwqTRV/p2JdLiXTAAsgGh1o+Re49L2L7ShZ3U0WixeDyLJl -xy16paq8U4Zt3VekyvggQQto8PT7dL5WXXp59fkdheMtlb71cZBDzI0fmgAKhynp -VSJYACPq4xJDKVtHCN2MQWplBqjlIapBtJUhlbl90TSrE9atvNziPTnNvT51cKEY -WQPJIrSPnNVeKtelttQKbfi3QBFGmh95DmK/D5fs4C8fF5Q= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBjzELMAkGA1UEBhMCVVMx -EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT -HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xMjAwBgNVBAMTKVN0YXJmaWVs -ZCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5MDkwMTAwMDAw -MFoXDTM3MTIzMTIzNTk1OVowgY8xCzAJBgNVBAYTAlVTMRAwDgYDVQQIEwdBcml6 -b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFyZmllbGQgVGVj -aG5vbG9naWVzLCBJbmMuMTIwMAYDVQQDEylTdGFyZmllbGQgUm9vdCBDZXJ0aWZp -Y2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC -ggEBAL3twQP89o/8ArFvW59I2Z154qK3A2FWGMNHttfKPTUuiUP3oWmb3ooa/RMg -nLRJdzIpVv257IzdIvpy3Cdhl+72WoTsbhm5iSzchFvVdPtrX8WJpRBSiUZV9Lh1 -HOZ/5FSuS/hVclcCGfgXcVnrHigHdMWdSL5stPSksPNkN3mSwOxGXn/hbVNMYq/N -Hwtjuzqd+/x5AJhhdM8mgkBj87JyahkNmcrUDnXMN/uLicFZ8WJ/X7NfZTD4p7dN -dloedl40wOiWVpmKs/B/pM293DIxfJHP4F8R+GuqSVzRmZTRouNjWwl2tVZi4Ut0 -HZbUJtQIBFnQmA4O5t78w+wfkPECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAO -BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFHwMMh+n2TB/xH1oo2Kooc6rB1snMA0G -CSqGSIb3DQEBCwUAA4IBAQARWfolTwNvlJk7mh+ChTnUdgWUXuEok21iXQnCoKjU -sHU48TRqneSfioYmUeYs0cYtbpUgSpIB7LiKZ3sx4mcujJUDJi5DnUox9g61DLu3 -4jd/IroAow57UvtruzvE03lRTs2Q9GcHGcg8RnoNAX3FWOdt5oUwF5okxBDgBPfg -8n/Uqgr/Qh037ZTlZFkSIHc40zI+OIF1lnP6aI+xy84fxez6nH7PfrHxBy22/L/K -pL/QlwVKvOoYKAKQvVR4CSFx09F9HdkWsKlhPdAKACL8x3vLCWRFCztAgfd9fDL1 -mMpYjn0q7pBZc2T5NnReJaH1ZgUufzkVqSr7UIuOhWn0 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID7zCCAtegAwIBAgIBADANBgkqhkiG9w0BAQsFADCBmDELMAkGA1UEBhMCVVMx -EDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAjBgNVBAoT -HFN0YXJmaWVsZCBUZWNobm9sb2dpZXMsIEluYy4xOzA5BgNVBAMTMlN0YXJmaWVs -ZCBTZXJ2aWNlcyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAtIEcyMB4XDTA5 -MDkwMTAwMDAwMFoXDTM3MTIzMTIzNTk1OVowgZgxCzAJBgNVBAYTAlVTMRAwDgYD -VQQIEwdBcml6b25hMRMwEQYDVQQHEwpTY290dHNkYWxlMSUwIwYDVQQKExxTdGFy -ZmllbGQgVGVjaG5vbG9naWVzLCBJbmMuMTswOQYDVQQDEzJTdGFyZmllbGQgU2Vy -dmljZXMgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgLSBHMjCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBANUMOsQq+U7i9b4Zl1+OiFOxHz/Lz58gE20p -OsgPfTz3a3Y4Y9k2YKibXlwAgLIvWX/2h/klQ4bnaRtSmpDhcePYLQ1Ob/bISdm2 -8xpWriu2dBTrz/sm4xq6HZYuajtYlIlHVv8loJNwU4PahHQUw2eeBGg6345AWh1K -Ts9DkTvnVtYAcMtS7nt9rjrnvDH5RfbCYM8TWQIrgMw0R9+53pBlbQLPLJGmpufe -hRhJfGZOozptqbXuNC66DQO4M99H67FrjSXZm86B0UVGMpZwh94CDklDhbZsc7tk -6mFBrMnUVN+HL8cisibMn1lUaJ/8viovxFUcdUBgF4UCVTmLfwUCAwEAAaNCMEAw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFJxfAN+q -AdcwKziIorhtSpzyEZGDMA0GCSqGSIb3DQEBCwUAA4IBAQBLNqaEd2ndOxmfZyMI -bw5hyf2E3F/YNoHN2BtBLZ9g3ccaaNnRbobhiCPPE95Dz+I0swSdHynVv/heyNXB -ve6SbzJ08pGCL72CQnqtKrcgfU28elUSwhXqvfdqlS5sdJ/PHLTyxQGjhdByPq1z -qwubdQxtRbeOlKyWN7Wg0I8VRw7j6IPdj/3vQQF3zCepYoUz8jcI73HPdwbeyBkd -iEDPfUYd/x7H4c7/I9vG+o1VTqkC50cRRj70/b17KSa7qWFiNyi2LSr2EIZkyXCn -0q23KXB56jzaYyWf/Wi3MOxw+3WKt21gZ7IeyLnp2KhvAotnDU0mV3HaIPzBSlCN -sSi6 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFujCCA6KgAwIBAgIJALtAHEP1Xk+wMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV -BAYTAkNIMRUwEwYDVQQKEwxTd2lzc1NpZ24gQUcxHzAdBgNVBAMTFlN3aXNzU2ln -biBHb2xkIENBIC0gRzIwHhcNMDYxMDI1MDgzMDM1WhcNMzYxMDI1MDgzMDM1WjBF -MQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMR8wHQYDVQQDExZT -d2lzc1NpZ24gR29sZCBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC -CgKCAgEAr+TufoskDhJuqVAtFkQ7kpJcyrhdhJJCEyq8ZVeCQD5XJM1QiyUqt2/8 -76LQwB8CJEoTlo8jE+YoWACjR8cGp4QjK7u9lit/VcyLwVcfDmJlD909Vopz2q5+ -bbqBHH5CjCA12UNNhPqE21Is8w4ndwtrvxEvcnifLtg+5hg3Wipy+dpikJKVyh+c -6bM8K8vzARO/Ws/BtQpgvd21mWRTuKCWs2/iJneRjOBiEAKfNA+k1ZIzUd6+jbqE -emA8atufK+ze3gE/bk3lUIbLtK/tREDFylqM2tIrfKjuvqblCqoOpd8FUrdVxyJd -MmqXl2MT28nbeTZ7hTpKxVKJ+STnnXepgv9VHKVxaSvRAiTysybUa9oEVeXBCsdt -MDeQKuSeFDNeFhdVxVu1yzSJkvGdJo+hB9TGsnhQ2wwMC3wLjEHXuendjIj3o02y -MszYF9rNt85mndT9Xv+9lz4pded+p2JYryU0pUHHPbwNUMoDAw8IWh+Vc3hiv69y -FGkOpeUDDniOJihC8AcLYiAQZzlG+qkDzAQ4embvIIO1jEpWjpEA/I5cgt6IoMPi -aG59je883WX0XaxR7ySArqpWl2/5rX3aYT+YdzylkbYcjCbaZaIJbcHiVOO5ykxM -gI93e2CaHt+28kgeDrpOVG2Y4OGiGqJ3UM/EY5LsRxmd6+ZrzsECAwEAAaOBrDCB -qTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUWyV7 -lqRlUX64OfPAeGZe6Drn8O4wHwYDVR0jBBgwFoAUWyV7lqRlUX64OfPAeGZe6Drn -8O4wRgYDVR0gBD8wPTA7BglghXQBWQECAQEwLjAsBggrBgEFBQcCARYgaHR0cDov -L3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIBACe6 -45R88a7A3hfm5djV9VSwg/S7zV4Fe0+fdWavPOhWfvxyeDgD2StiGwC5+OlgzczO -UYrHUDFu4Up+GC9pWbY9ZIEr44OE5iKHjn3g7gKZYbge9LgriBIWhMIxkziWMaa5 -O1M/wySTVltpkuzFwbs4AOPsF6m43Md8AYOfMke6UiI0HTJ6CVanfCU2qT1L2sCC -bwq7EsiHSycR+R4tx5M/nttfJmtS2S6K8RTGRI0Vqbe/vd6mGu6uLftIdxf+u+yv -GPUqUfA5hJeVbG4bwyvEdGB5JbAKJ9/fXtI5z0V9QkvfsywexcZdylU6oJxpmo/a -77KwPJ+HbBIrZXAVUjEaJM9vMSNQH4xPjyPDdEFjHFWoFN0+4FFQz/EbMFYOkrCC -hdiDyyJkvC24JdVUorgG6q2SpCSgwYa1ShNqR88uC1aVVMvOmttqtKay20EIhid3 -92qgQmwLOM7XdVAyksLfKzAiSNDVQTglXaTpXZ/GlHXQRf0wl0OPkKsKx4ZzYEpp -Ld6leNcG2mqeSz53OiATIgHQv2ieY2BrNU0LbbqhPcCT4H8js1WtciVORvnSFu+w -ZMEBnunKoGqYDs/YYPIvSbjkQuE4NRb0yG5P94FW6LqjviOvrv1vA+ACOzB2+htt -Qc8Bsem4yWb02ybzOqR08kkkW8mw0FfB+j564ZfJ ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFwTCCA6mgAwIBAgIITrIAZwwDXU8wDQYJKoZIhvcNAQEFBQAwSTELMAkGA1UE -BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEjMCEGA1UEAxMaU3dpc3NTaWdu -IFBsYXRpbnVtIENBIC0gRzIwHhcNMDYxMDI1MDgzNjAwWhcNMzYxMDI1MDgzNjAw -WjBJMQswCQYDVQQGEwJDSDEVMBMGA1UEChMMU3dpc3NTaWduIEFHMSMwIQYDVQQD -ExpTd2lzc1NpZ24gUGxhdGludW0gQ0EgLSBHMjCCAiIwDQYJKoZIhvcNAQEBBQAD -ggIPADCCAgoCggIBAMrfogLi2vj8Bxax3mCq3pZcZB/HL37PZ/pEQtZ2Y5Wu669y -IIpFR4ZieIbWIDkm9K6j/SPnpZy1IiEZtzeTIsBQnIJ71NUERFzLtMKfkr4k2Htn -IuJpX+UFeNSH2XFwMyVTtIc7KZAoNppVRDBopIOXfw0enHb/FZ1glwCNioUD7IC+ -6ixuEFGSzH7VozPY1kneWCqv9hbrS3uQMpe5up1Y8fhXSQQeol0GcN1x2/ndi5ob -jM89o03Oy3z2u5yg+gnOI2Ky6Q0f4nIoj5+saCB9bzuohTEJfwvH6GXp43gOCWcw -izSC+13gzJ2BbWLuCB4ELE6b7P6pT1/9aXjvCR+htL/68++QHkwFix7qepF6w9fl -+zC8bBsQWJj3Gl/QKTIDE0ZNYWqFTFJ0LwYfexHihJfGmfNtf9dng34TaNhxKFrY -zt3oEBSa/m0jh26OWnA81Y0JAKeqvLAxN23IhBQeW71FYyBrS3SMvds6DsHPWhaP -pZjydomyExI7C3d3rLvlPClKknLKYRorXkzig3R3+jVIeoVNjZpTxN94ypeRSCtF -KwH3HBqi7Ri6Cr2D+m+8jVeTO9TUps4e8aCxzqv9KyiaTxvXw3LbpMS/XUz13XuW -ae5ogObnmLo2t/5u7Su9IPhlGdpVCX4l3P5hYnL5fhgC72O00Puv5TtjjGePAgMB -AAGjgawwgakwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0O -BBYEFFCvzAeHFUdvOMW0ZdHelarp35zMMB8GA1UdIwQYMBaAFFCvzAeHFUdvOMW0 -ZdHelarp35zMMEYGA1UdIAQ/MD0wOwYJYIV0AVkBAQEBMC4wLAYIKwYBBQUHAgEW -IGh0dHA6Ly9yZXBvc2l0b3J5LnN3aXNzc2lnbi5jb20vMA0GCSqGSIb3DQEBBQUA -A4ICAQAIhab1Fgz8RBrBY+D5VUYI/HAcQiiWjrfFwUF1TglxeeVtlspLpYhg0DB0 -uMoI3LQwnkAHFmtllXcBrqS3NQuB2nEVqXQXOHtYyvkv+8Bldo1bAbl93oI9ZLi+ -FHSjClTTLJUYFzX1UWs/j6KWYTl4a0vlpqD4U99REJNi54Av4tHgvI42Rncz7Lj7 -jposiU0xEQ8mngS7twSNC/K5/FqdOxa3L8iYq/6KUFkuozv8KV2LwUvJ4ooTHbG/ -u0IdUt1O2BReEMYxB+9xJ/cbOQncguqLs5WGXv312l0xpuAxtpTmREl0xRbl9x8D -YSjFyMsSoEJL+WuICI20MhjzdZ/EfwBPBZWcoxcCw7NTm6ogOSkrZvqdr16zktK1 -puEa+S1BaYEUtLS17Yk9zvupnTVCRLEcFHOBzyoBNZox1S2PbYTfgE1X4z/FhHXa -icYwu+uPyyIIoK6q8QNsOktNCaUOcsZWayFCTiMlFGiudgp8DAdwZPmaL/YFOSbG -DI8Zf0NebvRbFS/bYV3mZy8/CJT5YLSYMdp08YSTcU1f+2BY0fvEwW2JorsgH51x -kcsymxM9Pn2SUjWskpSi0xjCfMfqr3YFFt1nJ8J+HAciIfNAChs0B0QTwoRqjt8Z -Wr9/6x3iGjjRXK9HkmuAtTClyY3YqzGBH9/CZjfTk6mFhnll0g== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFvTCCA6WgAwIBAgIITxvUL1S7L0swDQYJKoZIhvcNAQEFBQAwRzELMAkGA1UE -BhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMYU3dpc3NTaWdu -IFNpbHZlciBDQSAtIEcyMB4XDTA2MTAyNTA4MzI0NloXDTM2MTAyNTA4MzI0Nlow -RzELMAkGA1UEBhMCQ0gxFTATBgNVBAoTDFN3aXNzU2lnbiBBRzEhMB8GA1UEAxMY -U3dpc3NTaWduIFNpbHZlciBDQSAtIEcyMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A -MIICCgKCAgEAxPGHf9N4Mfc4yfjDmUO8x/e8N+dOcbpLj6VzHVxumK4DV644N0Mv -Fz0fyM5oEMF4rhkDKxD6LHmD9ui5aLlV8gREpzn5/ASLHvGiTSf5YXu6t+WiE7br -YT7QbNHm+/pe7R20nqA1W6GSy/BJkv6FCgU+5tkL4k+73JU3/JHpMjUi0R86TieF -nbAVlDLaYQ1HTWBCrpJH6INaUFjpiou5XaHc3ZlKHzZnu0jkg7Y360g6rw9njxcH -6ATK72oxh9TAtvmUcXtnZLi2kUpCe2UuMGoM9ZDulebyzYLs2aFK7PayS+VFheZt -eJMELpyCbTapxDFkH4aDCyr0NQp4yVXPQbBH6TCfmb5hqAaEuSh6XzjZG6k4sIN/ -c8HDO0gqgg8hm7jMqDXDhBuDsz6+pJVpATqJAHgE2cn0mRmrVn5bi4Y5FZGkECwJ -MoBgs5PAKrYYC51+jUnyEEp/+dVGLxmSo5mnJqy7jDzmDrxHB9xzUfFwZC8I+bRH -HTBsROopN4WSaGa8gzj+ezku01DwH/teYLappvonQfGbGHLy9YR0SslnxFSuSGTf -jNFusB3hB48IHpmccelM2KX3RxIfdNFRnobzwqIjQAtz20um53MGjMGg6cFZrEb6 -5i/4z3GcRm25xBWNOHkDRUjvxF3XCO6HOSKGsg0PWEP3calILv3q1h8CAwEAAaOB -rDCBqTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU -F6DNweRBtjpbO8tFnb0cwpj6hlgwHwYDVR0jBBgwFoAUF6DNweRBtjpbO8tFnb0c -wpj6hlgwRgYDVR0gBD8wPTA7BglghXQBWQEDAQEwLjAsBggrBgEFBQcCARYgaHR0 -cDovL3JlcG9zaXRvcnkuc3dpc3NzaWduLmNvbS8wDQYJKoZIhvcNAQEFBQADggIB -AHPGgeAn0i0P4JUw4ppBf1AsX19iYamGamkYDHRJ1l2E6kFSGG9YrVBWIGrGvShp -WJHckRE1qTodvBqlYJ7YH39FkWnZfrt4csEGDyrOj4VwYaygzQu4OSlWhDJOhrs9 -xCrZ1x9y7v5RoSJBsXECYxqCsGKrXlcSH9/L3XWgwF15kIwb4FDm3jH+mHtwX6WQ -2K34ArZv02DdQEsixT2tOnqfGhpHkXkzuoLcMmkDlm4fS/Bx/uNncqCxv1yL5PqZ -IseEuRuNI5c/7SXgz2W79WEE790eslpBIlqhn10s6FvJbakMDHiqYMZWjwFaDGi8 -aRl5xB9+lwW/xekkUV7U1UtT7dkjWjYDZaPBA61BMPNGG4WQr2W11bHkFlt4dR2X -em1ZqSqPe97Dh4kQmUlzeMg9vVE1dCrV8X5pGyq7O70luJpaPXJhkGaH7gzWTdQR -dAtq/gsD/KNVV4n+SsuuWxcFyPKNIzFTONItaj+CuY0IavdeQXRuwxF+B6wpYJE/ -OMpXEA29MC/HpeZBoNquBYeaoKRlbEwJDIm6uNO5wJOKMPqN5ZprFQFOZ6raYlY+ -hAhm0sQ2fac+EPyI4NSA5QC9qvNOBqN6avlicuMJT+ubDgEj8Z+7fNzcbBGXJbLy -tGMU0gYqZ4yD9c7qB9iaah7s5Aq7KkzrCWA5zspi2C5u ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIF2TCCA8GgAwIBAgIQHp4o6Ejy5e/DfEoeWhhntjANBgkqhkiG9w0BAQsFADBk -MQswCQYDVQQGEwJjaDERMA8GA1UEChMIU3dpc3Njb20xJTAjBgNVBAsTHERpZ2l0 -YWwgQ2VydGlmaWNhdGUgU2VydmljZXMxGzAZBgNVBAMTElN3aXNzY29tIFJvb3Qg -Q0EgMjAeFw0xMTA2MjQwODM4MTRaFw0zMTA2MjUwNzM4MTRaMGQxCzAJBgNVBAYT -AmNoMREwDwYDVQQKEwhTd2lzc2NvbTElMCMGA1UECxMcRGlnaXRhbCBDZXJ0aWZp -Y2F0ZSBTZXJ2aWNlczEbMBkGA1UEAxMSU3dpc3Njb20gUm9vdCBDQSAyMIICIjAN -BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlUJOhJ1R5tMJ6HJaI2nbeHCOFvEr -jw0DzpPMLgAIe6szjPTpQOYXTKueuEcUMncy3SgM3hhLX3af+Dk7/E6J2HzFZ++r -0rk0X2s682Q2zsKwzxNoysjL67XiPS4h3+os1OD5cJZM/2pYmLcX5BtS5X4HAB1f -2uY+lQS3aYg5oUFgJWFLlTloYhyxCwWJwDaCFCE/rtuh/bxvHGCGtlOUSbkrRsVP -ACu/obvLP+DHVxxX6NZp+MEkUp2IVd3Chy50I9AU/SpHWrumnf2U5NGKpV+GY3aF -y6//SSj8gO1MedK75MDvAe5QQQg1I3ArqRa0jG6F6bYRzzHdUyYb3y1aSgJA/MTA -tukxGggo5WDDH8SQjhBiYEQN7Aq+VRhxLKX0srwVYv8c474d2h5Xszx+zYIdkeNL -6yxSNLCK/RJOlrDrcH+eOfdmQrGrrFLadkBXeyq96G4DsguAhYidDMfCd7Camlf0 -uPoTXGiTOmekl9AbmbeGMktg2M7v0Ax/lZ9vh0+Hio5fCHyqW/xavqGRn1V9TrAL -acywlKinh/LTSlDcX3KwFnUey7QYYpqwpzmqm59m2I2mbJYV4+by+PGDYmy7Velh -k6M99bFXi08jsJvllGov34zflVEpYKELKeRcVVi3qPyZ7iVNTA6z00yPhOgpD/0Q -VAKFyPnlw4vP5w8CAwEAAaOBhjCBgzAOBgNVHQ8BAf8EBAMCAYYwHQYDVR0hBBYw -FDASBgdghXQBUwIBBgdghXQBUwIBMBIGA1UdEwEB/wQIMAYBAf8CAQcwHQYDVR0O -BBYEFE0mICKJS9PVpAqhb97iEoHF8TwuMB8GA1UdIwQYMBaAFE0mICKJS9PVpAqh -b97iEoHF8TwuMA0GCSqGSIb3DQEBCwUAA4ICAQAyCrKkG8t9voJXiblqf/P0wS4R -fbgZPnm3qKhyN2abGu2sEzsOv2LwnN+ee6FTSA5BesogpxcbtnjsQJHzQq0Qw1zv -/2BZf82Fo4s9SBwlAjxnffUy6S8w5X2lejjQ82YqZh6NM4OKb3xuqFp1mrjX2lhI -REeoTPpMSQpKwhI3qEAMw8jh0FcNlzKVxzqfl9NX+Ave5XLzo9v/tdhZsnPdTSpx -srpJ9csc1fV5yJmz/MFMdOO0vSk3FQQoHt5FRnDsr7p4DooqzgB53MBfGWcsa0vv -aGgLQ+OswWIJ76bdZWGgr4RVSJFSHMYlkSrQwSIjYVmvRRGFHQEkNI/Ps/8XciAT -woCqISxxOQ7Qj1zB09GOInJGTB2Wrk9xseEFKZZZ9LuedT3PDTcNYtsmjGOpI99n -Bjx8Oto0QuFmtEYE3saWmA9LSHokMnWRn6z3aOkquVVlzl1h0ydw2Df+n7mvoC5W -t6NlUe07qxS/TFED6F+KBZvuim6c779o+sjaC+NCydAXFJy3SuCvkychVSa1ZC+N -8f+mQAWFBVzKBxlcCxMoTFh/wqXvRdpg065lYZ1Tg3TCrvJcwhbtkj6EPnNgiLx2 -9CzP0H1907he0ZESEOnN3col49XtmS++dYFLJPlFRpTJKSFTnCZFqhMX5OfNeOI5 -wSsSnqaeG8XmDtkx2Q== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICqDCCAi2gAwIBAgIQIW4zpcvTiKRvKQe0JzzE2DAKBggqhkjOPQQDAzCBlDEL -MAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYD -VQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBD -bGFzcyAxIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0g -RzQwHhcNMTExMDA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBlDELMAkGA1UEBhMC -VVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZTeW1h -bnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBDbGFzcyAxIFB1 -YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcq -hkjOPQIBBgUrgQQAIgNiAATXZrUb266zYO5G6ohjdTsqlG3zXxL24w+etgoUU0hS -yNw6s8tIICYSTvqJhNTfkeQpfSgB2dsYQ2mhH7XThhbcx39nI9/fMTGDAzVwsUu3 -yBe7UcvclBfb6gk7dhLeqrWjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E -BTADAQH/MB0GA1UdDgQWBBRlwI0l9Qy6l3eQP54u4Fr1ztXh5DAKBggqhkjOPQQD -AwNpADBmAjEApa7jRlP4mDbjIvouKEkN7jB+M/PsP3FezFWJeJmssv3cHFwzjim5 -axfIEWi13IMHAjEAnMhE2mnCNsNUGRCFAtqdR+9B52wmnQk9922Q0QVEL7C8g5No -8gxFSTm/mQQc0xCg ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID9jCCAt6gAwIBAgIQJDJ18h0v0gkz97RqytDzmDANBgkqhkiG9w0BAQsFADCB -lDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8w -HQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRl -YyBDbGFzcyAxIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 -IC0gRzYwHhcNMTExMDE4MDAwMDAwWhcNMzcxMjAxMjM1OTU5WjCBlDELMAkGA1UE -BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZT -eW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBDbGFzcyAx -IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzYwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHOddJZKmZgiJM6kXZBxbje/SD -6Jlz+muxNuCad6BAwoGNAcfMjL2Pffd543pMA03Z+/2HOCgs3ZqLVAjbZ/sbjP4o -ki++t7JIp4Gh2F6Iw8w5QEFa0dzl2hCfL9oBTf0uRnz5LicKaTfukaMbasxEvxvH -w9QRslBglwm9LiL1QYRmn81ApqkAgMEflZKf3vNI79sdd2H8f9/ulqRy0LY+/3gn -r8uSFWkI22MQ4uaXrG7crPaizh5HmbmJtxLmodTNWRFnw2+F2EJOKL5ZVVkElauP -N4C/DfD8HzpkMViBeNfiNfYgPym4jxZuPkjctUwH4fIa6n4KedaovetdhitNAgMB -AAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW -BBQzQejIORIVk0jyljIuWvXalF9TYDANBgkqhkiG9w0BAQsFAAOCAQEAFeNzV7EX -tl9JaUSm9l56Z6zS3nVJq/4lVcc6yUQVEG6/MWvL2QeTfxyFYwDjMhLgzMv7OWyP -4lPiPEAz2aSMR+atWPuJr+PehilWNCxFuBL6RIluLRQlKCQBZdbqUqwFblYSCT3Q -dPTXvQbKqDqNVkL6jXI+dPEDct+HG14OelWWLDi3mIXNTTNEyZSPWjEwN0ujOhKz -5zbRIWhLLTjmU64cJVYIVgNnhJ3Gw84kYsdMNs+wBkS39V8C3dlU6S+QTnrIToNA -DJqXPDe/v+z28LSFdyjBC8hnghAXOKK3Buqbvzr46SMHv3TgmDgVVXjucgBcGaP0 -0jPg/73RVDkpDw== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICqDCCAi2gAwIBAgIQNBdlEkA7t1aALYDLeVWmHjAKBggqhkjOPQQDAzCBlDEL -MAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYD -VQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBD -bGFzcyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0g -RzQwHhcNMTExMDA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBlDELMAkGA1UEBhMC -VVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZTeW1h -bnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBDbGFzcyAyIFB1 -YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzQwdjAQBgcq -hkjOPQIBBgUrgQQAIgNiAATR2UqOTA2ESlG6fO/TzPo6mrWnYxM9AeBJPvrBR8mS -szrX/m+c95o6D/UOCgrDP8jnEhSO1dVtmCyzcTIK6yq99tdqIAtnRZzSsr9TImYJ -XdsR8/EFM1ij4rjPfM2Cm72jQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E -BTADAQH/MB0GA1UdDgQWBBQ9MvM6qQyQhPmijGkGYVQvh3L+BTAKBggqhkjOPQQD -AwNpADBmAjEAyKapr0F/tckRQhZoaUxcuCcYtpjxwH+QbYfTjEYX8D5P/OqwCMR6 -S7wIL8fip29lAjEA1lnehs5fDspU1cbQFQ78i5Ry1I4AWFPPfrFLDeVQhuuea9// -KabYR9mglhjb8kWz ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIID9jCCAt6gAwIBAgIQZIKe/DcedF38l/+XyLH/QTANBgkqhkiG9w0BAQsFADCB -lDELMAkGA1UEBhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8w -HQYDVQQLExZTeW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRl -YyBDbGFzcyAyIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 -IC0gRzYwHhcNMTExMDE4MDAwMDAwWhcNMzcxMjAxMjM1OTU5WjCBlDELMAkGA1UE -BhMCVVMxHTAbBgNVBAoTFFN5bWFudGVjIENvcnBvcmF0aW9uMR8wHQYDVQQLExZT -eW1hbnRlYyBUcnVzdCBOZXR3b3JrMUUwQwYDVQQDEzxTeW1hbnRlYyBDbGFzcyAy -IFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzYwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNzOkFyGOFyz9AYxe9GPo15gRn -V2WYKaRPyVyPDzTS+NqoE2KquB5QZ3iwFkygOakVeq7t0qLA8JA3KRgmXOgNPLZs -ST/B4NzZS7YUGQum05bh1gnjGSYc+R9lS/kaQxwAg9bQqkmi1NvmYji6UBRDbfkx -+FYW2TgCkc/rbN27OU6Z4TBnRfHU8I3D3/7yOAchfQBeVkSz5GC9kSucq1sEcg+y -KNlyqwUgQiWpWwNqIBDMMfAr2jUs0Pual07wgksr2F82owstr2MNHSV/oW5cYqGN -KD6h/Bwg+AEvulWaEbAZ0shQeWsOagXXqgQ2sqPy4V93p3ec5R7c6d9qwWVdAgMB -AAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW -BBSHjCCVyJhK0daABkqQNETfHE2/sDANBgkqhkiG9w0BAQsFAAOCAQEAgY6ypWaW -tyGltu9vI1pf24HFQqV4wWn99DzX+VxrcHIa/FqXTQCAiIiCisNxDY7FiZss7Y0L -0nJU9X3UXENX6fOupQIR9nYrgVfdfdp0MP1UR/bgFm6mtApI5ud1Bw8pGTnOefS2 -bMVfmdUfS/rfbSw8DVSAcPCIC4DPxmiiuB1w2XaM/O6lyc+tHc+ZJVdaYkXLFmu9 -Sc2lo4xpeSWuuExsi0BmSxY/zwIa3eFsawdhanYVKZl/G92IgMG/tY9zxaaWI4Sm -KIYkM2oBLldzJbZev4/mHWGoQClnHYebHX+bn5nNMdZUvmK7OaxoEkiRIKXLsd3+ -b/xa5IJVWa8xqQ== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx -KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd -BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl -YyBHbG9iYWxSb290IENsYXNzIDIwHhcNMDgxMDAxMTA0MDE0WhcNMzMxMDAxMjM1 -OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy -aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 -ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDIwggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqX9obX+hzkeXaXPSi5kfl82hVYAUd -AqSzm1nzHoqvNK38DcLZSBnuaY/JIPwhqgcZ7bBcrGXHX+0CfHt8LRvWurmAwhiC -FoT6ZrAIxlQjgeTNuUk/9k9uN0goOA/FvudocP05l03Sx5iRUKrERLMjfTlH6VJi -1hKTXrcxlkIF+3anHqP1wvzpesVsqXFP6st4vGCvx9702cu+fjOlbpSD8DT6Iavq -jnKgP6TeMFvvhk1qlVtDRKgQFRzlAVfFmPHmBiiRqiDFt1MmUUOyCxGVWOHAD3bZ -wI18gfNycJ5v/hqO2V81xrJvNHy+SE/iWjnX2J14np+GPgNeGYtEotXHAgMBAAGj -QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS/ -WSA2AHmgoCJrjNXyYdK4LMuCSjANBgkqhkiG9w0BAQsFAAOCAQEAMQOiYQsfdOhy -NsZt+U2e+iKo4YFWz827n+qrkRk4r6p8FU3ztqONpfSO9kSpp+ghla0+AGIWiPAC -uvxhI+YzmzB6azZie60EI4RYZeLbK4rnJVM3YlNfvNoBYimipidx5joifsFvHZVw -IEoHNN/q/xWA5brXethbdXwFeilHfkCoMRN3zUA7tFFHei4R40cR3p1m0IvVVGb6 -g1XqfMIpiRvpb7PO4gWEyS8+eIVibslfwXhjdFjASBgMmTnrpMwatXlajRWc2BQN -9noHV8cigwUtPJslJj0Ys6lDfMjIq2SPDqO/nBudMNva0Bkuqjzx+zOAduTNrRlP -BSeOE6Fuwg== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDwzCCAqugAwIBAgIBATANBgkqhkiG9w0BAQsFADCBgjELMAkGA1UEBhMCREUx -KzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnByaXNlIFNlcnZpY2VzIEdtYkgxHzAd -BgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50ZXIxJTAjBgNVBAMMHFQtVGVsZVNl -YyBHbG9iYWxSb290IENsYXNzIDMwHhcNMDgxMDAxMTAyOTU2WhcNMzMxMDAxMjM1 -OTU5WjCBgjELMAkGA1UEBhMCREUxKzApBgNVBAoMIlQtU3lzdGVtcyBFbnRlcnBy -aXNlIFNlcnZpY2VzIEdtYkgxHzAdBgNVBAsMFlQtU3lzdGVtcyBUcnVzdCBDZW50 -ZXIxJTAjBgNVBAMMHFQtVGVsZVNlYyBHbG9iYWxSb290IENsYXNzIDMwggEiMA0G -CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC9dZPwYiJvJK7genasfb3ZJNW4t/zN -8ELg63iIVl6bmlQdTQyK9tPPcPRStdiTBONGhnFBSivwKixVA9ZIw+A5OO3yXDw/ -RLyTPWGrTs0NvvAgJ1gORH8EGoel15YUNpDQSXuhdfsaa3Ox+M6pCSzyU9XDFES4 -hqX2iys52qMzVNn6chr3IhUciJFrf2blw2qAsCTz34ZFiP0Zf3WHHx+xGwpzJFu5 -ZeAsVMhg02YXP+HMVDNzkQI6pn97djmiH5a2OK61yJN0HZ65tOVgnS9W0eDrXltM -EnAMbEQgqxHY9Bn20pxSN+f6tsIxO0rUFJmtxxr1XV/6B7h8DR/Wgx6zAgMBAAGj -QjBAMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBS1 -A/d2O2GCahKqGFPrAyGUv/7OyjANBgkqhkiG9w0BAQsFAAOCAQEAVj3vlNW92nOy -WL6ukK2YJ5f+AbGwUgC4TeQbIXQbfsDuXmkqJa9c1h3a0nnJ85cp4IaH3gRZD/FZ -1GSFS5mvJQQeyUapl96Cshtwn5z2r3Ex3XsFpSzTucpH9sry9uetuUg/vBa3wW30 -6gmv7PO15wWeph6KU1HWk4HMdJP2udqmJQV0eVp+QD6CSyYRMG7hP0HHRwA11fXT -91Q+gT3aSWqas+8QPebrb9HIIkfLzM8BMZLZGOMivgkeGj5asuRrDFR6fUNOuIml -e9eiPZaGzPImNC1qkp2aGtAw4l1OBLBfiyB+d8E9lYLRRpo7PHi4b6HQDWSieB4p -TpPDpFQUWw== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQsFADCB0jELMAkGA1UEBhMCVFIx -GDAWBgNVBAcTD0dlYnplIC0gS29jYWVsaTFCMEAGA1UEChM5VHVya2l5ZSBCaWxp -bXNlbCB2ZSBUZWtub2xvamlrIEFyYXN0aXJtYSBLdXJ1bXUgLSBUVUJJVEFLMS0w -KwYDVQQLEyRLYW11IFNlcnRpZmlrYXN5b24gTWVya2V6aSAtIEthbXUgU00xNjA0 -BgNVBAMTLVRVQklUQUsgS2FtdSBTTSBTU0wgS29rIFNlcnRpZmlrYXNpIC0gU3Vy -dW0gMTAeFw0xMzExMjUwODI1NTVaFw00MzEwMjUwODI1NTVaMIHSMQswCQYDVQQG -EwJUUjEYMBYGA1UEBxMPR2ViemUgLSBLb2NhZWxpMUIwQAYDVQQKEzlUdXJraXll -IEJpbGltc2VsIHZlIFRla25vbG9qaWsgQXJhc3Rpcm1hIEt1cnVtdSAtIFRVQklU -QUsxLTArBgNVBAsTJEthbXUgU2VydGlmaWthc3lvbiBNZXJrZXppIC0gS2FtdSBT -TTE2MDQGA1UEAxMtVFVCSVRBSyBLYW11IFNNIFNTTCBLb2sgU2VydGlmaWthc2kg -LSBTdXJ1bSAxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr3UwM6q7 -a9OZLBI3hNmNe5eA027n/5tQlT6QlVZC1xl8JoSNkvoBHToP4mQ4t4y86Ij5iySr -LqP1N+RAjhgleYN1Hzv/bKjFxlb4tO2KRKOrbEz8HdDc72i9z+SqzvBV96I01INr -N3wcwv61A+xXzry0tcXtAA9TNypN9E8Mg/uGz8v+jE69h/mniyFXnHrfA2eJLJ2X -YacQuFWQfw4tJzh03+f92k4S400VIgLI4OD8D62K18lUUMw7D8oWgITQUVbDjlZ/ -iSIzL+aFCr2lqBs23tPcLG07xxO9WSMs5uWk99gL7eqQQESolbuT1dCANLZGeA4f -AJNG4e7p+exPFwIDAQABo0IwQDAdBgNVHQ4EFgQUZT/HiobGPN08VFw1+DrtUgxH -V8gwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL -BQADggEBACo/4fEyjq7hmFxLXs9rHmoJ0iKpEsdeV31zVmSAhHqT5Am5EM2fKifh -AHe+SMg1qIGf5LgsyX8OsNJLN13qudULXjS99HMpw+0mFZx+CFOKWI3QSyjfwbPf -IPP54+M638yclNhOT8NrF7f3cuitZjO1JVOr4PhMqZ398g26rrnZqsZr+ZO7rqu4 -lzwDGrpDxpa5RXI4s6ehlj2Re37AIVNMh+3yC1SVUZPVIqUNivGTDj5UDrDYyU7c -8jEyVupk+eq1nRZmQnLzf9OxMUP8pI4X8W0jq5Rm+K37DwhuJi1/FwcJsoz7UMCf -lo3Ptv0AnVoUmr8CRPXBwp8iXqIPoeM= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFQTCCAymgAwIBAgICDL4wDQYJKoZIhvcNAQELBQAwUTELMAkGA1UEBhMCVFcx -EjAQBgNVBAoTCVRBSVdBTi1DQTEQMA4GA1UECxMHUm9vdCBDQTEcMBoGA1UEAxMT -VFdDQSBHbG9iYWwgUm9vdCBDQTAeFw0xMjA2MjcwNjI4MzNaFw0zMDEyMzExNTU5 -NTlaMFExCzAJBgNVBAYTAlRXMRIwEAYDVQQKEwlUQUlXQU4tQ0ExEDAOBgNVBAsT -B1Jvb3QgQ0ExHDAaBgNVBAMTE1RXQ0EgR2xvYmFsIFJvb3QgQ0EwggIiMA0GCSqG -SIb3DQEBAQUAA4ICDwAwggIKAoICAQCwBdvI64zEbooh745NnHEKH1Jw7W2CnJfF -10xORUnLQEK1EjRsGcJ0pDFfhQKX7EMzClPSnIyOt7h52yvVavKOZsTuKwEHktSz -0ALfUPZVr2YOy+BHYC8rMjk1Ujoog/h7FsYYuGLWRyWRzvAZEk2tY/XTP3VfKfCh -MBwqoJimFb3u/Rk28OKRQ4/6ytYQJ0lM793B8YVwm8rqqFpD/G2Gb3PpN0Wp8DbH -zIh1HrtsBv+baz4X7GGqcXzGHaL3SekVtTzWoWH1EfcFbx39Eb7QMAfCKbAJTibc -46KokWofwpFFiFzlmLhxpRUZyXx1EcxwdE8tmx2RRP1WKKD+u4ZqyPpcC1jcxkt2 -yKsi2XMPpfRaAok/T54igu6idFMqPVMnaR1sjjIsZAAmY2E2TqNGtz99sy2sbZCi -laLOz9qC5wc0GZbpuCGqKX6mOL6OKUohZnkfs8O1CWfe1tQHRvMq2uYiN2DLgbYP -oA/pyJV/v1WRBXrPPRXAb94JlAGD1zQbzECl8LibZ9WYkTunhHiVJqRaCPgrdLQA -BDzfuBSO6N+pjWxnkjMdwLfS7JLIvgm/LCkFbwJrnu+8vyq8W8BQj0FwcYeyTbcE -qYSjMq+u7msXi7Kx/mzhkIyIqJdIzshNy/MGz19qCkKxHh53L46g5pIOBvwFItIm -4TFRfTLcDwIDAQABoyMwITAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB -/zANBgkqhkiG9w0BAQsFAAOCAgEAXzSBdu+WHdXltdkCY4QWwa6gcFGn90xHNcgL -1yg9iXHZqjNB6hQbbCEAwGxCGX6faVsgQt+i0trEfJdLjbDorMjupWkEmQqSpqsn -LhpNgb+E1HAerUf+/UqdM+DyucRFCCEK2mlpc3INvjT+lIutwx4116KD7+U4x6WF -H6vPNOw/KP4M8VeGTslV9xzU2KV9Bnpv1d8Q34FOIWWxtuEXeZVFBs5fzNxGiWNo -RI2T9GRwoD2dKAXDOXC4Ynsg/eTb6QihuJ49CcdP+yz4k3ZB3lLg4VfSnQO8d57+ -nile98FRYB/e2guyLXW3Q0iT5/Z5xoRdgFlglPx4mI88k1HtQJAH32RjJMtOcQWh -15QaiDLxInQirqWm2BJpTGCjAu4r7NRjkgtevi92a6O2JryPA9gK8kxkRr05YuWW -6zRjESjMlfGt7+/cgFhI6Uu46mWs6fyAtbXIRfmswZ/ZuepiiI7E8UuDEq3mi4TW -nsLrgxifarsbJGAzcMzs9zLzXNl5fe+epP7JI8Mk7hWSsT2RTyaGvWZzJBPqpK5j -wa19hAM8EHiGG3njxPPyBJUgriOCxLM6AGK/5jYk4Ve6xx6QddVfP5VhK8E7zeWz -aGHQRiapIVJpLesux+t3zqY6tQMzT3bR51xUAV3LePTJDL/PEo4XLSNolOer/qmy -KwbQBM0= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDezCCAmOgAwIBAgIBATANBgkqhkiG9w0BAQUFADBfMQswCQYDVQQGEwJUVzES -MBAGA1UECgwJVEFJV0FOLUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFU -V0NBIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwODI4MDcyNDMz -WhcNMzAxMjMxMTU1OTU5WjBfMQswCQYDVQQGEwJUVzESMBAGA1UECgwJVEFJV0FO -LUNBMRAwDgYDVQQLDAdSb290IENBMSowKAYDVQQDDCFUV0NBIFJvb3QgQ2VydGlm -aWNhdGlvbiBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB -AQCwfnK4pAOU5qfeCTiRShFAh6d8WWQUe7UREN3+v9XAu1bihSX0NXIP+FPQQeFE -AcK0HMMxQhZHhTMidrIKbw/lJVBPhYa+v5guEGcevhEFhgWQxFnQfHgQsIBct+HH -K3XLfJ+utdGdIzdjp9xCoi2SBBtQwXu4PhvJVgSLL1KbralW6cH/ralYhzC2gfeX -RfwZVzsrb+RH9JlF/h3x+JejiB03HFyP4HYlmlD4oFT/RJB2I9IyxsOrBr/8+7/z -rX2SYgJbKdM1o5OaQ2RgXbL6Mv87BK9NQGr5x+PvI/1ry+UPizgN7gr8/g+YnzAx -3WxSZfmLgb4i4RxYA7qRG4kHAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV -HRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqOFsmjd6LWvJPelSDGRjjCDWmujANBgkq -hkiG9w0BAQUFAAOCAQEAPNV3PdrfibqHDAhUaiBQkr6wQT25JmSDCi/oQMCXKCeC -MErJk/9q56YAf4lCmtYR5VPOL8zy2gXE/uJQxDqGfczafhAJO5I1KlOy/usrBdls -XebQ79NqZp4VKIV66IIArB6nCWlWQtNoURi+VJq/REG6Sb4gumlc7rh3zc5sH62D -lhh9DrUUOYTxKOkto557HnpyWoOzeW/vtPzQCqVYT0bf+215WfKEIlKuD8z7fDvn -aspHYcN6+NOSBB+4IIThNlQWx0DeO4pz3N/GCUzf7Nr/1FNCocnyYh0igzyXxfkZ -YiesZSLX0zzG5Y6yU8xJzrww/nsOM5D77dIUkR8Hrw== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFcjCCA1qgAwIBAgIQH51ZWtcvwgZEpYAIaeNe9jANBgkqhkiG9w0BAQUFADA/ -MQswCQYDVQQGEwJUVzEwMC4GA1UECgwnR292ZXJubWVudCBSb290IENlcnRpZmlj -YXRpb24gQXV0aG9yaXR5MB4XDTAyMTIwNTEzMjMzM1oXDTMyMTIwNTEzMjMzM1ow -PzELMAkGA1UEBhMCVFcxMDAuBgNVBAoMJ0dvdmVybm1lbnQgUm9vdCBDZXJ0aWZp -Y2F0aW9uIEF1dGhvcml0eTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB -AJoluOzMonWoe/fOW1mKydGGEghU7Jzy50b2iPN86aXfTEc2pBsBHH8eV4qNw8XR -IePaJD9IK/ufLqGU5ywck9G/GwGHU5nOp/UKIXZ3/6m3xnOUT0b3EEk3+qhZSV1q -gQdW8or5BtD3cCJNtLdBuTK4sfCxw5w/cP1T3YGq2GN49thTbqGsaoQkclSGxtKy -yhwOeYHWtXBiCAEuTk8O1RGvqa/lmr/czIdtJuTJV6L7lvnM4T9TjGxMfptTCAts -F/tnyMKtsc2AtJfcdgEWFelq16TheEfOhtX7MfP6Mb40qij7cEwdScevLJ1tZqa2 -jWR+tSBqnTuBto9AAGdLiYa4zGX+FVPpBMHWXx1E1wovJ5pGfaENda1UhhXcSTvx -ls4Pm6Dso3pdvtUqdULle96ltqqvKKyskKw4t9VoNSZ63Pc78/1Fm9G7Q3hub/FC -VGqY8A2tl+lSXunVanLeavcbYBT0peS2cWeqH+riTcFCQP5nRhc4L0c/cZyu5SHK -YS1tB6iEfC3uUSXxY5Ce/eFXiGvviiNtsea9P63RPZYLhY3Naye7twWb7LuRqQoH -EgKXTiCQ8P8NHuJBO9NAOueNXdpm5AKwB1KYXA6OM5zCppX7VRluTI6uSw+9wThN -Xo+EHWbNxWCWtFJaBYmOlXqYwZE8lSOyDvR5tMl8wUohAgMBAAGjajBoMB0GA1Ud -DgQWBBTMzO/MKWCkO7GStjz6MmKPrCUVOzAMBgNVHRMEBTADAQH/MDkGBGcqBwAE -MTAvMC0CAQAwCQYFKw4DAhoFADAHBgVnKgMAAAQUA5vwIhP/lSg209yewDL7MTqK -UWUwDQYJKoZIhvcNAQEFBQADggIBAECASvomyc5eMN1PhnR2WPWus4MzeKR6dBcZ -TulStbngCnRiqmjKeKBMmo4sIy7VahIkv9Ro04rQ2JyftB8M3jh+Vzj8jeJPXgyf -qzvS/3WXy6TjZwj/5cAWtUgBfen5Cv8b5Wppv3ghqMKnI6mGq3ZW6A4M9hPdKmaK -ZEk9GhiHkASfQlK3T8v+R0F2Ne//AHY2RTKbxkaFXeIksB7jSJaYV0eUVXoPQbFE -JPPB/hprv4j9wabak2BegUqZIJxIZhm1AHlUD7gsL0u8qV1bYH+Mh6XgUmMqvtg7 -hUAV/h62ZT/FS9p+tXo1KaMuephgIqP0fSdOLeq0dDzpD6QzDxARvBMB1uUO07+1 -EqLhRSPAzAhuYbeJq4PjJB7mXQfnHyA+z2fI56wwbSdLaG5LKlwCCDTb+HbkZ6Mm -nD+iMsJKxYEYMRBWqoTvLQr/uB930r+lWKBi5NdLkXWNiYCYfm3LU05er/ayl4WX -udpVBrkk7tfGOB5jGxI7leFYrPLfhNVfmS8NVVvmONsuP3LpSIXLuykTjx44Vbnz -ssQwmSNOXfJIoRIM3BKQCZBUkQM8R+XVyWXgt0t97EfTsws+rZ7QdAAO671RrcDe -LMDDav7v3Aun+kbfYNucpllQdSNpc5Oy+fwC00fmcc4QAu4njIT/rEUNE1yDMuAl -pYYsfPQS ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFODCCAyCgAwIBAgIRAJW+FqD3LkbxezmCcvqLzZYwDQYJKoZIhvcNAQEFBQAw -NzEUMBIGA1UECgwLVGVsaWFTb25lcmExHzAdBgNVBAMMFlRlbGlhU29uZXJhIFJv -b3QgQ0EgdjEwHhcNMDcxMDE4MTIwMDUwWhcNMzIxMDE4MTIwMDUwWjA3MRQwEgYD -VQQKDAtUZWxpYVNvbmVyYTEfMB0GA1UEAwwWVGVsaWFTb25lcmEgUm9vdCBDQSB2 -MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMK+6yfwIaPzaSZVfp3F -VRaRXP3vIb9TgHot0pGMYzHw7CTww6XScnwQbfQ3t+XmfHnqjLWCi65ItqwA3GV1 -7CpNX8GH9SBlK4GoRz6JI5UwFpB/6FcHSOcZrr9FZ7E3GwYq/t75rH2D+1665I+X -Z75Ljo1kB1c4VWk0Nj0TSO9P4tNmHqTPGrdeNjPUtAa9GAH9d4RQAEX1jF3oI7x+ -/jXh7VB7qTCNGdMJjmhnXb88lxhTuylixcpecsHHltTbLaC0H2kD7OriUPEMPPCs -81Mt8Bz17Ww5OXOAFshSsCPN4D7c3TxHoLs1iuKYaIu+5b9y7tL6pe0S7fyYGKkm -dtwoSxAgHNN/Fnct7W+A90m7UwW7XWjH1Mh1Fj+JWov3F0fUTPHSiXk+TT2YqGHe -Oh7S+F4D4MHJHIzTjU3TlTazN19jY5szFPAtJmtTfImMMsJu7D0hADnJoWjiUIMu -sDor8zagrC/kb2HCUQk5PotTubtn2txTuXZZNp1D5SDgPTJghSJRt8czu90VL6R4 -pgd7gUY2BIbdeTXHlSw7sKMXNeVzH7RcWe/a6hBle3rQf5+ztCo3O3CLm1u5K7fs -slESl1MpWtTwEhDcTwK7EpIvYtQ/aUN8Ddb8WHUBiJ1YFkveupD/RwGJBmr2X7KQ -arMCpgKIv7NHfirZ1fpoeDVNAgMBAAGjPzA9MA8GA1UdEwEB/wQFMAMBAf8wCwYD -VR0PBAQDAgEGMB0GA1UdDgQWBBTwj1k4ALP1j5qWDNXr+nuqF+gTEjANBgkqhkiG -9w0BAQUFAAOCAgEAvuRcYk4k9AwI//DTDGjkk0kiP0Qnb7tt3oNmzqjMDfz1mgbl -dxSR651Be5kqhOX//CHBXfDkH1e3damhXwIm/9fH907eT/j3HEbAek9ALCI18Bmx -0GtnLLCo4MBANzX2hFxc469CeP6nyQ1Q6g2EdvZR74NTxnr/DlZJLo961gzmJ1Tj -TQpgcmLNkQfWpb/ImWvtxBnmq0wROMVvMeJuScg/doAmAyYp4Db29iBT4xdwNBed -Y2gea+zDTYa4EzAvXUYNR0PVG6pZDrlcjQZIrXSHX8f8MVRBE+LHIQ6e4B4N4cB7 -Q4WQxYpYxmUKeFfyxiMPAdkgS94P+5KFdSpcc41teyWRyu5FrgZLAMzTsVlQ2jqI -OylDRl6XK1TOU2+NSueW+r9xDkKLfP0ooNBIytrEgUy7onOTJsjrDNYmiLbAJM+7 -vVvrdX3pCI6GMyx5dwlppYn8s3CQh3aP0yK7Qs69cwsgJirQmz1wHiRszYd2qReW -t88NkvuOGKmYSdGe/mBEciG5Ge3C9THxOUiIkCR1VBatzvT4aRRkOfujuLpwQMcn -HL/EVlP6Y2XQ8xwOFvVrhlhNGNTkDY6lnVuR3HYkUD/GKvvZt5y11ubQ2egZixVx -SK236thZiNSQvxaz2emsWWFUyBy6ysHK4bkgTI86k4mloMy/0/Z1pHWWbVY= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEIDCCAwigAwIBAgIJAISCLF8cYtBAMA0GCSqGSIb3DQEBCwUAMIGcMQswCQYD -VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk -MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U -cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxFzAVBgNVBAMMDlRydXN0Q29y -IEVDQS0xMB4XDTE2MDIwNDEyMzIzM1oXDTI5MTIzMTE3MjgwN1owgZwxCzAJBgNV -BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw -IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy -dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAwwOVHJ1c3RDb3Ig -RUNBLTEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPj+ARtZ+odnbb -3w9U73NjKYKtR8aja+3+XzP4Q1HpGjORMRegdMTUpwHmspI+ap3tDvl0mEDTPwOA -BoJA6LHip1GnHYMma6ve+heRK9jGrB6xnhkB1Zem6g23xFUfJ3zSCNV2HykVh0A5 -3ThFEXXQmqc04L/NyFIduUd+Dbi7xgz2c1cWWn5DkR9VOsZtRASqnKmcp0yJF4Ou -owReUoCLHhIlERnXDH19MURB6tuvsBzvgdAsxZohmz3tQjtQJvLsznFhBmIhVE5/ -wZ0+fyCMgMsq2JdiyIMzkX2woloPV+g7zPIlstR8L+xNxqE6FXrntl019fZISjZF -ZtS6mFjBAgMBAAGjYzBhMB0GA1UdDgQWBBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAf -BgNVHSMEGDAWgBREnkj1zG1I1KBLf/5ZJC+Dl5mahjAPBgNVHRMBAf8EBTADAQH/ -MA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsFAAOCAQEABT41XBVwm8nHc2Fv -civUwo/yQ10CzsSUuZQRg2dd4mdsdXa/uwyqNsatR5Nj3B5+1t4u/ukZMjgDfxT2 -AHMsWbEhBuH7rBiVDKP/mZb3Kyeb1STMHd3BOuCYRLDE5D53sXOpZCz2HAF8P11F -hcCF5yWPldwX8zyfGm6wyuMdKulMY/okYWLW2n62HGz1Ah3UKt1VkOsqEUc8Ll50 -soIipX1TH0XsJ5F95yIW6MBoNtjG8U+ARDL54dHRHareqKucBK+tIA5kmE2la8BI -WJZpTdwHjFGTot+fDz2LYLSCjaoITmJF4PkL0uDgPFveXHEnJcLmA4GLEFPjx1Wi -tJ/X5g== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEMDCCAxigAwIBAgIJANqb7HHzA7AZMA0GCSqGSIb3DQEBCwUAMIGkMQswCQYD -VQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEgQ2l0eTEk -MCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYDVQQLDB5U -cnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRydXN0Q29y -IFJvb3RDZXJ0IENBLTEwHhcNMTYwMjA0MTIzMjE2WhcNMjkxMjMxMTcyMzE2WjCB -pDELMAkGA1UEBhMCUEExDzANBgNVBAgMBlBhbmFtYTEUMBIGA1UEBwwLUGFuYW1h -IENpdHkxJDAiBgNVBAoMG1RydXN0Q29yIFN5c3RlbXMgUy4gZGUgUi5MLjEnMCUG -A1UECwweVHJ1c3RDb3IgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MR8wHQYDVQQDDBZU -cnVzdENvciBSb290Q2VydCBDQS0xMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB -CgKCAQEAv463leLCJhJrMxnHQFgKq1mqjQCj/IDHUHuO1CAmujIS2CNUSSUQIpid -RtLByZ5OGy4sDjjzGiVoHKZaBeYei0i/mJZ0PmnK6bV4pQa81QBeCQryJ3pS/C3V -seq0iWEk8xoT26nPUu0MJLq5nux+AHT6k61sKZKuUbS701e/s/OojZz0JEsq1pme -9J7+wH5COucLlVPat2gOkEz7cD+PSiyU8ybdY2mplNgQTsVHCJCZGxdNuWxu72CV -EY4hgLW9oHPY0LJ3xEXqWib7ZnZ2+AYfYW0PVcWDtxBWcgYHpfOxGgMFZA6dWorW -hnAbJN7+KIor0Gqw/Hqi3LJ5DotlDwIDAQABo2MwYTAdBgNVHQ4EFgQU7mtJPHo/ -DeOxCbeKyKsZn3MzUOcwHwYDVR0jBBgwFoAU7mtJPHo/DeOxCbeKyKsZn3MzUOcw -DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQAD -ggEBACUY1JGPE+6PHh0RU9otRCkZoB5rMZ5NDp6tPVxBb5UrJKF5mDo4Nvu7Zp5I -/5CQ7z3UuJu0h3U/IJvOcs+hVcFNZKIZBqEHMwwLKeXx6quj7LUKdJDHfXLy11yf -ke+Ri7fc7Waiz45mO7yfOgLgJ90WmMCV1Aqk5IGadZQ1nJBfiDcGrVmVCrDRZ9MZ -yonnMlo2HD6CqFqTvsbQZJG2z9m2GM/bftJlo6bEjhcxwft+dtvTheNYsnd6djts -L1Ac59v2Z3kf9YKVmgenFK+P3CghZwnS1k1aHBkcjndcw5QkPTJrS37UeJSDvjdN -zl/HHk484IkzlQsPpTLWPFp5LBk= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIGLzCCBBegAwIBAgIIJaHfyjPLWQIwDQYJKoZIhvcNAQELBQAwgaQxCzAJBgNV -BAYTAlBBMQ8wDQYDVQQIDAZQYW5hbWExFDASBgNVBAcMC1BhbmFtYSBDaXR5MSQw -IgYDVQQKDBtUcnVzdENvciBTeXN0ZW1zIFMuIGRlIFIuTC4xJzAlBgNVBAsMHlRy -dXN0Q29yIENlcnRpZmljYXRlIEF1dGhvcml0eTEfMB0GA1UEAwwWVHJ1c3RDb3Ig -Um9vdENlcnQgQ0EtMjAeFw0xNjAyMDQxMjMyMjNaFw0zNDEyMzExNzI2MzlaMIGk -MQswCQYDVQQGEwJQQTEPMA0GA1UECAwGUGFuYW1hMRQwEgYDVQQHDAtQYW5hbWEg -Q2l0eTEkMCIGA1UECgwbVHJ1c3RDb3IgU3lzdGVtcyBTLiBkZSBSLkwuMScwJQYD -VQQLDB5UcnVzdENvciBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxHzAdBgNVBAMMFlRy -dXN0Q29yIFJvb3RDZXJ0IENBLTIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK -AoICAQCnIG7CKqJiJJWQdsg4foDSq8GbZQWU9MEKENUCrO2fk8eHyLAnK0IMPQo+ -QVqedd2NyuCb7GgypGmSaIwLgQ5WoD4a3SwlFIIvl9NkRvRUqdw6VC0xK5mC8tkq -1+9xALgxpL56JAfDQiDyitSSBBtlVkxs1Pu2YVpHI7TYabS3OtB0PAx1oYxOdqHp -2yqlO/rOsP9+aij9JxzIsekp8VduZLTQwRVtDr4uDkbIXvRR/u8OYzo7cbrPb1nK -DOObXUm4TOJXsZiKQlecdu/vvdFoqNL0Cbt3Nb4lggjEFixEIFapRBF37120Hape -az6LMvYHL1cEksr1/p3C6eizjkxLAjHZ5DxIgif3GIJ2SDpxsROhOdUuxTTCHWKF -3wP+TfSvPd9cW436cOGlfifHhi5qjxLGhF5DUVCcGZt45vz27Ud+ez1m7xMTiF88 -oWP7+ayHNZ/zgp6kPwqcMWmLmaSISo5uZk3vFsQPeSghYA2FFn3XVDjxklb9tTNM -g9zXEJ9L/cb4Qr26fHMC4P99zVvh1Kxhe1fVSntb1IVYJ12/+CtgrKAmrhQhJ8Z3 -mjOAPF5GP/fDsaOGM8boXg25NSyqRsGFAnWAoOsk+xWq5Gd/bnc/9ASKL3x74xdh -8N0JqSDIvgmk0H5Ew7IwSjiqqewYmgeCK9u4nBit2uBGF6zPXQIDAQABo2MwYTAd -BgNVHQ4EFgQU2f4hQG6UnrybPZx9mCAZ5YwwYrIwHwYDVR0jBBgwFoAU2f4hQG6U -nrybPZx9mCAZ5YwwYrIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYw -DQYJKoZIhvcNAQELBQADggIBAJ5Fngw7tu/hOsh80QA9z+LqBrWyOrsGS2h60COX -dKcs8AjYeVrXWoSK2BKaG9l9XE1wxaX5q+WjiYndAfrs3fnpkpfbsEZC89NiqpX+ -MWcUaViQCqoL7jcjx1BRtPV+nuN79+TMQjItSQzL/0kMmx40/W5ulop5A7Zv2wnL -/V9lFDfhOPXzYRZY5LVtDQsEGz9QLX+zx3oaFoBg+Iof6Rsqxvm6ARppv9JYx1RX -CI/hOWB3S6xZhBqI8d3LT3jX5+EzLfzuQfogsL7L9ziUwOHQhQ+77Sxzq+3+knYa -ZH9bDTMJBzN7Bj8RpFxwPIXAz+OQqIN3+tvmxYxoZxBnpVIt8MSZj3+/0WvitUfW -2dCFmU2Umw9Lje4AWkcdEQOsQRivh7dvDDqPys/cA8GiCcjl/YBeyGBCARsaU1q7 -N6a3vLqE6R5sGtRk2tRD/pOLS/IseRYQ1JMLiI+h2IYURpFHmygk71dSTlxCnKr3 -Sewn6EAes6aJInKc9Q0ztFijMDvd1GpUk74aTfOTlPf8hAs/hCBcNANExdqtvArB -As8e5ZTZ845b2EzwnexhF7sUMlQMAimTHpKG9n/v55IFDlndmQguLvqcAFLTxWYp -5KeXRKQOKIETNcX2b2TmQcTVL8w0RSXPQQCWPUouwpaYT05KnJe32x+SMsj/D1Fu -1uwJ ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDZzCCAk+gAwIBAgIQGx+ttiD5JNM2a/fH8YygWTANBgkqhkiG9w0BAQUFADBF -MQswCQYDVQQGEwJHQjEYMBYGA1UEChMPVHJ1c3RpcyBMaW1pdGVkMRwwGgYDVQQL -ExNUcnVzdGlzIEZQUyBSb290IENBMB4XDTAzMTIyMzEyMTQwNloXDTI0MDEyMTEx -MzY1NFowRTELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1RydXN0aXMgTGltaXRlZDEc -MBoGA1UECxMTVHJ1c3RpcyBGUFMgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAMVQe547NdDfxIzNjpvto8A2mfRC6qc+gIMPpqdZh8mQRUN+ -AOqGeSoDvT03mYlmt+WKVoaTnGhLaASMk5MCPjDSNzoiYYkchU59j9WvezX2fihH -iTHcDnlkH5nSW7r+f2C/revnPDgpai/lkQtV/+xvWNUtyd5MZnGPDNcE2gfmHhjj -vSkCqPoc4Vu5g6hBSLwacY3nYuUtsuvffM/bq1rKMfFMIvMFE/eC+XN5DL7XSxzA -0RU8k0Fk0ea+IxciAIleH2ulrG6nS4zto3Lmr2NNL4XSFDWaLk6M6jKYKIahkQlB -OrTh4/L68MkKokHdqeMDx4gVOxzUGpTXn2RZEm0CAwEAAaNTMFEwDwYDVR0TAQH/ -BAUwAwEB/zAfBgNVHSMEGDAWgBS6+nEleYtXQSUhhgtx67JkDoshZzAdBgNVHQ4E -FgQUuvpxJXmLV0ElIYYLceuyZA6LIWcwDQYJKoZIhvcNAQEFBQADggEBAH5Y//01 -GX2cGE+esCu8jowU/yyg2kdbw++BLa8F6nRIW/M+TgfHbcWzk88iNVy2P3UnXwmW -zaD+vkAMXBJV+JOCyinpXj9WV4s4NvdFGkwozZ5BuO1WTISkQMi4sKUraXAEasP4 -1BIy+Q7DsdwyhEQsb8tGD+pmQQ9P8Vilpg0ND2HepZ5dfWWhPBfnqFVO76DH7cZE -f1T1o+CP8HxVIo8ptoGj4W1OLBuAZ+ytIJ8MYmHVl/9D7S3B2l0pKoU/rGXuhg8F -jZBf3+6f9L/uHfuY5H+QK4R4EA5sSVPvFVtlRkpdr7r7OnIdzfYliB6XzCGcKQEN -ZetX2fNXlrtIzYE= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFWjCCA0KgAwIBAgIQT9Irj/VkyDOeTzRYZiNwYDANBgkqhkiG9w0BAQsFADBH -MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNVBAMMHFVDQSBF -eHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwHhcNMTUwMzEzMDAwMDAwWhcNMzgxMjMx -MDAwMDAwWjBHMQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxJTAjBgNV -BAMMHFVDQSBFeHRlbmRlZCBWYWxpZGF0aW9uIFJvb3QwggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQCpCQcoEwKwmeBkqh5DFnpzsZGgdT6o+uM4AHrsiWog -D4vFsJszA1qGxliG1cGFu0/GnEBNyr7uaZa4rYEwmnySBesFK5pI0Lh2PpbIILvS -sPGP2KxFRv+qZ2C0d35qHzwaUnoEPQc8hQ2E0B92CvdqFN9y4zR8V05WAT558aop -O2z6+I9tTcg1367r3CTueUWnhbYFiN6IXSV8l2RnCdm/WhUFhvMJHuxYMjMR83dk -sHYf5BA1FxvyDrFspCqjc/wJHx4yGVMR59mzLC52LqGj3n5qiAno8geK+LLNEOfi -c0CTuwjRP+H8C5SzJe98ptfRr5//lpr1kXuYC3fUfugH0mK1lTnj8/FtDw5lhIpj -VMWAtuCeS31HJqcBCF3RiJ7XwzJE+oJKCmhUfzhTA8ykADNkUVkLo4KRel7sFsLz -KuZi2irbWWIQJUoqgQtHB0MGcIfS+pMRKXpITeuUx3BNr2fVUbGAIAEBtHoIppB/ -TuDvB0GHr2qlXov7z1CymlSvw4m6WC31MJixNnI5fkkE/SmnTHnkBVfblLkWU41G -sx2VYVdWf6/wFlthWG82UBEL2KwrlRYaDh8IzTY0ZRBiZtWAXxQgXy0MoHgKaNYs -1+lvK9JKBZP8nm9rZ/+I8U6laUpSNwXqxhaN0sSZ0YIrO7o1dfdRUVjzyAfd5LQD -fwIDAQABo0IwQDAdBgNVHQ4EFgQU2XQ65DA9DfcS3H5aBZ8eNJr34RQwDwYDVR0T -AQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcNAQELBQADggIBADaN -l8xCFWQpN5smLNb7rhVpLGsaGvdftvkHTFnq88nIua7Mui563MD1sC3AO6+fcAUR -ap8lTwEpcOPlDOHqWnzcSbvBHiqB9RZLcpHIojG5qtr8nR/zXUACE/xOHAbKsxSQ -VBcZEhrxH9cMaVr2cXj0lH2RC47skFSOvG+hTKv8dGT9cZr4QQehzZHkPJrgmzI5 -c6sq1WnIeJEmMX3ixzDx/BR4dxIOE/TdFpS/S2d7cFOFyrC78zhNLJA5wA3CXWvp -4uXViI3WLL+rG761KIcSF3Ru/H38j9CHJrAb+7lsq+KePRXBOy5nAliRn+/4Qh8s -t2j1da3Ptfb/EX3C8CSlrdP6oDyp+l3cpaDvRKS+1ujl5BOWF3sGPjLtx7dCvHaj -2GU4Kzg1USEODm8uNBNA4StnDG1KQTAYI1oyVZnJF+A83vbsea0rWBmirSwiGpWO -vpaQXUJXxPkUAzUrHC1RVwinOt4/5Mi0A3PCwSaAuwtCH60NryZy2sy+s6ODWA2C -xR9GUeOcGMyNm43sSet1UNWMKFnKdDTajAshqx7qG+XH/RU+wBeq+yNuJkbL+vmx -cmtpzyKEC2IPrNkZAJSidjzULZrtBJ4tBmIQN1IchXIbJ+XMxjHsN+xjWZsLHXbM -fjKaiJUINlK73nZfdklJrX+9ZSCyycErdhh2n1ax ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFRjCCAy6gAwIBAgIQXd+x2lqj7V2+WmUgZQOQ7zANBgkqhkiG9w0BAQsFADA9 -MQswCQYDVQQGEwJDTjERMA8GA1UECgwIVW5pVHJ1c3QxGzAZBgNVBAMMElVDQSBH -bG9iYWwgRzIgUm9vdDAeFw0xNjAzMTEwMDAwMDBaFw00MDEyMzEwMDAwMDBaMD0x -CzAJBgNVBAYTAkNOMREwDwYDVQQKDAhVbmlUcnVzdDEbMBkGA1UEAwwSVUNBIEds -b2JhbCBHMiBSb290MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxeYr -b3zvJgUno4Ek2m/LAfmZmqkywiKHYUGRO8vDaBsGxUypK8FnFyIdK+35KYmToni9 -kmugow2ifsqTs6bRjDXVdfkX9s9FxeV67HeToI8jrg4aA3++1NDtLnurRiNb/yzm -VHqUwCoV8MmNsHo7JOHXaOIxPAYzRrZUEaalLyJUKlgNAQLx+hVRZ2zA+te2G3/R -VogvGjqNO7uCEeBHANBSh6v7hn4PJGtAnTRnvI3HLYZveT6OqTwXS3+wmeOwcWDc -C/Vkw85DvG1xudLeJ1uK6NjGruFZfc8oLTW4lVYa8bJYS7cSN8h8s+1LgOGN+jIj -tm+3SJUIsUROhYw6AlQgL9+/V087OpAh18EmNVQg7Mc/R+zvWr9LesGtOxdQXGLY -D0tK3Cv6brxzks3sx1DoQZbXqX5t2Okdj4q1uViSukqSKwxW/YDrCPBeKW4bHAyv -j5OJrdu9o54hyokZ7N+1wxrrFv54NkzWbtA+FxyQF2smuvt6L78RHBgOLXMDj6Dl -NaBa4kx1HXHhOThTeEDMg5PXCp6dW4+K5OXgSORIskfNTip1KnvyIvbJvgmRlld6 -iIis7nCs+dwp4wwcOxJORNanTrAmyPPZGpeRaOrvjUYG0lZFWJo8DA+DuAUlwznP -O6Q0ibd5Ei9Hxeepl2n8pndntd978XplFeRhVmUCAwEAAaNCMEAwDgYDVR0PAQH/ -BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIHEjMz15DD/pQwIX4wV -ZyF0Ad/fMA0GCSqGSIb3DQEBCwUAA4ICAQATZSL1jiutROTL/7lo5sOASD0Ee/oj -L3rtNtqyzm325p7lX1iPyzcyochltq44PTUbPrw7tgTQvPlJ9Zv3hcU2tsu8+Mg5 -1eRfB70VVJd0ysrtT7q6ZHafgbiERUlMjW+i67HM0cOU2kTC5uLqGOiiHycFutfl -1qnN3e92mI0ADs0b+gO3joBYDic/UvuUospeZcnWhNq5NXHzJsBPd+aBJ9J3O5oU -b3n09tDh05S60FdRvScFDcH9yBIw7m+NESsIndTUv4BFFJqIRNow6rSn4+7vW4LV -PtateJLbXDzz2K36uGt/xDYotgIVilQsnLAXc47QN6MUPJiVAAwpBVueSUmxX8fj -y88nZY41F7dXyDDZQVu5FLbowg+UMaeUmMxq67XhJ/UQqAHojhJi6IjMtX9Gl8Cb -EGY4GjZGXyJoPd/JxhMnq1MGrKI8hgZlb7F+sSlEmqO6SWkoaY/X5V+tBIZkbxqg -DMUIYs6Ao9Dz7GjevjPHF1t/gMRMTLGmhIrDO7gJzRSBuhjjVFc2/tsvfEehOjPI -+Vg7RE+xygKJBJYoaMVLuCaJu9YzL1DV/pqJuhgyklTGW+Cd+V7lDSKb9triyCGy -YiGqhkCyLmTTX8jjfhFnRR8F/uOi77Oos/N9j/gMHyIfLXC0uAE0djAA5SN4p1bX -UB+K+wb1whnw0A== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICjzCCAhWgAwIBAgIQXIuZxVqUxdJxVt7NiYDMJjAKBggqhkjOPQQDAzCBiDEL -MAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNl -eSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMT -JVVTRVJUcnVzdCBFQ0MgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMjAx -MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgT -Ck5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVUaGUg -VVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBFQ0MgQ2VydGlm -aWNhdGlvbiBBdXRob3JpdHkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAQarFRaqflo -I+d61SRvU8Za2EurxtW20eZzca7dnNYMYf3boIkDuAUU7FfO7l0/4iGzzvfUinng -o4N+LZfQYcTxmdwlkWOrfzCjtHDix6EznPO/LlxTsV+zfTJ/ijTjeXmjQjBAMB0G -A1UdDgQWBBQ64QmG1M8ZwpZ2dEl23OA1xmNjmjAOBgNVHQ8BAf8EBAMCAQYwDwYD -VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAwNoADBlAjA2Z6EWCNzklwBBHU6+4WMB -zzuqQhFkoJ2UOQIReVx7Hfpkue4WQrO/isIJxOzksU0CMQDpKmFHjFJKS04YcPbW -RNZu9YO6bVi9JNlWSOrvxKJGgYhqOkbRqZtNyWHa0V1Xahg= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB -iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl -cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV -BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw -MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV -BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU -aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy -dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK -AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B -3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY -tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/ -Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2 -VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT -79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6 -c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT -Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l -c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee -UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE -Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd -BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G -A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF -Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO -VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3 -ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs -8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR -iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze -Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ -XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/ -qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB -VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB -L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG -jjxDah2nGN59PRbxYvnKkKj9 ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEojCCA4qgAwIBAgIQRL4Mi1AAJLQR0zYlJWfJiTANBgkqhkiG9w0BAQUFADCB -rjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug -Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho -dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xNjA0BgNVBAMTLVVUTi1VU0VSRmlyc3Qt -Q2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBFbWFpbDAeFw05OTA3MDkxNzI4NTBa -Fw0xOTA3MDkxNzM2NThaMIGuMQswCQYDVQQGEwJVUzELMAkGA1UECBMCVVQxFzAV -BgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5l -dHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cudXNlcnRydXN0LmNvbTE2MDQGA1UE -AxMtVVROLVVTRVJGaXJzdC1DbGllbnQgQXV0aGVudGljYXRpb24gYW5kIEVtYWls -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsjmFpPJ9q0E7YkY3rs3B -YHW8OWX5ShpHornMSMxqmNVNNRm5pELlzkniii8efNIxB8dOtINknS4p1aJkxIW9 -hVE1eaROaJB7HHqkkqgX8pgV8pPMyaQylbsMTzC9mKALi+VuG6JG+ni8om+rWV6l -L8/K2m2qL+usobNqqrcuZzWLeeEeaYji5kbNoKXqvgvOdjp6Dpvq/NonWz1zHyLm -SGHGTPNpsaguG7bUMSAsvIKKjqQOpdeJQ/wWWq8dcdcRWdq6hw2v+vPhwvCkxWeM -1tZUOt4KpLoDd7NlyP0e03RiqhjKaJMeoYV+9Udly/hNVyh00jT/MLbu9mIwFIws -6wIDAQABo4G5MIG2MAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud -DgQWBBSJgmd9xJ0mcABLtFBIfN49rgRufTBYBgNVHR8EUTBPME2gS6BJhkdodHRw -Oi8vY3JsLnVzZXJ0cnVzdC5jb20vVVROLVVTRVJGaXJzdC1DbGllbnRBdXRoZW50 -aWNhdGlvbmFuZEVtYWlsLmNybDAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH -AwQwDQYJKoZIhvcNAQEFBQADggEBALFtYV2mGn98q0rkMPxTbyUkxsrt4jFcKw7u -7mFVbwQ+zznexRtJlOTrIEy05p5QLnLZjfWqo7NK2lYcYJeA3IKirUq9iiv/Cwm0 -xtcgBEXkzYABurorbs6q15L+5K/r9CYdFip/bDCVNy8zEqx/3cfREYxRmLLQo5HQ -rfafnoOTHh1CuEava2bwm3/q4wMC5QJRwarVNZ1yQAOJujEdxRBoUp7fooXFXAim -eOZTT7Hot9MUnpOmw2TjrH5xzbyf6QMbzPvprDHBr3wVdAKZw7JHpsIyYdfHb0gk -USeh1YdV8nuPmD0Wnu51tvjQjvLzxq4oW6fw8zYX/MMF08oDSlQ= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDhDCCAwqgAwIBAgIQL4D+I4wOIg9IZxIokYesszAKBggqhkjOPQQDAzCByjEL -MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW -ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2ln -biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp -U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y -aXR5IC0gRzQwHhcNMDcxMTA1MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCByjELMAkG -A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZWZXJp -U2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNyBWZXJpU2lnbiwg -SW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJpU2ln -biBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 -IC0gRzQwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASnVnp8Utpkmw4tXNherJI9/gHm -GUo9FANL+mAnINmDiWn6VMaaGF5VKmTeBvaNSjutEDxlPZCIBIngMGGzrl0Bp3ve -fLK+ymVhAIau2o970ImtTR1ZmkGxvEeA3J5iw/mjgbIwga8wDwYDVR0TAQH/BAUw -AwEB/zAOBgNVHQ8BAf8EBAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJ -aW1hZ2UvZ2lmMCEwHzAHBgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYj -aHR0cDovL2xvZ28udmVyaXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFLMW -kf3upm7ktS5Jj4d4gYDs5bG1MAoGCCqGSM49BAMDA2gAMGUCMGYhDBgmYFo4e1ZC -4Kf8NoRRkSAsdk1DPcQdhCPQrNZ8NQbOzWm9kA3bbEhCHQ6qQgIxAJw9SDkjOVga -FRJZap7v1VmyHVIsmXHNxynfGyphe3HR3vPA5Q06Sqotp9iGKt0uEA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB -yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL -ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp -U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW -ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0 -aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL -MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW -ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln -biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp -U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y -aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1 -nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex -t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz -SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG -BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+ -rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/ -NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E -BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH -BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy -aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv -MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE -p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y -5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK -WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ -4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N -hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEuTCCA6GgAwIBAgIQQBrEZCGzEyEDDrvkEhrFHTANBgkqhkiG9w0BAQsFADCB -vTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL -ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwOCBWZXJp -U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MTgwNgYDVQQDEy9W -ZXJpU2lnbiBVbml2ZXJzYWwgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe -Fw0wODA0MDIwMDAwMDBaFw0zNzEyMDEyMzU5NTlaMIG9MQswCQYDVQQGEwJVUzEX -MBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlTaWduIFRydXN0 -IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAyMDA4IFZlcmlTaWduLCBJbmMuIC0gRm9y -IGF1dGhvcml6ZWQgdXNlIG9ubHkxODA2BgNVBAMTL1ZlcmlTaWduIFVuaXZlcnNh -bCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEF -AAOCAQ8AMIIBCgKCAQEAx2E3XrEBNNti1xWb/1hajCMj1mCOkdeQmIN65lgZOIzF -9uVkhbSicfvtvbnazU0AtMgtc6XHaXGVHzk8skQHnOgO+k1KxCHfKWGPMiJhgsWH -H26MfF8WIFFE0XBPV+rjHOPMee5Y2A7Cs0WTwCznmhcrewA3ekEzeOEz4vMQGn+H -LL729fdC4uW/h2KJXwBL38Xd5HVEMkE6HnFuacsLdUYI0crSK5XQz/u5QGtkjFdN -/BMReYTtXlT2NJ8IAfMQJQYXStrxHXpma5hgZqTZ79IugvHw7wnqRMkVauIDbjPT -rJ9VAMf2CGqUuV/c4DPxhGD5WycRtPwW8rtWaoAljQIDAQABo4GyMIGvMA8GA1Ud -EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMG0GCCsGAQUFBwEMBGEwX6FdoFsw -WTBXMFUWCWltYWdlL2dpZjAhMB8wBwYFKw4DAhoEFI/l0xqGrI2Oa8PPgGrUSBgs -exkuMCUWI2h0dHA6Ly9sb2dvLnZlcmlzaWduLmNvbS92c2xvZ28uZ2lmMB0GA1Ud -DgQWBBS2d/ppSEefUxLVwuoHMnYH0ZcHGTANBgkqhkiG9w0BAQsFAAOCAQEASvj4 -sAPmLGd75JR3Y8xuTPl9Dg3cyLk1uXBPY/ok+myDjEedO2Pzmvl2MpWRsXe8rJq+ -seQxIcaBlVZaDrHC1LGmWazxY8u4TB1ZkErvkBYoH1quEPuBUDgMbMzxPcP1Y+Oz -4yHJJDnp/RVmRvQbEdBNc6N9Rvk97ahfYtTxP/jgdFcrGJ2BtMQo2pSXpXDrrB2+ -BxHw1dvd5Yzw1TKwg+ZX4o+/vqGqvz0dtdQ46tewXDpPaj+PwGZsY6rp2aQW9IHR -lRQOfc2VNNnSj3BzgXucfr2YYdhFh5iQxeuGMMY1v/D/w1WIg0vvBZIGcfK4mJO3 -7M2CYfE45k+XmCpajQ== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEGjCCAwICEQCLW3VWhFSFCwDPrzhIzrGkMA0GCSqGSIb3DQEBBQUAMIHKMQsw -CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl -cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu -LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT -aWduIENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp -dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD -VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT -aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ -bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu -IENsYXNzIDEgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg -LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN2E1Lm0+afY8wR4 -nN493GwTFtl63SRRZsDHJlkNrAYIwpTRMx/wgzUfbhvI3qpuFU5UJ+/EbRrsC+MO -8ESlV8dAWB6jRx9x7GD2bZTIGDnt/kIYVt/kTEkQeE4BdjVjEjbdZrwBBDajVWjV -ojYJrKshJlQGrT/KFOCsyq0GHZXi+J3x4GD/wn91K0zM2v6HmSHquv4+VNfSWXjb -PG7PoBMAGrgnoeS+Z5bKoMWznN3JdZ7rMJpfo83ZrngZPyPpXNspva1VyBtUjGP2 -6KbqxzcSXKMpHgLZ2x87tNcPVkeBFQRKr4Mn0cVYiMHd9qqnoxjaaKptEVHhv2Vr -n5Z20T0CAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAq2aN17O6x5q25lXQBfGfMY1a -qtmqRiYPce2lrVNWYgFHKkTp/j90CxObufRNG7LRX7K20ohcs5/Ny9Sn2WCVhDr4 -wTcdYcrnsMXlkdpUpqwxga6X3s0IrLjAl4B/bnKk52kTlWUfxJM8/XmPBNQ+T+r3 -ns7NZ3xPZQL/kYVUc8f/NveGLezQXk//EZ9yBta4GvFMDSZl4kSAHsef493oCtrs -pSCAaWihT37ha88HQfqDjrw43bAuEbFrskLMmrz5SCJ5ShkPshw+IHTZasO+8ih4 -E1Z5T21Q6huwtVexN2ZYI/PcD98Kh8TvhgXVOBRgmaNL3gaWcSzy27YfpO8/7g== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEGTCCAwECEGFwy0mMX5hFKeewptlQW3owDQYJKoZIhvcNAQEFBQAwgcoxCzAJ -BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVy -aVNpZ24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24s -IEluYy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNp -Z24gQ2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0 -eSAtIEczMB4XDTk5MTAwMTAwMDAwMFoXDTM2MDcxNjIzNTk1OVowgcoxCzAJBgNV -BAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjEfMB0GA1UECxMWVmVyaVNp -Z24gVHJ1c3QgTmV0d29yazE6MDgGA1UECxMxKGMpIDE5OTkgVmVyaVNpZ24sIElu -Yy4gLSBGb3IgYXV0aG9yaXplZCB1c2Ugb25seTFFMEMGA1UEAxM8VmVyaVNpZ24g -Q2xhc3MgMiBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAt -IEczMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArwoNwtUs22e5LeWU -J92lvuCwTY+zYVY81nzD9M0+hsuiiOLh2KRpxbXiv8GmR1BeRjmL1Za6tW8UvxDO -JxOeBUebMXoT2B/Z0wI3i60sR/COgQanDTAM6/c8DyAd3HJG7qUCyFvDyVZpTMUY -wZF7C9UTAJu878NIPkZgIIUq1ZC2zYugzDLdt/1AVbJQHFauzI13TccgTacxdu9o -koqQHgiBVrKtaaNS0MscxCM9H5n+TOgWY47GCI72MfbS+uV23bUckqNJzc0BzWjN -qWm6o+sdDZykIKbBoMXRRkwXbdKsZj+WjOCE1Db/IlnF+RFgqF8EffIa9iVCYQ/E -Srg+iQIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQA0JhU8wI1NQ0kdvekhktdmnLfe -xbjQ5F1fdiLAJvmEOjr5jLX77GDx6M4EsMjdpwOPMPOY36TmpDHf0xwLRtxyID+u -7gU8pDM/CzmscHhzS5kr3zDCVLCoO1Wh/hYozUK9dG6A2ydEp85EXdQbkJgNHkKU -sQAsBNB0owIFImNjzYO1+8FtYmtpdf1dcEG59b98377BMnMiIYtYgXsVkXq642RI -sH/7NiXaldDxJBQX3RiAa0YjOVT1jmIJBB2UkKab5iXiQkWquJCtvgiPqQtCGJTP -cjnhsUPgKM+351psE2tJs//jGHyJizNdrDPXp/naOlXJWBD5qu9ats9LS98q ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEGjCCAwICEQCbfgZJoz5iudXukEhxKe9XMA0GCSqGSIb3DQEBBQUAMIHKMQsw -CQYDVQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZl -cmlTaWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWdu -LCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlT -aWduIENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3Jp -dHkgLSBHMzAeFw05OTEwMDEwMDAwMDBaFw0zNjA3MTYyMzU5NTlaMIHKMQswCQYD -VQQGEwJVUzEXMBUGA1UEChMOVmVyaVNpZ24sIEluYy4xHzAdBgNVBAsTFlZlcmlT -aWduIFRydXN0IE5ldHdvcmsxOjA4BgNVBAsTMShjKSAxOTk5IFZlcmlTaWduLCBJ -bmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxRTBDBgNVBAMTPFZlcmlTaWdu -IENsYXNzIDMgUHVibGljIFByaW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkg -LSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMu6nFL8eB8aHm8b -N3O9+MlrlBIwT/A2R/XQkQr1F8ilYcEWQE37imGQ5XYgwREGfassbqb1EUGO+i2t -KmFZpGcmTNDovFJbcCAEWNF6yaRpvIMXZK0Fi7zQWM6NjPXr8EJJC52XJ2cybuGu -kxUccLwgTS8Y3pKI6GyFVxEa6X7jJhFUokWWVYPKMIno3Nij7SqAP395ZVc+FSBm -CC+Vk7+qRy+oRpfwEuL+wgorUeZ25rdGt+INpsyow0xZVYnm6FNcHOqd8GIWC6fJ -Xwzw3sJ2zq/3avL6QaaiMxTJ5Xpj055iN9WFZZ4O5lMkdBteHRJTW8cs54NJOxWu -imi5V5cCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAERSWwauSCPc/L8my/uRan2Te -2yFPhpk0djZX3dAVL8WtfxUfN2JzPtTnX84XA9s1+ivbrmAJXx5fj267Cz3qWhMe -DGBvtcC1IyIuBwvLqXTLR7sdwdela8wv0kL9Sd2nic9TutoAWii/gt/4uhMdUIaC -/Y4wjylGsB49Ndo4YhYYSq3mtlFs3q9i6wHQHiT+eo8SGhJouPtmmRQURVyu565p -F4ErWjfJXir0xuKhXFSbplQAz/DxwceYMBo7Nhbbo27q/a2ywtrvAkcTisDxszGt -TxzhT5yvDwyd93gN2PQ1VoDat20Xj50egWTh/sVFuq1ruQp6Tk9LhO5L8X3dEQ== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEMDCCAxigAwIBAgIQUJRs7Bjq1ZxN1ZfvdY+grTANBgkqhkiG9w0BAQUFADCB -gjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3dy54cmFtcHNlY3VyaXR5LmNvbTEk -MCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2VydmljZXMgSW5jMS0wKwYDVQQDEyRY -UmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDQxMTAxMTcx -NDA0WhcNMzUwMTAxMDUzNzE5WjCBgjELMAkGA1UEBhMCVVMxHjAcBgNVBAsTFXd3 -dy54cmFtcHNlY3VyaXR5LmNvbTEkMCIGA1UEChMbWFJhbXAgU2VjdXJpdHkgU2Vy -dmljZXMgSW5jMS0wKwYDVQQDEyRYUmFtcCBHbG9iYWwgQ2VydGlmaWNhdGlvbiBB -dXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYJB69FbS6 -38eMpSe2OAtp87ZOqCwuIR1cRN8hXX4jdP5efrRKt6atH67gBhbim1vZZ3RrXYCP -KZ2GG9mcDZhtdhAoWORlsH9KmHmf4MMxfoArtYzAQDsRhtDLooY2YKTVMIJt2W7Q -DxIEM5dfT2Fa8OT5kavnHTu86M/0ay00fOJIYRyO82FEzG+gSqmUsE3a56k0enI4 -qEHMPJQRfevIpoy3hsvKMzvZPTeL+3o+hiznc9cKV6xkmxnr9A8ECIqsAxcZZPRa -JSKNNCyy9mgdEm3Tih4U2sSPpuIjhdV6Db1q4Ons7Be7QhtnqiXtRYMh/MHJfNVi -PvryxS3T/dRlAgMBAAGjgZ8wgZwwEwYJKwYBBAGCNxQCBAYeBABDAEEwCwYDVR0P -BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMZPoj0GY4QJnM5i5ASs -jVy16bYbMDYGA1UdHwQvMC0wK6ApoCeGJWh0dHA6Ly9jcmwueHJhbXBzZWN1cml0 -eS5jb20vWEdDQS5jcmwwEAYJKwYBBAGCNxUBBAMCAQEwDQYJKoZIhvcNAQEFBQAD -ggEBAJEVOQMBG2f7Shz5CmBbodpNl2L5JFMn14JkTpAuw0kbK5rc/Kh4ZzXxHfAR -vbdI4xD2Dd8/0sm2qlWkSLoC295ZLhVbO50WfUfXN+pfTXYSNrsf16GBBEYgoyxt -qZ4Bfj8pzgCT3/3JknOJiWSe5yvkHJEs0rnOfc5vMZnT5r7SHpDwCRR5XCOrTdLa -IR9NmXmd4c8nnxCbHIgNsIpkQTG4DmyQJKSbXHGPurt+HBvbaoAPIbzp26a3QPSy -i6mx5O+aGtA9aZnuqCij4Tyz8LIRnM98QObd50N9otg6tamN8jSZxNQQ4Qb9CYQQ -O+7ETPTsJ3xCwnR8gooJybQDJbw= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIDODCCAiCgAwIBAgIGIAYFFnACMA0GCSqGSIb3DQEBBQUAMDsxCzAJBgNVBAYT -AlJPMREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBD -QTAeFw0wNjA3MDQxNzIwMDRaFw0zMTA3MDQxNzIwMDRaMDsxCzAJBgNVBAYTAlJP -MREwDwYDVQQKEwhjZXJ0U0lHTjEZMBcGA1UECxMQY2VydFNJR04gUk9PVCBDQTCC -ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALczuX7IJUqOtdu0KBuqV5Do -0SLTZLrTk+jUrIZhQGpgV2hUhE28alQCBf/fm5oqrl0Hj0rDKH/v+yv6efHHrfAQ -UySQi2bJqIirr1qjAOm+ukbuW3N7LBeCgV5iLKECZbO9xSsAfsT8AzNXDe3i+s5d -RdY4zTW2ssHQnIFKquSyAVwdj1+ZxLGt24gh65AIgoDzMKND5pCCrlUoSe1b16kQ -OA7+j0xbm0bqQfWwCHTD0IgztnzXdN/chNFDDnU5oSVAKOp4yw4sLjmdjItuFhwv -JoIQ4uNllAoEwF73XVv4EOLQunpL+943AAAaWyjj0pxzPjKHmKHJUS/X3qwzs08C -AwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAcYwHQYDVR0O -BBYEFOCMm9slSbPxfIbWskKHC9BroNnkMA0GCSqGSIb3DQEBBQUAA4IBAQA+0hyJ -LjX8+HXd5n9liPRyTMks1zJO890ZeUe9jjtbkw9QSSQTaxQGcu8J06Gh40CEyecY -MnQ8SG4Pn0vU9x7Tk4ZkVJdjclDVVc/6IJMCopvDI5NOFlV2oHB5bc0hH88vLbwZ -44gx+FkagQnIl6Z0x2DEW8xXjrJ1/RsCCdtZb3KTafcxQdaIOL+Hsr0Wefmq5L6I -Jd1hJyMctTEHBDa0GpC9oHRxUIltvBTjD4au8as+x6AJzKNI0eDbZOeStc+vckNw -i/nDhDwTqn6Sm1dTk/pwwpEOMfmbZ13pljheX7NzTogVZ96edhBiIL5VaZVDADlN -9u6wWk5JRFRYX0KD ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIFsDCCA5igAwIBAgIQFci9ZUdcr7iXAF7kBtK8nTANBgkqhkiG9w0BAQUFADBe -MQswCQYDVQQGEwJUVzEjMCEGA1UECgwaQ2h1bmdod2EgVGVsZWNvbSBDby4sIEx0 -ZC4xKjAoBgNVBAsMIWVQS0kgUm9vdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAe -Fw0wNDEyMjAwMjMxMjdaFw0zNDEyMjAwMjMxMjdaMF4xCzAJBgNVBAYTAlRXMSMw -IQYDVQQKDBpDaHVuZ2h3YSBUZWxlY29tIENvLiwgTHRkLjEqMCgGA1UECwwhZVBL -SSBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkqhkiG9w0BAQEF -AAOCAg8AMIICCgKCAgEA4SUP7o3biDN1Z82tH306Tm2d0y8U82N0ywEhajfqhFAH -SyZbCUNsIZ5qyNUD9WBpj8zwIuQf5/dqIjG3LBXy4P4AakP/h2XGtRrBp0xtInAh -ijHyl3SJCRImHJ7K2RKilTza6We/CKBk49ZCt0Xvl/T29de1ShUCWH2YWEtgvM3X -DZoTM1PRYfl61dd4s5oz9wCGzh1NlDivqOx4UXCKXBCDUSH3ET00hl7lSM2XgYI1 -TBnsZfZrxQWh7kcT1rMhJ5QQCtkkO7q+RBNGMD+XPNjX12ruOzjjK9SXDrkb5wdJ -fzcq+Xd4z1TtW0ado4AOkUPB1ltfFLqfpo0kR0BZv3I4sjZsN/+Z0V0OWQqraffA -sgRFelQArr5T9rXn4fg8ozHSqf4hUmTFpmfwdQcGlBSBVcYn5AGPF8Fqcde+S/uU -WH1+ETOxQvdibBjWzwloPn9s9h6PYq2lY9sJpx8iQkEeb5mKPtf5P0B6ebClAZLS -nT0IFaUQAS2zMnaolQ2zepr7BxB4EW/hj8e6DyUadCrlHJhBmd8hh+iVBmoKs2pH -dmX2Os+PYhcZewoozRrSgx4hxyy/vv9haLdnG7t4TY3OZ+XkwY63I2binZB1NJip -NiuKmpS5nezMirH4JYlcWrYvjB9teSSnUmjDhDXiZo1jDiVN1Rmy5nk3pyKdVDEC -AwEAAaNqMGgwHQYDVR0OBBYEFB4M97Zn8uGSJglFwFU5Lnc/QkqiMAwGA1UdEwQF -MAMBAf8wOQYEZyoHAAQxMC8wLQIBADAJBgUrDgMCGgUAMAcGBWcqAwAABBRFsMLH -ClZ87lt4DJX5GFPBphzYEDANBgkqhkiG9w0BAQUFAAOCAgEACbODU1kBPpVJufGB -uvl2ICO1J2B01GqZNF5sAFPZn/KmsSQHRGoqxqWOeBLoR9lYGxMqXnmbnwoqZ6Yl -PwZpVnPDimZI+ymBV3QGypzqKOg4ZyYr8dW1P2WT+DZdjo2NQCCHGervJ8A9tDkP -JXtoUHRVnAxZfVo9QZQlUgjgRywVMRnVvwdVxrsStZf0X4OFunHB2WyBEXYKCrC/ -gpf36j36+uwtqSiUO1bd0lEursC9CBWMd1I0ltabrNMdjmEPNXubrjlpC2JgQCA2 -j6/7Nu4tCEoduL+bXPjqpRugc6bY+G7gMwRfaKonh+3ZwZCc7b3jajWvY9+rGNm6 -5ulK6lCKD2GTHuItGeIwlDWSXQ62B68ZgI9HkFFLLk3dheLSClIKF5r8GrBQAuUB -o2M3IUxExJtRmREOc5wGj1QupyheRDmHVi03vYVElOEMSyycw5KFNGHLD7ibSkNS -/jQ6fbjpKdx2qcgw+BRxgMYeNkh0IkFch4LoGHGLQYlE535YW6i4jRPpp2zDR+2z -Gp1iro2C6pSe3VkQw63d4k3jMdXH7OjysP6SHhYKGvzZ8/gntsm+HbRsZJB/9OTE -W9c3rkIO3aQab3yIVMUWbuF6aC74Or8NpDyJO3inTmODBCEIZ43ygknQW/2xzQ+D -hNQ+IIX3Sj0rnP0qCglN6oH4EZw= ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB -qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf -Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw -MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV -BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw -NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j -LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG -A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl -IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs -W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta -3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk -6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6 -Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J -NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA -MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP -r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU -DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz -YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX -xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2 -/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/ -LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7 -jVaMaA== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIICiDCCAg2gAwIBAgIQNfwmXNmET8k9Jj1Xm67XVjAKBggqhkjOPQQDAzCBhDEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjE4MDYGA1UECxMvKGMp -IDIwMDcgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAi -BgNVBAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMjAeFw0wNzExMDUwMDAw -MDBaFw0zODAxMTgyMzU5NTlaMIGEMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhh -d3RlLCBJbmMuMTgwNgYDVQQLEy8oYykgMjAwNyB0aGF3dGUsIEluYy4gLSBGb3Ig -YXV0aG9yaXplZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9v -dCBDQSAtIEcyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEotWcgnuVnfFSeIf+iha/ -BebfowJPDQfGAFG6DAJSLSKkQjnE/o/qycG+1E3/n3qe4rF8mq2nhglzh9HnmuN6 -papu+7qzcMBniKI11KOasf2twu8x+qi58/sIxpHR+ymVo0IwQDAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUmtgAMADna3+FGO6Lts6K -DPgR4bswCgYIKoZIzj0EAwMDaQAwZgIxAN344FdHW6fmCsO99YCKlzUNG4k8VIZ3 -KMqh9HneteY4sPBlcIx/AlTCv//YoT7ZzwIxAMSNlPzcU9LcnXgWHxUzI1NS41ox -XZ3Krr0TKUQNJ1uo52icEvdYPy5yAlejj6EULg== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIEKjCCAxKgAwIBAgIQYAGXt0an6rS0mtZLL/eQ+zANBgkqhkiG9w0BAQsFADCB -rjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf -Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw -MDggdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxJDAiBgNV -BAMTG3RoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EgLSBHMzAeFw0wODA0MDIwMDAwMDBa -Fw0zNzEyMDEyMzU5NTlaMIGuMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMdGhhd3Rl -LCBJbmMuMSgwJgYDVQQLEx9DZXJ0aWZpY2F0aW9uIFNlcnZpY2VzIERpdmlzaW9u -MTgwNgYDVQQLEy8oYykgMjAwOCB0aGF3dGUsIEluYy4gLSBGb3IgYXV0aG9yaXpl -ZCB1c2Ugb25seTEkMCIGA1UEAxMbdGhhd3RlIFByaW1hcnkgUm9vdCBDQSAtIEcz -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsr8nLPvb2FvdeHsbnndm -gcs+vHyu86YnmjSjaDFxODNi5PNxZnmxqWWjpYvVj2AtP0LMqmsywCPLLEHd5N/8 -YZzic7IilRFDGF/Eth9XbAoFWCLINkw6fKXRz4aviKdEAhN0cXMKQlkC+BsUa0Lf -b1+6a4KinVvnSr0eAXLbS3ToO39/fR8EtCab4LRarEc9VbjXsCZSKAExQGbY2SS9 -9irY7CFJXJv2eul/VTV+lmuNk5Mny5K76qxAwJ/C+IDPXfRa3M50hqY+bAtTyr2S -zhkGcuYMXDhpxwTWvGzOW/b3aJzcJRVIiKHpqfiYnODz1TEoYRFsZ5aNOZnLwkUk -OQIDAQABo0IwQDAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNV -HQ4EFgQUrWyqlGCc7eT/+j4KdCtjA/e2Wb8wDQYJKoZIhvcNAQELBQADggEBABpA -2JVlrAmSicY59BDlqQ5mU1143vokkbvnRFHfxhY0Cu9qRFHqKweKA3rD6z8KLFIW -oCtDuSWQP3CpMyVtRRooOyfPqsMpQhvfO0zAMzRbQYi/aytlryjvsvXDqmbOe1bu -t8jLZ8HJnBoYuMTDSQPxYA5QzUbF83d597YV4Djbxy8ooAw/dyZ02SUS2jHaGh7c -KUGRIjxpp7sC8rZcJwOJ9Abqm+RyguOhCcHpABnTPtRwa7pxpqpYrvS76Wy274fM -m7v/OeZWYdMKp8RcTGB7BXcmer/YB1IsYvdwY9k5vG8cwnncdimvzsUsZAReiDZu -MdRAGmI0Nj81Aa6sY6A= ------END CERTIFICATE----- diff --git a/script/code-gen-docker.sh b/script/code-gen-docker.sh deleted file mode 100755 index e69de29bb..000000000 diff --git a/script/code-gen.sh b/script/code-gen.sh index 529930801..3f0112f35 100755 --- a/script/code-gen.sh +++ b/script/code-gen.sh @@ -4,11 +4,11 @@ set -e -o pipefail PROJECT_MODULE="github.com/traefik/traefik" MODULE_VERSION="v3" -KUBE_VERSION=v0.30.10 +KUBE_VERSION=v0.34.3 CURRENT_DIR="$(pwd)" go install "k8s.io/code-generator/cmd/deepcopy-gen@${KUBE_VERSION}" -go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.16.1 +go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.19.0 CODEGEN_PKG="$(go env GOPATH)/pkg/mod/k8s.io/code-generator@${KUBE_VERSION}" # shellcheck disable=SC1091 # Cannot check source of this file @@ -20,6 +20,7 @@ kube::codegen::gen_helpers \ "${CURRENT_DIR}" kube::codegen::gen_client \ + --with-applyconfig \ --with-watch \ --output-dir "${CURRENT_DIR}/pkg/provider/kubernetes/crd/generated" \ --output-pkg "${PROJECT_MODULE}/${MODULE_VERSION}/pkg/provider/kubernetes/crd/generated" \ @@ -34,3 +35,6 @@ controller-gen crd:crdVersions=v1 \ echo "# Concatenate the CRD definitions for publication and integration tests ..." cat "${CURRENT_DIR}"/docs/content/reference/dynamic-configuration/traefik.io_*.yaml > "${CURRENT_DIR}"/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml cp -f "${CURRENT_DIR}"/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml "${CURRENT_DIR}"/integration/fixtures/k8s/01-traefik-crd.yml + +# Remove leading '---' from the concatenated file (files with multiple resources should not start with ---) +sed -i '1{/^---$/d;}' "${CURRENT_DIR}"/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml diff --git a/script/gcg/traefik-bugfix.toml b/script/gcg/traefik-bugfix.toml index 36d6242a3..a893c77c6 100644 --- a/script/gcg/traefik-bugfix.toml +++ b/script/gcg/traefik-bugfix.toml @@ -4,14 +4,14 @@ RepositoryName = "traefik" OutputType = "file" FileName = "traefik_changelog.md" -# example new bugfix v3.5.4 -CurrentRef = "v3.5" -PreviousRef = "v3.5.3" -BaseBranch = "v3.5" -FutureCurrentRefName = "v3.5.4" +# example new bugfix v3.6.7 +CurrentRef = "v3.6" +PreviousRef = "v3.6.6" +BaseBranch = "v3.6" +FutureCurrentRefName = "v3.6.7" -ThresholdPreviousRef = 10 -ThresholdCurrentRef = 10 +ThresholdPreviousRef = 10000 +ThresholdCurrentRef = 10000 Debug = true DisplayLabel = true diff --git a/script/gcg/traefik-final-release-part1.toml b/script/gcg/traefik-final-release-part1.toml index 95291aea5..844b5809a 100644 --- a/script/gcg/traefik-final-release-part1.toml +++ b/script/gcg/traefik-final-release-part1.toml @@ -4,11 +4,11 @@ RepositoryName = "traefik" OutputType = "file" FileName = "traefik_changelog.md" -# example final release of v3.5.0 -CurrentRef = "v3.5" -PreviousRef = "v3.5.0-rc1" -BaseBranch = "v3.5" -FutureCurrentRefName = "v3.5.0" +# example final release of v3.6.0 +CurrentRef = "v3.6" +PreviousRef = "v3.6.0-rc1" +BaseBranch = "v3.6" +FutureCurrentRefName = "v3.6.0" ThresholdPreviousRef = 10 ThresholdCurrentRef = 10 diff --git a/script/gcg/traefik-final-release-part2.toml b/script/gcg/traefik-final-release-part2.toml index ceaa118de..925566e7f 100644 --- a/script/gcg/traefik-final-release-part2.toml +++ b/script/gcg/traefik-final-release-part2.toml @@ -4,11 +4,11 @@ RepositoryName = "traefik" OutputType = "file" FileName = "traefik_changelog.md" -# example final release of v3.5.0 -CurrentRef = "v3.5.0-rc1" -PreviousRef = "v3.4.0-rc1" +# example final release of v3.6.0 +CurrentRef = "v3.6.0-rc1" +PreviousRef = "v3.5.0-rc1" BaseBranch = "master" -FutureCurrentRefName = "v3.5.0" +FutureCurrentRefName = "v3.6.0" ThresholdPreviousRef = 10 ThresholdCurrentRef = 10 diff --git a/script/gcg/traefik-rc-first.toml b/script/gcg/traefik-rc-first.toml index a946c438d..29e3d0456 100644 --- a/script/gcg/traefik-rc-first.toml +++ b/script/gcg/traefik-rc-first.toml @@ -4,11 +4,11 @@ RepositoryName = "traefik" OutputType = "file" FileName = "traefik_changelog.md" -# example RC1 of v3.5.0-rc1 +# example RC1 of v3.6.0-rc1 CurrentRef = "master" -PreviousRef = "v3.4.0-rc1" +PreviousRef = "v3.5.0-rc1" BaseBranch = "master" -FutureCurrentRefName = "v3.5.0-rc1" +FutureCurrentRefName = "v3.6.0-rc1" ThresholdPreviousRef = 10 ThresholdCurrentRef = 10 diff --git a/script/update-cert.sh b/script/update-cert.sh deleted file mode 100755 index 5816df6cf..000000000 --- a/script/update-cert.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh - -set -e - -CERT_IMAGE="alpine:edge" - -# cd to the current directory so the script can be run from anywhere. -SCRIPT_DIR="$( cd "$( dirname "${0}" )" && pwd -P)"; export SCRIPT_DIR -cd "${SCRIPT_DIR}" - -# Update the cert image. -docker pull $CERT_IMAGE - -# Fetch the latest certificates. -ID=$(docker run -d $CERT_IMAGE sh -c "apk --update upgrade && apk add ca-certificates && update-ca-certificates") -docker logs -f "${ID}" -docker wait "${ID}" - -# Update the local certificates. -docker cp "${ID}":/etc/ssl/certs/ca-certificates.crt "${SCRIPT_DIR}" - -# Cleanup. -docker rm -f "${ID}" diff --git a/webui/.nvmrc b/webui/.nvmrc index 26600046d..0a492611a 100644 --- a/webui/.nvmrc +++ b/webui/.nvmrc @@ -1 +1 @@ -v22.15.1 +24.11.0 diff --git a/webui/.yarnrc.yml b/webui/.yarnrc.yml index 3186f3f07..94f5c254e 100644 --- a/webui/.yarnrc.yml +++ b/webui/.yarnrc.yml @@ -1 +1,2 @@ nodeLinker: node-modules +enableScripts: false diff --git a/webui/buildx.Dockerfile b/webui/buildx.Dockerfile index 09b65ab81..4d64776fd 100644 --- a/webui/buildx.Dockerfile +++ b/webui/buildx.Dockerfile @@ -1,4 +1,4 @@ -FROM node:22.15.1-alpine3.20 +FROM node:24-alpine3.22 ENV WEBUI_DIR=/src/webui RUN mkdir -p $WEBUI_DIR diff --git a/webui/package.json b/webui/package.json index 3721a8ae1..b75ac6d1b 100644 --- a/webui/package.json +++ b/webui/package.json @@ -43,11 +43,13 @@ "type": "module", "dependencies": { "@eslint/js": "^9.32.0", + "@noble/ed25519": "^3.0.0", + "@noble/hashes": "^2.0.1", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.4.2", "@testing-library/react": "^14.2.1", "@testing-library/user-event": "^14.5.2", - "@traefiklabs/faency": "11.1.4", + "@traefiklabs/faency": "12.0.4", "@types/lodash": "^4.17.16", "@types/node": "^22.15.18", "@types/react": "^18.2.0", @@ -56,6 +58,7 @@ "@typescript-eslint/parser": "^8.38.0", "@vitejs/plugin-react": "^4.7.0", "@vitest/coverage-v8": "^3.2.4", + "@vitest/web-worker": "^4.0.2", "chart.js": "^4.4.1", "eslint": "^9.32.0", "eslint-config-prettier": "^10.1.8", @@ -85,7 +88,7 @@ "usehooks-ts": "^2.14.0", "vite": "^5.4.19", "vite-tsconfig-paths": "^5.1.4", - "vitest": "^3.2.4", + "vitest": "^4.0.3", "vitest-canvas-mock": "^0.3.3" }, "devDependencies": { diff --git a/webui/public/img/gopher-something-went-wrong.png b/webui/public/img/gopher-something-went-wrong.png new file mode 100644 index 000000000..fb0d6d4c3 Binary files /dev/null and b/webui/public/img/gopher-something-went-wrong.png differ diff --git a/webui/public/traefiklabs-hub-button-app/main-v1.js b/webui/public/traefiklabs-hub-button-app/main-v1.js deleted file mode 100644 index e140dab34..000000000 --- a/webui/public/traefiklabs-hub-button-app/main-v1.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -(()=>{var e={110:(e,t,n)=>{"use strict";var r=n(309),a={childContextTypes:!0,contextType:!0,contextTypes:!0,defaultProps:!0,displayName:!0,getDefaultProps:!0,getDerivedStateFromError:!0,getDerivedStateFromProps:!0,mixins:!0,propTypes:!0,type:!0},l={name:!0,length:!0,prototype:!0,caller:!0,callee:!0,arguments:!0,arity:!0},o={$$typeof:!0,compare:!0,defaultProps:!0,displayName:!0,propTypes:!0,type:!0},i={};function u(e){return r.isMemo(e)?o:i[e.$$typeof]||a}i[r.ForwardRef]={$$typeof:!0,render:!0,defaultProps:!0,displayName:!0,propTypes:!0},i[r.Memo]=o;var s=Object.defineProperty,c=Object.getOwnPropertyNames,f=Object.getOwnPropertySymbols,d=Object.getOwnPropertyDescriptor,p=Object.getPrototypeOf,h=Object.prototype;e.exports=function e(t,n,r){if("string"!==typeof n){if(h){var a=p(n);a&&a!==h&&e(t,a,r)}var o=c(n);f&&(o=o.concat(f(n)));for(var i=u(t),m=u(n),g=0;g{"use strict";var n="function"===typeof Symbol&&Symbol.for,r=n?Symbol.for("react.element"):60103,a=n?Symbol.for("react.portal"):60106,l=n?Symbol.for("react.fragment"):60107,o=n?Symbol.for("react.strict_mode"):60108,i=n?Symbol.for("react.profiler"):60114,u=n?Symbol.for("react.provider"):60109,s=n?Symbol.for("react.context"):60110,c=n?Symbol.for("react.async_mode"):60111,f=n?Symbol.for("react.concurrent_mode"):60111,d=n?Symbol.for("react.forward_ref"):60112,p=n?Symbol.for("react.suspense"):60113,h=n?Symbol.for("react.suspense_list"):60120,m=n?Symbol.for("react.memo"):60115,g=n?Symbol.for("react.lazy"):60116,v=n?Symbol.for("react.block"):60121,y=n?Symbol.for("react.fundamental"):60117,b=n?Symbol.for("react.responder"):60118,S=n?Symbol.for("react.scope"):60119;function k(e){if("object"===typeof e&&null!==e){var t=e.$$typeof;switch(t){case r:switch(e=e.type){case c:case f:case l:case i:case o:case p:return e;default:switch(e=e&&e.$$typeof){case s:case d:case g:case m:case u:return e;default:return t}}case a:return t}}}function w(e){return k(e)===f}t.AsyncMode=c,t.ConcurrentMode=f,t.ContextConsumer=s,t.ContextProvider=u,t.Element=r,t.ForwardRef=d,t.Fragment=l,t.Lazy=g,t.Memo=m,t.Portal=a,t.Profiler=i,t.StrictMode=o,t.Suspense=p,t.isAsyncMode=function(e){return w(e)||k(e)===c},t.isConcurrentMode=w,t.isContextConsumer=function(e){return k(e)===s},t.isContextProvider=function(e){return k(e)===u},t.isElement=function(e){return"object"===typeof e&&null!==e&&e.$$typeof===r},t.isForwardRef=function(e){return k(e)===d},t.isFragment=function(e){return k(e)===l},t.isLazy=function(e){return k(e)===g},t.isMemo=function(e){return k(e)===m},t.isPortal=function(e){return k(e)===a},t.isProfiler=function(e){return k(e)===i},t.isStrictMode=function(e){return k(e)===o},t.isSuspense=function(e){return k(e)===p},t.isValidElementType=function(e){return"string"===typeof e||"function"===typeof e||e===l||e===f||e===i||e===o||e===p||e===h||"object"===typeof e&&null!==e&&(e.$$typeof===g||e.$$typeof===m||e.$$typeof===u||e.$$typeof===s||e.$$typeof===d||e.$$typeof===y||e.$$typeof===b||e.$$typeof===S||e.$$typeof===v)},t.typeOf=k},309:(e,t,n)=>{"use strict";e.exports=n(746)},463:(e,t,n)=>{"use strict";var r=n(791),a=n(296);function l(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n