diff --git a/.github/workflows/build_daily.yaml b/.github/workflows/build_daily.yaml index 2479dfcab06..b376f5c0c5b 100644 --- a/.github/workflows/build_daily.yaml +++ b/.github/workflows/build_daily.yaml @@ -19,7 +19,7 @@ jobs: e2e-contour-xds: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 @@ -46,7 +46,7 @@ jobs: CONTOUR_E2E_XDS_SERVER_TYPE: contour run: | make setup-kind-cluster run-e2e cleanup-kind - - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + - uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 with: status: ${{ job.status }} steps: ${{ toJson(steps) }} @@ -55,7 +55,7 @@ jobs: e2e-envoy-deployment: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 @@ -82,7 +82,7 @@ jobs: CONTOUR_E2E_ENVOY_DEPLOYMENT_MODE: deployment run: | make setup-kind-cluster run-e2e cleanup-kind - - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + - uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 with: status: ${{ job.status }} steps: ${{ toJson(steps) }} @@ -91,7 +91,7 @@ jobs: e2e-ipv6: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 @@ -121,16 +121,16 @@ jobs: make setup-kind-cluster export CONTOUR_E2E_LOCAL_HOST=$(ifconfig | grep inet6 | grep global | head -n1 | awk '{print $2}') make run-e2e cleanup-kind - - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + - uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 with: status: ${{ job.status }} steps: ${{ toJson(steps) }} channel: '#contour-ci-notifications' if: ${{ failure() && github.ref == 'refs/heads/main' }} - e2e-endpoint-slices: + e2e-endpoints: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 @@ -154,10 +154,10 @@ jobs: - name: e2e tests env: CONTOUR_E2E_IMAGE: ghcr.io/projectcontour/contour:main - CONTOUR_E2E_USE_ENDPOINT_SLICES: true + CONTOUR_E2E_USE_ENDPOINTS: true run: | make setup-kind-cluster run-e2e cleanup-kind - - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + - uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 with: status: ${{ job.status }} steps: ${{ toJson(steps) }} diff --git a/.github/workflows/build_main.yaml b/.github/workflows/build_main.yaml index 18d02090b80..da5c0427a6a 100644 --- a/.github/workflows/build_main.yaml +++ b/.github/workflows/build_main.yaml @@ -17,11 +17,11 @@ jobs: permissions: packages: write steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - name: Set up Docker Buildx - uses: docker/setup-buildx-action@2b51285047da1547ffb1b2203d8be4c0af6b1f20 # v3.2.0 + uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0 with: version: latest - name: Log in to GHCR @@ -38,7 +38,7 @@ jobs: PUSH_IMAGE: "true" run: | make multiarch-build - - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + - uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 with: status: ${{ job.status }} steps: ${{ toJson(steps) }} diff --git a/.github/workflows/build_tag.yaml b/.github/workflows/build_tag.yaml index 6452a4eadce..c839707df97 100644 --- a/.github/workflows/build_tag.yaml +++ b/.github/workflows/build_tag.yaml @@ -27,11 +27,11 @@ jobs: permissions: packages: write steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - name: Set up Docker Buildx - uses: docker/setup-buildx-action@2b51285047da1547ffb1b2203d8be4c0af6b1f20 # v3.2.0 + uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0 with: version: latest - name: Log in to GHCR @@ -46,7 +46,7 @@ jobs: TAG_LATEST: "false" run: | ./hack/actions/build-and-push-release-images.sh - - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + - uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 with: status: ${{ job.status }} steps: ${{ toJson(steps) }} @@ -56,7 +56,7 @@ jobs: runs-on: ubuntu-latest needs: [build] steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 @@ -84,11 +84,11 @@ jobs: export CONTOUR_E2E_IMAGE="ghcr.io/projectcontour/contour:$(git describe --tags)" make setup-kind-cluster run-gateway-conformance cleanup-kind - name: Upload gateway conformance report - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: gateway-conformance-report path: gateway-conformance-report/projectcontour-contour-*.yaml - - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + - uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 with: status: ${{ job.status }} steps: ${{ toJson(steps) }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 1f74fe4d522..ffa0cac8ca5 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -22,7 +22,7 @@ jobs: permissions: security-events: write steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 @@ -41,11 +41,11 @@ jobs: cache: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10 + uses: github/codeql-action/init@8f596b4ae3cb3c588a5c46780b86dd53fef16c52 # v3.25.2 with: languages: go # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - name: Autobuild - uses: github/codeql-action/autobuild@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10 + uses: github/codeql-action/autobuild@8f596b4ae3cb3c588a5c46780b86dd53fef16c52 # v3.25.2 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10 + uses: github/codeql-action/analyze@8f596b4ae3cb3c588a5c46780b86dd53fef16c52 # v3.25.2 diff --git a/.github/workflows/label_check.yaml b/.github/workflows/label_check.yaml index fb9f8203f7b..2790ffce068 100644 --- a/.github/workflows/label_check.yaml +++ b/.github/workflows/label_check.yaml @@ -44,7 +44,7 @@ jobs: needs: [check-label] runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 diff --git a/.github/workflows/openssf-scorecard.yaml b/.github/workflows/openssf-scorecard.yaml index 19d096e3331..e6b0f766b70 100644 --- a/.github/workflows/openssf-scorecard.yaml +++ b/.github/workflows/openssf-scorecard.yaml @@ -22,7 +22,7 @@ jobs: security-events: write id-token: write steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - name: "Run analysis" @@ -32,11 +32,11 @@ jobs: results_format: sarif publish_results: true - name: "Upload artifact" - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: SARIF file path: results.sarif - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10 + uses: github/codeql-action/upload-sarif@8f596b4ae3cb3c588a5c46780b86dd53fef16c52 # v3.25.2 with: sarif_file: results.sarif diff --git a/.github/workflows/prbuild.yaml b/.github/workflows/prbuild.yaml index 2b012f55caa..b4301d677f8 100644 --- a/.github/workflows/prbuild.yaml +++ b/.github/workflows/prbuild.yaml @@ -23,17 +23,15 @@ jobs: with: go-version: ${{ env.GO_VERSION }} cache: false - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - name: golangci-lint uses: golangci/golangci-lint-action@3cfe3a4abbb849e10058ce4af15d205b6da42804 # v4.0.0 with: version: v1.56.2 - # TODO: re-enable linting tools package once https://github.com/projectcontour/contour/issues/5077 - # is resolved - args: --build-tags=e2e,conformance,gcp,oidc,none --out-format=colored-line-number - - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + args: --build-tags=e2e,conformance,tools,gcp,oidc,none --out-format=colored-line-number + - uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 with: status: ${{ job.status }} steps: ${{ toJson(steps) }} @@ -44,7 +42,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - name: Codespell @@ -54,7 +52,7 @@ jobs: ignore_words_file: './.codespell.ignorewords' check_filenames: true check_hidden: true - - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + - uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 with: status: ${{ job.status }} steps: ${{ toJson(steps) }} @@ -63,7 +61,7 @@ jobs: codegen: runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 @@ -88,7 +86,7 @@ jobs: run: | make generate lint-yamllint lint-flags ./hack/actions/check-uncommitted-codegen.sh - - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + - uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 with: status: ${{ job.status }} steps: ${{ toJson(steps) }} @@ -101,11 +99,11 @@ jobs: - codegen runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - name: Set up Docker Buildx - uses: docker/setup-buildx-action@2b51285047da1547ffb1b2203d8be4c0af6b1f20 # v3.2.0 + uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0 with: version: latest - name: Build image @@ -114,11 +112,11 @@ jobs: run: | make multiarch-build - name: Upload image - uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1 + uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3 with: name: image path: image/contour-*.tar - - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + - uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 with: status: ${{ job.status }} steps: ${{ toJson(steps) }} @@ -151,11 +149,11 @@ jobs: use_config_crd: "true" steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - name: Download image - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 + uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7 with: name: image path: image @@ -185,7 +183,7 @@ jobs: run: | export CONTOUR_E2E_IMAGE="ghcr.io/projectcontour/contour:$(ls ./image/contour-*.tar | sed -E 's/.*contour-(.*).tar/\1/')" make e2e - - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + - uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 with: status: ${{ job.status }} steps: ${{ toJson(steps) }} @@ -211,14 +209,14 @@ jobs: - kubernetes_version: "kubernetes:n-2" node_image: "docker.io/kindest/node:v1.27.11@sha256:681253009e68069b8e01aad36a1e0fa8cf18bb0ab3e5c4069b2e65cafdd70843" steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false # Fetch history for all tags and branches so we can figure out most # recent release tag. fetch-depth: 0 - name: Download image - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 + uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7 with: name: image path: image @@ -249,7 +247,7 @@ jobs: run: | export CONTOUR_E2E_IMAGE="ghcr.io/projectcontour/contour:$(ls ./image/contour-*.tar | sed -E 's/.*contour-(.*).tar/\1/')" make upgrade - - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + - uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 with: status: ${{ job.status }} steps: ${{ toJson(steps) }} @@ -262,7 +260,7 @@ jobs: - codegen runs-on: ubuntu-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 @@ -292,11 +290,11 @@ jobs: make check-coverage - name: codeCoverage if: ${{ success() }} - uses: codecov/codecov-action@7afa10ed9b269c561c2336fd862446844e0cbf71 # v4.2.0 + uses: codecov/codecov-action@84508663e988701840491b86de86b666e8a86bed # v4.3.0 with: token: ${{ secrets.CODECOV_TOKEN }} files: coverage.out - - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + - uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 with: status: ${{ job.status }} steps: ${{ toJson(steps) }} @@ -309,7 +307,7 @@ jobs: - codegen runs-on: macos-latest steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 @@ -337,7 +335,7 @@ jobs: run: | make install make check-coverage - - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + - uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 with: status: ${{ job.status }} steps: ${{ toJson(steps) }} @@ -347,11 +345,11 @@ jobs: runs-on: ubuntu-latest needs: [build-image] steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false - name: Download image - uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4 + uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7 with: name: image path: image @@ -379,7 +377,7 @@ jobs: run: | export CONTOUR_E2E_IMAGE="ghcr.io/projectcontour/contour:$(ls ./image/contour-*.tar | sed -E 's/.*contour-(.*).tar/\1/')" make gateway-conformance - - uses: act10ns/slack@ed1309ab9862e57e9e583e51c7889486b9a00b0f # v2.0.0 + - uses: act10ns/slack@44541246747a30eb3102d87f7a4cc5471b0ffb7d # v2.1.0 with: status: ${{ job.status }} steps: ${{ toJson(steps) }} diff --git a/.github/workflows/trivy-scan.yaml b/.github/workflows/trivy-scan.yaml index e3fc7f6ba06..059550e9c53 100644 --- a/.github/workflows/trivy-scan.yaml +++ b/.github/workflows/trivy-scan.yaml @@ -23,7 +23,7 @@ jobs: permissions: security-events: write steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: persist-credentials: false ref: ${{ matrix.branch }} @@ -35,6 +35,6 @@ jobs: output: 'trivy-results.sarif' ignore-unfixed: true severity: 'HIGH,CRITICAL' - - uses: github/codeql-action/upload-sarif@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # v3.24.10 + - uses: github/codeql-action/upload-sarif@8f596b4ae3cb3c588a5c46780b86dd53fef16c52 # v3.25.2 with: sarif_file: 'trivy-results.sarif' diff --git a/Makefile b/Makefile index a1aac3fdcc1..8c45c54c179 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ IMAGE := $(REGISTRY)/$(PROJECT) SRCDIRS := ./cmd ./internal ./apis LOCAL_BOOTSTRAP_CONFIG = localenvoyconfig.yaml SECURE_LOCAL_BOOTSTRAP_CONFIG = securelocalenvoyconfig.yaml -ENVOY_IMAGE = docker.io/envoyproxy/envoy:v1.29.3 +ENVOY_IMAGE = docker.io/envoyproxy/envoy:v1.30.1 GATEWAY_API_VERSION ?= $(shell grep "sigs.k8s.io/gateway-api" go.mod | awk '{print $$2}') # Used to supply a local Envoy docker container an IP to connect to that is running @@ -195,12 +195,10 @@ lint-codespell: CODESPELL_SKIP := $(shell cat .codespell.skip | tr \\n ',') lint-codespell: @./hack/codespell.sh --skip $(CODESPELL_SKIP) --ignore-words .codespell.ignorewords --check-filenames --check-hidden -q2 -# TODO: re-enable linting tools package once https://github.com/projectcontour/contour/issues/5077 -# is resolved .PHONY: lint-golint lint-golint: @echo Running Go linter ... - @./hack/golangci-lint run --build-tags=e2e,conformance,gcp,oidc,none + @./hack/golangci-lint run --build-tags=e2e,conformance,tools,gcp,oidc,none .PHONY: lint-yamllint lint-yamllint: diff --git a/apis/projectcontour/v1alpha1/contourconfig.go b/apis/projectcontour/v1alpha1/contourconfig.go index 2d5d2ccdd81..0d284b03706 100644 --- a/apis/projectcontour/v1alpha1/contourconfig.go +++ b/apis/projectcontour/v1alpha1/contourconfig.go @@ -87,9 +87,9 @@ type ContourConfigurationSpec struct { // FeatureFlags defines toggle to enable new contour features. // Available toggles are: - // useEndpointSlices - configures contour to fetch endpoint data - // from k8s endpoint slices. defaults to false and reading endpoint - // data from the k8s endpoints. + // useEndpointSlices - Configures contour to fetch endpoint data + // from k8s endpoint slices. defaults to true, + // If false then reads endpoint data from the k8s endpoints. FeatureFlags FeatureFlags `json:"featureFlags,omitempty"` // GlobalExternalProcessing allows envoys external processing filter diff --git a/apis/projectcontour/v1alpha1/contourconfig_helpers.go b/apis/projectcontour/v1alpha1/contourconfig_helpers.go index babadc5bc09..ffe4978267c 100644 --- a/apis/projectcontour/v1alpha1/contourconfig_helpers.go +++ b/apis/projectcontour/v1alpha1/contourconfig_helpers.go @@ -15,18 +15,16 @@ package v1alpha1 import ( "fmt" - "slices" "strconv" + "strings" "k8s.io/apimachinery/pkg/util/sets" ) -const ( - featureFlagUseEndpointSlices string = "useEndpointSlices" -) +const featureFlagUseEndpointSlices string = "useEndpointSlices" -var featureFlagsMap = map[string]bool{ - featureFlagUseEndpointSlices: true, +var featureFlagsMap = map[string]struct{}{ + featureFlagUseEndpointSlices: {}, } // Validate configuration that is not already covered by CRD validation. @@ -226,16 +224,26 @@ func (e *EnvoyTLS) SanitizedCipherSuites() []string { func (f FeatureFlags) Validate() error { for _, featureFlag := range f { - if _, found := featureFlagsMap[featureFlag]; !found { + fields := strings.Split(featureFlag, "=") + if _, found := featureFlagsMap[fields[0]]; !found { return fmt.Errorf("invalid contour configuration, unknown feature flag:%s", featureFlag) } } - return nil } func (f FeatureFlags) IsEndpointSliceEnabled() bool { - return slices.Contains(f, featureFlagUseEndpointSlices) + // only when the flag: 'useEndpointSlices=false' is exists, return false + for _, flag := range f { + if !strings.HasPrefix(flag, featureFlagUseEndpointSlices) { + continue + } + fields := strings.Split(flag, "=") + if len(fields) == 2 && strings.ToLower(fields[1]) == "false" { + return false + } + } + return true } // Validate ensures that GatewayRef namespace/name is specified. diff --git a/apis/projectcontour/v1alpha1/contourconfig_helpers_test.go b/apis/projectcontour/v1alpha1/contourconfig_helpers_test.go index 1b6fdbcb116..0db2b44c161 100644 --- a/apis/projectcontour/v1alpha1/contourconfig_helpers_test.go +++ b/apis/projectcontour/v1alpha1/contourconfig_helpers_test.go @@ -303,10 +303,26 @@ func TestFeatureFlagsValidate(t *testing.T) { expected error }{ { - name: "valid flag", + name: "valid flag: no value", flags: contour_v1alpha1.FeatureFlags{"useEndpointSlices"}, expected: nil, }, + { + name: "valid flag2: empty", + flags: contour_v1alpha1.FeatureFlags{"useEndpointSlices="}, + expected: nil, + }, + { + name: "valid flag: true", + flags: contour_v1alpha1.FeatureFlags{"useEndpointSlices=true"}, + expected: nil, + }, + { + name: "valid flag: false", + flags: contour_v1alpha1.FeatureFlags{"useEndpointSlices=false"}, + expected: nil, + }, + { name: "invalid flag", flags: contour_v1alpha1.FeatureFlags{"invalidFlag"}, @@ -331,3 +347,68 @@ func TestFeatureFlagsValidate(t *testing.T) { }) } } + +func TestFeatureFlagsIsEndpointSliceEnabled(t *testing.T) { + tests := []struct { + name string + flags contour_v1alpha1.FeatureFlags + expected bool + }{ + { + name: "valid flag: no value", + flags: contour_v1alpha1.FeatureFlags{"useEndpointSlices"}, + expected: true, + }, + { + name: "valid flag2: empty", + flags: contour_v1alpha1.FeatureFlags{"useEndpointSlices="}, + expected: true, + }, + { + name: "valid flag: true", + flags: contour_v1alpha1.FeatureFlags{"useEndpointSlices=true"}, + expected: true, + }, + { + name: "valid flag: ANY", + flags: contour_v1alpha1.FeatureFlags{"useEndpointSlices=ANY"}, + expected: true, + }, + + { + name: "empty flags", + flags: contour_v1alpha1.FeatureFlags{}, + expected: true, + }, + { + name: "empty string", + flags: contour_v1alpha1.FeatureFlags{""}, + expected: true, + }, + + { + name: "multi-flags", + flags: contour_v1alpha1.FeatureFlags{"useEndpointSlices", "otherFlag"}, + expected: true, + }, + + { + name: "valid flag: false", + flags: contour_v1alpha1.FeatureFlags{"useEndpointSlices=false"}, + expected: false, + }, + + { + name: "valid flag: FALSE", + flags: contour_v1alpha1.FeatureFlags{"useEndpointSlices=FALSE"}, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.flags.IsEndpointSliceEnabled() + assert.Equal(t, tt.expected, err) + }) + } +} diff --git a/changelogs/CHANGELOG-v1.26.3.md b/changelogs/CHANGELOG-v1.26.3.md new file mode 100644 index 00000000000..c5df25a9f6d --- /dev/null +++ b/changelogs/CHANGELOG-v1.26.3.md @@ -0,0 +1,25 @@ +We are delighted to present version v1.26.3 of Contour, our layer 7 HTTP reverse proxy for Kubernetes clusters. + +- [All Changes](#all-changes) +- [Installing/Upgrading](#installing-and-upgrading) +- [Compatible Kubernetes Versions](#compatible-kubernetes-versions) + +# All Changes + +- Updates Envoy to v1.27.4. See the release notes for v1.27.4 [here](https://www.envoyproxy.io/docs/envoy/v1.27.4/version_history/v1.27/v1.27.4). +- Updates Go to v1.20.14. See the release notes for v1.20.14 [here](https://go.dev/doc/devel/release#go1.20.minor). + +# Installing and Upgrading + +For a fresh install of Contour, consult the [getting started documentation](https://projectcontour.io/getting-started/). + +To upgrade an existing Contour installation, please consult the [upgrade documentation](https://projectcontour.io/resources/upgrading/). + + +# Compatible Kubernetes Versions + +Contour v1.26.3 is tested against Kubernetes 1.26 through 1.28. + + +# Are you a Contour user? We would love to know! +If you're using Contour and want to add your organization to our adopters list, please visit this [page](https://projectcontour.io/resources/adopters/). If you prefer to keep your organization name anonymous but still give us feedback into your usage and scenarios for Contour, please post on this [GitHub thread](https://github.com/projectcontour/contour/issues/1269). diff --git a/changelogs/CHANGELOG-v1.27.2.md b/changelogs/CHANGELOG-v1.27.2.md new file mode 100644 index 00000000000..92efc440d71 --- /dev/null +++ b/changelogs/CHANGELOG-v1.27.2.md @@ -0,0 +1,25 @@ +We are delighted to present version v1.27.2 of Contour, our layer 7 HTTP reverse proxy for Kubernetes clusters. + +- [All Changes](#all-changes) +- [Installing/Upgrading](#installing-and-upgrading) +- [Compatible Kubernetes Versions](#compatible-kubernetes-versions) + +# All Changes + +- Updates Envoy to v1.28.2. See the release notes for v1.28.2 [here](https://www.envoyproxy.io/docs/envoy/v1.28.2/version_history/v1.28/v1.28.2). +- Updates Go to v1.21.9. See the release notes for v1.21.9 [here](https://go.dev/doc/devel/release#go1.21.minor). + +# Installing and Upgrading + +For a fresh install of Contour, consult the [getting started documentation](https://projectcontour.io/getting-started/). + +To upgrade an existing Contour installation, please consult the [upgrade documentation](https://projectcontour.io/resources/upgrading/). + + +# Compatible Kubernetes Versions + +Contour v1.27.2 is tested against Kubernetes 1.26 through 1.28. + + +# Are you a Contour user? We would love to know! +If you're using Contour and want to add your organization to our adopters list, please visit this [page](https://projectcontour.io/resources/adopters/). If you prefer to keep your organization name anonymous but still give us feedback into your usage and scenarios for Contour, please post on this [GitHub thread](https://github.com/projectcontour/contour/issues/1269). diff --git a/changelogs/CHANGELOG-v1.28.3.md b/changelogs/CHANGELOG-v1.28.3.md new file mode 100644 index 00000000000..f12d9c79398 --- /dev/null +++ b/changelogs/CHANGELOG-v1.28.3.md @@ -0,0 +1,34 @@ +We are delighted to present version v1.28.3 of Contour, our layer 7 HTTP reverse proxy for Kubernetes clusters. + +- [All Changes](#all-changes) +- [Installing/Upgrading](#installing-and-upgrading) +- [Compatible Kubernetes Versions](#compatible-kubernetes-versions) + +# All Changes + +## Update Envoy to v1.29.3 + +See the release notes for v1.29.3 [here](https://www.envoyproxy.io/docs/envoy/v1.29.3/version_history/v1.29/v1.29.3). + +Note that this Envoy version retains the hop-by-hop TE header when set to `trailers`, fixing a regression seen in v1.29.0-v1.29.2 for HTTP/2, particularly gRPC. +However, this version of Contour continues to set the `envoy.reloadable_features.sanitize_te` Envoy runtime setting to `false` to ensure seamless upgrades. +This runtime setting will be removed in Contour v1.29.0. + +## Update Go to v1.21.9 + +See the release notes for v1.21.9 [here](https://go.dev/doc/devel/release#go1.21.minor). + +# Installing and Upgrading + +For a fresh install of Contour, consult the [getting started documentation](https://projectcontour.io/getting-started/). + +To upgrade an existing Contour installation, please consult the [upgrade documentation](https://projectcontour.io/resources/upgrading/). + + +# Compatible Kubernetes Versions + +Contour v1.28.3 is tested against Kubernetes 1.27 through 1.29. + + +# Are you a Contour user? We would love to know! +If you're using Contour and want to add your organization to our adopters list, please visit this [page](https://projectcontour.io/resources/adopters/). If you prefer to keep your organization name anonymous but still give us feedback into your usage and scenarios for Contour, please post on this [GitHub thread](https://github.com/projectcontour/contour/issues/1269). diff --git a/changelogs/unreleased/6149-izturn-deprecation.md b/changelogs/unreleased/6149-izturn-deprecation.md new file mode 100644 index 00000000000..2566d916f15 --- /dev/null +++ b/changelogs/unreleased/6149-izturn-deprecation.md @@ -0,0 +1,3 @@ +## Use of Endpoints API is deprecated + +Contour now uses the EndpointSlices API by default, and its usage of the Endpoints API is deprecated as of this release. Support for Endpoints, and the associated `useEndpointSlices` feature flag, will be removed in a future release. \ No newline at end of file diff --git a/changelogs/unreleased/6149-izturn-minor.md b/changelogs/unreleased/6149-izturn-minor.md new file mode 100644 index 00000000000..9fa8122d473 --- /dev/null +++ b/changelogs/unreleased/6149-izturn-minor.md @@ -0,0 +1,5 @@ + +## Use EndpointSlices by default + +Contour now uses the Kubernetes EndpointSlices API by default to determine the endpoints to configure Envoy, instead of the Endpoints API. +Note: if you need to continue using the Endpoints API, you can disable the feature flag via `featureFlags: ["useEndpointSlices=false"]` in the Contour config file or ContourConfiguration CRD. diff --git a/changelogs/unreleased/6188-lubronzhan-minor.md b/changelogs/unreleased/6188-lubronzhan-minor.md new file mode 100644 index 00000000000..73240450ad5 --- /dev/null +++ b/changelogs/unreleased/6188-lubronzhan-minor.md @@ -0,0 +1,10 @@ +## Gateway API: handle Route conflicts with HTTPRoute.Matches + +It's possible that multiple HTTPRoutes will define the same Match conditions. In this case the following logic is applied to resolve the conflict: + +- 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”. +- The Route appearing first in alphabetical order (namespace/name) for example, foo/bar is given precedence over foo/baz. + +With above ordering, any HTTPRoute that ranks lower, will be marked with below conditions accordionly +1. If only partial rules under this HTTPRoute are conflicted, it's marked with `Accepted: True` and `PartiallyInvalid: true` Conditions and Reason: `RuleMatchPartiallyConflict`. +2. If all the rules under this HTTPRoute are conflicted, it's marked with `Accepted: False` Condition and Reason `RuleMatchConflict`. diff --git a/changelogs/unreleased/6295-tsaarni-small.md b/changelogs/unreleased/6295-tsaarni-small.md new file mode 100644 index 00000000000..9747391898f --- /dev/null +++ b/changelogs/unreleased/6295-tsaarni-small.md @@ -0,0 +1 @@ +If there were no relevant resources for Contour in the watched namespaces during the startup of a follower instance of Contour, it did not reach a ready state. diff --git a/changelogs/unreleased/6297-rajatvig-small.md b/changelogs/unreleased/6297-rajatvig-small.md new file mode 100644 index 00000000000..b62c0e8b429 --- /dev/null +++ b/changelogs/unreleased/6297-rajatvig-small.md @@ -0,0 +1 @@ +Added support for enabling circuit breaker statistics tracking. diff --git a/changelogs/unreleased/6335-skriss-small.md b/changelogs/unreleased/6335-skriss-small.md new file mode 100644 index 00000000000..35dfdfe90d8 --- /dev/null +++ b/changelogs/unreleased/6335-skriss-small.md @@ -0,0 +1 @@ +Gateway API: add support for HTTPRoute's Timeouts.BackendRequest field. \ No newline at end of file diff --git a/changelogs/unreleased/6353-tico88612-small.md b/changelogs/unreleased/6353-tico88612-small.md new file mode 100644 index 00000000000..533d34e8979 --- /dev/null +++ b/changelogs/unreleased/6353-tico88612-small.md @@ -0,0 +1 @@ +Updates Envoy to v1.30.1. See the v1.30.0 release notes [here](https://www.envoyproxy.io/docs/envoy/v1.30.1/version_history/v1.30/v1.30.0) and the v1.30.1 release notes [here](https://www.envoyproxy.io/docs/envoy/v1.30.1/version_history/v1.30/v1.30.1). diff --git a/cmd/contour/gatewayprovisioner.go b/cmd/contour/gatewayprovisioner.go index b33def817df..dd6785dfff1 100644 --- a/cmd/contour/gatewayprovisioner.go +++ b/cmd/contour/gatewayprovisioner.go @@ -36,7 +36,7 @@ func registerGatewayProvisioner(app *kingpin.Application) (*kingpin.CmdClause, * provisionerConfig := &gatewayProvisionerConfig{ contourImage: "ghcr.io/projectcontour/contour:main", - envoyImage: "docker.io/envoyproxy/envoy:v1.29.3", + envoyImage: "docker.io/envoyproxy/envoy:v1.30.1", metricsBindAddress: ":8080", leaderElection: false, leaderElectionID: "0d879e31.projectcontour.io", diff --git a/examples/contour/01-crds.yaml b/examples/contour/01-crds.yaml index 01a8d19bb91..515ab555add 100644 --- a/examples/contour/01-crds.yaml +++ b/examples/contour/01-crds.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: contourconfigurations.projectcontour.io spec: preserveUnknownFields: false @@ -600,9 +600,9 @@ spec: description: |- FeatureFlags defines toggle to enable new contour features. Available toggles are: - useEndpointSlices - configures contour to fetch endpoint data - from k8s endpoint slices. defaults to false and reading endpoint - data from the k8s endpoints. + useEndpointSlices - Configures contour to fetch endpoint data + from k8s endpoint slices. defaults to true, + If false then reads endpoint data from the k8s endpoints. items: type: string type: array @@ -1533,7 +1533,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: contourdeployments.projectcontour.io spec: preserveUnknownFields: false @@ -1987,6 +1987,8 @@ spec: to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). type: string name: description: This must match the Name of a Volume. @@ -1996,6 +1998,21 @@ spec: Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false. type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + If ReadOnly is false, this field has no meaning and must be unspecified. + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + If this field is not specified, it is treated as an equivalent of Disabled. + type: string subPath: description: |- Path within the volume from which the container's volume should be mounted. @@ -2123,6 +2140,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic path: description: 'path is Optional: Used as the mounted root, rather than the full Ceph tree, default is /' @@ -2251,6 +2269,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: description: |- Name of the referent. @@ -2335,8 +2354,8 @@ spec: properties: fieldRef: description: 'Required: Selects a field of the - pod: only annotations, labels, name and namespace - are supported.' + pod: only annotations, labels, name, namespace + and uid are supported.' properties: apiVersion: description: Version of the schema the FieldPath @@ -2395,6 +2414,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object emptyDir: description: |- @@ -2486,6 +2506,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic dataSource: description: |- dataSource field can be used to specify either: @@ -2630,11 +2651,13 @@ spec: 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 @@ -2662,7 +2685,7 @@ spec: If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource exists. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ (Alpha) Using this field requires the VolumeAttributesClass feature gate to be enabled. type: string volumeMode: @@ -2706,6 +2729,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic wwids: description: |- wwids Optional: FC volume world wide identifiers (wwids) @@ -2713,6 +2737,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object flexVolume: description: |- @@ -2933,6 +2958,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic readOnly: description: |- readOnly here will force the ReadOnly setting in VolumeMounts. @@ -3117,11 +3143,13 @@ spec: 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 @@ -3200,6 +3228,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: description: |- Name of the referent. @@ -3227,7 +3256,7 @@ spec: fieldRef: description: 'Required: Selects a field of the pod: only annotations, labels, - name and namespace are supported.' + name, namespace and uid are supported.' properties: apiVersion: description: Version of the schema @@ -3291,6 +3320,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object secret: description: secret information about the secret @@ -3334,6 +3364,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: description: |- Name of the referent. @@ -3377,6 +3408,7 @@ spec: type: object type: object type: array + x-kubernetes-list-type: atomic type: object quobyte: description: quobyte represents a Quobyte mount on the host @@ -3447,6 +3479,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic pool: description: |- pool is the rados pool name. @@ -3602,6 +3635,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic optional: description: optional field specify whether the Secret or its keys must be defined @@ -4458,9 +4492,9 @@ spec: description: |- FeatureFlags defines toggle to enable new contour features. Available toggles are: - useEndpointSlices - configures contour to fetch endpoint data - from k8s endpoint slices. defaults to false and reading endpoint - data from the k8s endpoints. + useEndpointSlices - Configures contour to fetch endpoint data + from k8s endpoint slices. defaults to true, + If false then reads endpoint data from the k8s endpoints. items: type: string type: array @@ -5269,7 +5303,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: extensionservices.projectcontour.io spec: preserveUnknownFields: false @@ -5717,7 +5751,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: httpproxies.projectcontour.io spec: preserveUnknownFields: false @@ -8918,6 +8952,7 @@ spec: x-kubernetes-list-type: atomic type: object type: array + x-kubernetes-list-type: atomic type: object type: object required: @@ -8933,7 +8968,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: tlscertificatedelegations.projectcontour.io spec: preserveUnknownFields: false diff --git a/examples/contour/03-envoy.yaml b/examples/contour/03-envoy.yaml index 9ea26143515..54f64296012 100644 --- a/examples/contour/03-envoy.yaml +++ b/examples/contour/03-envoy.yaml @@ -50,7 +50,7 @@ spec: - --log-level info command: - envoy - image: docker.io/envoyproxy/envoy:v1.29.3 + image: docker.io/envoyproxy/envoy:v1.30.1 imagePullPolicy: IfNotPresent name: envoy env: diff --git a/examples/deployment/03-envoy-deployment.yaml b/examples/deployment/03-envoy-deployment.yaml index 1409b50c00b..3fd6d5e7f9e 100644 --- a/examples/deployment/03-envoy-deployment.yaml +++ b/examples/deployment/03-envoy-deployment.yaml @@ -62,7 +62,7 @@ spec: - --log-level info command: - envoy - image: docker.io/envoyproxy/envoy:v1.29.3 + image: docker.io/envoyproxy/envoy:v1.30.1 imagePullPolicy: IfNotPresent name: envoy env: diff --git a/examples/render/contour-deployment.yaml b/examples/render/contour-deployment.yaml index 45144659a0c..3e38791fef3 100644 --- a/examples/render/contour-deployment.yaml +++ b/examples/render/contour-deployment.yaml @@ -223,7 +223,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: contourconfigurations.projectcontour.io spec: preserveUnknownFields: false @@ -820,9 +820,9 @@ spec: description: |- FeatureFlags defines toggle to enable new contour features. Available toggles are: - useEndpointSlices - configures contour to fetch endpoint data - from k8s endpoint slices. defaults to false and reading endpoint - data from the k8s endpoints. + useEndpointSlices - Configures contour to fetch endpoint data + from k8s endpoint slices. defaults to true, + If false then reads endpoint data from the k8s endpoints. items: type: string type: array @@ -1753,7 +1753,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: contourdeployments.projectcontour.io spec: preserveUnknownFields: false @@ -2207,6 +2207,8 @@ spec: to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). type: string name: description: This must match the Name of a Volume. @@ -2216,6 +2218,21 @@ spec: Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false. type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + If ReadOnly is false, this field has no meaning and must be unspecified. + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + If this field is not specified, it is treated as an equivalent of Disabled. + type: string subPath: description: |- Path within the volume from which the container's volume should be mounted. @@ -2343,6 +2360,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic path: description: 'path is Optional: Used as the mounted root, rather than the full Ceph tree, default is /' @@ -2471,6 +2489,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: description: |- Name of the referent. @@ -2555,8 +2574,8 @@ spec: properties: fieldRef: description: 'Required: Selects a field of the - pod: only annotations, labels, name and namespace - are supported.' + pod: only annotations, labels, name, namespace + and uid are supported.' properties: apiVersion: description: Version of the schema the FieldPath @@ -2615,6 +2634,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object emptyDir: description: |- @@ -2706,6 +2726,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic dataSource: description: |- dataSource field can be used to specify either: @@ -2850,11 +2871,13 @@ spec: 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 @@ -2882,7 +2905,7 @@ spec: If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource exists. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ (Alpha) Using this field requires the VolumeAttributesClass feature gate to be enabled. type: string volumeMode: @@ -2926,6 +2949,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic wwids: description: |- wwids Optional: FC volume world wide identifiers (wwids) @@ -2933,6 +2957,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object flexVolume: description: |- @@ -3153,6 +3178,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic readOnly: description: |- readOnly here will force the ReadOnly setting in VolumeMounts. @@ -3337,11 +3363,13 @@ spec: 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 @@ -3420,6 +3448,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: description: |- Name of the referent. @@ -3447,7 +3476,7 @@ spec: fieldRef: description: 'Required: Selects a field of the pod: only annotations, labels, - name and namespace are supported.' + name, namespace and uid are supported.' properties: apiVersion: description: Version of the schema @@ -3511,6 +3540,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object secret: description: secret information about the secret @@ -3554,6 +3584,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: description: |- Name of the referent. @@ -3597,6 +3628,7 @@ spec: type: object type: object type: array + x-kubernetes-list-type: atomic type: object quobyte: description: quobyte represents a Quobyte mount on the host @@ -3667,6 +3699,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic pool: description: |- pool is the rados pool name. @@ -3822,6 +3855,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic optional: description: optional field specify whether the Secret or its keys must be defined @@ -4678,9 +4712,9 @@ spec: description: |- FeatureFlags defines toggle to enable new contour features. Available toggles are: - useEndpointSlices - configures contour to fetch endpoint data - from k8s endpoint slices. defaults to false and reading endpoint - data from the k8s endpoints. + useEndpointSlices - Configures contour to fetch endpoint data + from k8s endpoint slices. defaults to true, + If false then reads endpoint data from the k8s endpoints. items: type: string type: array @@ -5489,7 +5523,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: extensionservices.projectcontour.io spec: preserveUnknownFields: false @@ -5937,7 +5971,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: httpproxies.projectcontour.io spec: preserveUnknownFields: false @@ -9138,6 +9172,7 @@ spec: x-kubernetes-list-type: atomic type: object type: array + x-kubernetes-list-type: atomic type: object type: object required: @@ -9153,7 +9188,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: tlscertificatedelegations.projectcontour.io spec: preserveUnknownFields: false @@ -9870,7 +9905,7 @@ spec: - --log-level info command: - envoy - image: docker.io/envoyproxy/envoy:v1.29.3 + image: docker.io/envoyproxy/envoy:v1.30.1 imagePullPolicy: IfNotPresent name: envoy env: diff --git a/examples/render/contour-gateway-provisioner.yaml b/examples/render/contour-gateway-provisioner.yaml index a80bd151325..7a75e40d992 100644 --- a/examples/render/contour-gateway-provisioner.yaml +++ b/examples/render/contour-gateway-provisioner.yaml @@ -14,7 +14,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: contourconfigurations.projectcontour.io spec: preserveUnknownFields: false @@ -611,9 +611,9 @@ spec: description: |- FeatureFlags defines toggle to enable new contour features. Available toggles are: - useEndpointSlices - configures contour to fetch endpoint data - from k8s endpoint slices. defaults to false and reading endpoint - data from the k8s endpoints. + useEndpointSlices - Configures contour to fetch endpoint data + from k8s endpoint slices. defaults to true, + If false then reads endpoint data from the k8s endpoints. items: type: string type: array @@ -1544,7 +1544,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: contourdeployments.projectcontour.io spec: preserveUnknownFields: false @@ -1998,6 +1998,8 @@ spec: to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). type: string name: description: This must match the Name of a Volume. @@ -2007,6 +2009,21 @@ spec: Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false. type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + If ReadOnly is false, this field has no meaning and must be unspecified. + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + If this field is not specified, it is treated as an equivalent of Disabled. + type: string subPath: description: |- Path within the volume from which the container's volume should be mounted. @@ -2134,6 +2151,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic path: description: 'path is Optional: Used as the mounted root, rather than the full Ceph tree, default is /' @@ -2262,6 +2280,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: description: |- Name of the referent. @@ -2346,8 +2365,8 @@ spec: properties: fieldRef: description: 'Required: Selects a field of the - pod: only annotations, labels, name and namespace - are supported.' + pod: only annotations, labels, name, namespace + and uid are supported.' properties: apiVersion: description: Version of the schema the FieldPath @@ -2406,6 +2425,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object emptyDir: description: |- @@ -2497,6 +2517,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic dataSource: description: |- dataSource field can be used to specify either: @@ -2641,11 +2662,13 @@ spec: 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 @@ -2673,7 +2696,7 @@ spec: If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource exists. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ (Alpha) Using this field requires the VolumeAttributesClass feature gate to be enabled. type: string volumeMode: @@ -2717,6 +2740,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic wwids: description: |- wwids Optional: FC volume world wide identifiers (wwids) @@ -2724,6 +2748,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object flexVolume: description: |- @@ -2944,6 +2969,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic readOnly: description: |- readOnly here will force the ReadOnly setting in VolumeMounts. @@ -3128,11 +3154,13 @@ spec: 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 @@ -3211,6 +3239,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: description: |- Name of the referent. @@ -3238,7 +3267,7 @@ spec: fieldRef: description: 'Required: Selects a field of the pod: only annotations, labels, - name and namespace are supported.' + name, namespace and uid are supported.' properties: apiVersion: description: Version of the schema @@ -3302,6 +3331,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object secret: description: secret information about the secret @@ -3345,6 +3375,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: description: |- Name of the referent. @@ -3388,6 +3419,7 @@ spec: type: object type: object type: array + x-kubernetes-list-type: atomic type: object quobyte: description: quobyte represents a Quobyte mount on the host @@ -3458,6 +3490,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic pool: description: |- pool is the rados pool name. @@ -3613,6 +3646,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic optional: description: optional field specify whether the Secret or its keys must be defined @@ -4469,9 +4503,9 @@ spec: description: |- FeatureFlags defines toggle to enable new contour features. Available toggles are: - useEndpointSlices - configures contour to fetch endpoint data - from k8s endpoint slices. defaults to false and reading endpoint - data from the k8s endpoints. + useEndpointSlices - Configures contour to fetch endpoint data + from k8s endpoint slices. defaults to true, + If false then reads endpoint data from the k8s endpoints. items: type: string type: array @@ -5280,7 +5314,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: extensionservices.projectcontour.io spec: preserveUnknownFields: false @@ -5728,7 +5762,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: httpproxies.projectcontour.io spec: preserveUnknownFields: false @@ -8929,6 +8963,7 @@ spec: x-kubernetes-list-type: atomic type: object type: array + x-kubernetes-list-type: atomic type: object type: object required: @@ -8944,7 +8979,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: tlscertificatedelegations.projectcontour.io spec: preserveUnknownFields: false diff --git a/examples/render/contour-gateway.yaml b/examples/render/contour-gateway.yaml index baa268ece4d..65521927f05 100644 --- a/examples/render/contour-gateway.yaml +++ b/examples/render/contour-gateway.yaml @@ -39,7 +39,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: contourconfigurations.projectcontour.io spec: preserveUnknownFields: false @@ -636,9 +636,9 @@ spec: description: |- FeatureFlags defines toggle to enable new contour features. Available toggles are: - useEndpointSlices - configures contour to fetch endpoint data - from k8s endpoint slices. defaults to false and reading endpoint - data from the k8s endpoints. + useEndpointSlices - Configures contour to fetch endpoint data + from k8s endpoint slices. defaults to true, + If false then reads endpoint data from the k8s endpoints. items: type: string type: array @@ -1569,7 +1569,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: contourdeployments.projectcontour.io spec: preserveUnknownFields: false @@ -2023,6 +2023,8 @@ spec: to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). type: string name: description: This must match the Name of a Volume. @@ -2032,6 +2034,21 @@ spec: Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false. type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + If ReadOnly is false, this field has no meaning and must be unspecified. + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + If this field is not specified, it is treated as an equivalent of Disabled. + type: string subPath: description: |- Path within the volume from which the container's volume should be mounted. @@ -2159,6 +2176,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic path: description: 'path is Optional: Used as the mounted root, rather than the full Ceph tree, default is /' @@ -2287,6 +2305,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: description: |- Name of the referent. @@ -2371,8 +2390,8 @@ spec: properties: fieldRef: description: 'Required: Selects a field of the - pod: only annotations, labels, name and namespace - are supported.' + pod: only annotations, labels, name, namespace + and uid are supported.' properties: apiVersion: description: Version of the schema the FieldPath @@ -2431,6 +2450,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object emptyDir: description: |- @@ -2522,6 +2542,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic dataSource: description: |- dataSource field can be used to specify either: @@ -2666,11 +2687,13 @@ spec: 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 @@ -2698,7 +2721,7 @@ spec: If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource exists. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ (Alpha) Using this field requires the VolumeAttributesClass feature gate to be enabled. type: string volumeMode: @@ -2742,6 +2765,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic wwids: description: |- wwids Optional: FC volume world wide identifiers (wwids) @@ -2749,6 +2773,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object flexVolume: description: |- @@ -2969,6 +2994,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic readOnly: description: |- readOnly here will force the ReadOnly setting in VolumeMounts. @@ -3153,11 +3179,13 @@ spec: 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 @@ -3236,6 +3264,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: description: |- Name of the referent. @@ -3263,7 +3292,7 @@ spec: fieldRef: description: 'Required: Selects a field of the pod: only annotations, labels, - name and namespace are supported.' + name, namespace and uid are supported.' properties: apiVersion: description: Version of the schema @@ -3327,6 +3356,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object secret: description: secret information about the secret @@ -3370,6 +3400,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: description: |- Name of the referent. @@ -3413,6 +3444,7 @@ spec: type: object type: object type: array + x-kubernetes-list-type: atomic type: object quobyte: description: quobyte represents a Quobyte mount on the host @@ -3483,6 +3515,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic pool: description: |- pool is the rados pool name. @@ -3638,6 +3671,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic optional: description: optional field specify whether the Secret or its keys must be defined @@ -4494,9 +4528,9 @@ spec: description: |- FeatureFlags defines toggle to enable new contour features. Available toggles are: - useEndpointSlices - configures contour to fetch endpoint data - from k8s endpoint slices. defaults to false and reading endpoint - data from the k8s endpoints. + useEndpointSlices - Configures contour to fetch endpoint data + from k8s endpoint slices. defaults to true, + If false then reads endpoint data from the k8s endpoints. items: type: string type: array @@ -5305,7 +5339,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: extensionservices.projectcontour.io spec: preserveUnknownFields: false @@ -5753,7 +5787,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: httpproxies.projectcontour.io spec: preserveUnknownFields: false @@ -8954,6 +8988,7 @@ spec: x-kubernetes-list-type: atomic type: object type: array + x-kubernetes-list-type: atomic type: object type: object required: @@ -8969,7 +9004,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: tlscertificatedelegations.projectcontour.io spec: preserveUnknownFields: false @@ -9674,7 +9709,7 @@ spec: - --log-level info command: - envoy - image: docker.io/envoyproxy/envoy:v1.29.3 + image: docker.io/envoyproxy/envoy:v1.30.1 imagePullPolicy: IfNotPresent name: envoy env: diff --git a/examples/render/contour.yaml b/examples/render/contour.yaml index 2a269385575..3638fd1262f 100644 --- a/examples/render/contour.yaml +++ b/examples/render/contour.yaml @@ -223,7 +223,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: contourconfigurations.projectcontour.io spec: preserveUnknownFields: false @@ -820,9 +820,9 @@ spec: description: |- FeatureFlags defines toggle to enable new contour features. Available toggles are: - useEndpointSlices - configures contour to fetch endpoint data - from k8s endpoint slices. defaults to false and reading endpoint - data from the k8s endpoints. + useEndpointSlices - Configures contour to fetch endpoint data + from k8s endpoint slices. defaults to true, + If false then reads endpoint data from the k8s endpoints. items: type: string type: array @@ -1753,7 +1753,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: contourdeployments.projectcontour.io spec: preserveUnknownFields: false @@ -2207,6 +2207,8 @@ spec: to container and the other way around. When not set, MountPropagationNone is used. This field is beta in 1.10. + When RecursiveReadOnly is set to IfPossible or to Enabled, MountPropagation must be None or unspecified + (which defaults to None). type: string name: description: This must match the Name of a Volume. @@ -2216,6 +2218,21 @@ spec: Mounted read-only if true, read-write otherwise (false or unspecified). Defaults to false. type: boolean + recursiveReadOnly: + description: |- + RecursiveReadOnly specifies whether read-only mounts should be handled + recursively. + If ReadOnly is false, this field has no meaning and must be unspecified. + If ReadOnly is true, and this field is set to Disabled, the mount is not made + recursively read-only. If this field is set to IfPossible, the mount is made + recursively read-only, if it is supported by the container runtime. If this + field is set to Enabled, the mount is made recursively read-only if it is + supported by the container runtime, otherwise the pod will not be started and + an error will be generated to indicate the reason. + If this field is set to IfPossible or Enabled, MountPropagation must be set to + None (or be unspecified, which defaults to None). + If this field is not specified, it is treated as an equivalent of Disabled. + type: string subPath: description: |- Path within the volume from which the container's volume should be mounted. @@ -2343,6 +2360,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic path: description: 'path is Optional: Used as the mounted root, rather than the full Ceph tree, default is /' @@ -2471,6 +2489,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: description: |- Name of the referent. @@ -2555,8 +2574,8 @@ spec: properties: fieldRef: description: 'Required: Selects a field of the - pod: only annotations, labels, name and namespace - are supported.' + pod: only annotations, labels, name, namespace + and uid are supported.' properties: apiVersion: description: Version of the schema the FieldPath @@ -2615,6 +2634,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object emptyDir: description: |- @@ -2706,6 +2726,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic dataSource: description: |- dataSource field can be used to specify either: @@ -2850,11 +2871,13 @@ spec: 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 @@ -2882,7 +2905,7 @@ spec: If the resource referred to by volumeAttributesClass does not exist, this PersistentVolumeClaim will be set to a Pending state, as reflected by the modifyVolumeStatus field, until such as a resource exists. - More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#volumeattributesclass + More info: https://kubernetes.io/docs/concepts/storage/volume-attributes-classes/ (Alpha) Using this field requires the VolumeAttributesClass feature gate to be enabled. type: string volumeMode: @@ -2926,6 +2949,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic wwids: description: |- wwids Optional: FC volume world wide identifiers (wwids) @@ -2933,6 +2957,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic type: object flexVolume: description: |- @@ -3153,6 +3178,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic readOnly: description: |- readOnly here will force the ReadOnly setting in VolumeMounts. @@ -3337,11 +3363,13 @@ spec: 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 @@ -3420,6 +3448,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: description: |- Name of the referent. @@ -3447,7 +3476,7 @@ spec: fieldRef: description: 'Required: Selects a field of the pod: only annotations, labels, - name and namespace are supported.' + name, namespace and uid are supported.' properties: apiVersion: description: Version of the schema @@ -3511,6 +3540,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic type: object secret: description: secret information about the secret @@ -3554,6 +3584,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic name: description: |- Name of the referent. @@ -3597,6 +3628,7 @@ spec: type: object type: object type: array + x-kubernetes-list-type: atomic type: object quobyte: description: quobyte represents a Quobyte mount on the host @@ -3667,6 +3699,7 @@ spec: items: type: string type: array + x-kubernetes-list-type: atomic pool: description: |- pool is the rados pool name. @@ -3822,6 +3855,7 @@ spec: - path type: object type: array + x-kubernetes-list-type: atomic optional: description: optional field specify whether the Secret or its keys must be defined @@ -4678,9 +4712,9 @@ spec: description: |- FeatureFlags defines toggle to enable new contour features. Available toggles are: - useEndpointSlices - configures contour to fetch endpoint data - from k8s endpoint slices. defaults to false and reading endpoint - data from the k8s endpoints. + useEndpointSlices - Configures contour to fetch endpoint data + from k8s endpoint slices. defaults to true, + If false then reads endpoint data from the k8s endpoints. items: type: string type: array @@ -5489,7 +5523,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: extensionservices.projectcontour.io spec: preserveUnknownFields: false @@ -5937,7 +5971,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: httpproxies.projectcontour.io spec: preserveUnknownFields: false @@ -9138,6 +9172,7 @@ spec: x-kubernetes-list-type: atomic type: object type: array + x-kubernetes-list-type: atomic type: object type: object required: @@ -9153,7 +9188,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.15.0 name: tlscertificatedelegations.projectcontour.io spec: preserveUnknownFields: false @@ -9858,7 +9893,7 @@ spec: - --log-level info command: - envoy - image: docker.io/envoyproxy/envoy:v1.29.3 + image: docker.io/envoyproxy/envoy:v1.30.1 imagePullPolicy: IfNotPresent name: envoy env: diff --git a/go.mod b/go.mod index 5f19f2b61c9..2cd7aa894eb 100644 --- a/go.mod +++ b/go.mod @@ -19,32 +19,32 @@ require ( github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 github.com/onsi/ginkgo/v2 v2.17.1 - github.com/onsi/gomega v1.32.0 + github.com/onsi/gomega v1.33.0 github.com/projectcontour/yages v0.1.0 github.com/prometheus/client_golang v1.19.0 github.com/prometheus/client_model v0.6.1 - github.com/prometheus/common v0.52.2 + github.com/prometheus/common v0.53.0 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 github.com/tsaarni/certyaml v0.9.3 - github.com/vektra/mockery/v2 v2.42.2 + github.com/vektra/mockery/v2 v2.42.3 go.uber.org/automaxprocs v1.5.3 golang.org/x/oauth2 v0.19.0 gonum.org/v1/plot v0.14.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de - google.golang.org/grpc v1.63.0 + google.golang.org/grpc v1.63.2 google.golang.org/protobuf v1.33.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.29.3 - k8s.io/apiextensions-apiserver v0.29.3 - k8s.io/apimachinery v0.29.3 - k8s.io/client-go v0.29.3 + k8s.io/api v0.30.0 + k8s.io/apiextensions-apiserver v0.30.0 + k8s.io/apimachinery v0.30.0 + k8s.io/client-go v0.30.0 k8s.io/klog/v2 v2.120.1 k8s.io/utils v0.0.0-20240102154912-e7106e64919e - sigs.k8s.io/controller-runtime v0.17.2 - sigs.k8s.io/controller-tools v0.14.0 + sigs.k8s.io/controller-runtime v0.18.0 + sigs.k8s.io/controller-tools v0.15.0 sigs.k8s.io/gateway-api v1.0.0 - sigs.k8s.io/kustomize/kyaml v0.16.0 + sigs.k8s.io/kustomize/kyaml v0.17.0 sigs.k8s.io/yaml v1.4.0 ) @@ -61,7 +61,7 @@ require ( github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect github.com/evanphx/json-patch v5.7.0+incompatible // indirect - github.com/evanphx/json-patch/v5 v5.8.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/fatih/color v1.16.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect @@ -119,26 +119,26 @@ require ( github.com/subosito/gotenv v1.4.2 // indirect github.com/tsaarni/x500dn v1.0.0 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect - golang.org/x/crypto v0.21.0 // indirect + golang.org/x/crypto v0.22.0 // indirect golang.org/x/exp v0.0.0-20231226003508-02704c960a9b // indirect golang.org/x/image v0.11.0 // indirect - golang.org/x/mod v0.14.0 // indirect - golang.org/x/net v0.22.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/term v0.18.0 // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.17.0 // indirect + golang.org/x/tools v0.20.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - k8s.io/component-base v0.29.3 // indirect k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 // indirect k8s.io/klog v1.0.0 // indirect - k8s.io/kube-openapi v0.0.0-20240103051144-eec4567ac022 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect ) diff --git a/go.sum b/go.sum index d4cb09607b5..7b01fc97690 100644 --- a/go.sum +++ b/go.sum @@ -105,8 +105,8 @@ github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.8.0 h1:lRj6N9Nci7MvzrXuX6HFzU8XjmhPiXPlsKEy1u0KQro= -github.com/evanphx/json-patch/v5 v5.8.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= @@ -302,8 +302,8 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= -github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= -github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg= +github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE= +github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -325,8 +325,8 @@ github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdU github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.52.2 h1:LW8Vk7BccEdONfrJBDffQGRtpSzi5CQaRZGtboOO2ck= -github.com/prometheus/common v0.52.2/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q= +github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE= +github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -377,8 +377,8 @@ github.com/tsaarni/certyaml v0.9.3 h1:m8HHbuUzWVUOmv8IQU9HgVZZ8r5ICExKm++54DJKCs github.com/tsaarni/certyaml v0.9.3/go.mod h1:hhuU1qYr5re488geArUP4gZWqMUMqGlj4HA2qUyGYLk= github.com/tsaarni/x500dn v1.0.0 h1:LvaWTkqRpse4VHBhB5uwf3wytokK4vF9IOyNAEyiA+U= github.com/tsaarni/x500dn v1.0.0/go.mod h1:QaHa3EcUKC4dfCAZmj8+ZRGLKukWgpGv9H3oOCsAbcE= -github.com/vektra/mockery/v2 v2.42.2 h1:SSIvwnXBl9EynibBZhQ/hBXuV5TYPdZFIqkm4UkQHU4= -github.com/vektra/mockery/v2 v2.42.2/go.mod h1:XNTE9RIu3deGAGQRVjP1VZxGpQNm0YedZx4oDs3prr8= +github.com/vektra/mockery/v2 v2.42.3 h1:ikkdzvlLNHVzKh6cEyOHFY+ItZXkZMvo76mp1HS1T9Q= +github.com/vektra/mockery/v2 v2.42.3/go.mod h1:XNTE9RIu3deGAGQRVjP1VZxGpQNm0YedZx4oDs3prr8= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -412,8 +412,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -453,8 +453,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -489,8 +489,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= 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= @@ -514,8 +514,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -561,13 +561,13 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= 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= @@ -638,8 +638,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 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.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= -golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= 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= @@ -735,8 +735,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8= -google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= 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= @@ -777,16 +777,14 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt 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= honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= -k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= -k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= -k8s.io/apiextensions-apiserver v0.29.3 h1:9HF+EtZaVpFjStakF4yVufnXGPRppWFEQ87qnO91YeI= -k8s.io/apiextensions-apiserver v0.29.3/go.mod h1:po0XiY5scnpJfFizNGo6puNU6Fq6D70UJY2Cb2KwAVc= -k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= -k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= -k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= -k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= -k8s.io/component-base v0.29.3 h1:Oq9/nddUxlnrCuuR2K/jp6aflVvc0uDvxMzAWxnGzAo= -k8s.io/component-base v0.29.3/go.mod h1:Yuj33XXjuOk2BAaHsIGHhCKZQAgYKhqIxIjIr2UXYio= +k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA= +k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE= +k8s.io/apiextensions-apiserver v0.30.0 h1:jcZFKMqnICJfRxTgnC4E+Hpcq8UEhT8B2lhBcQ+6uAs= +k8s.io/apiextensions-apiserver v0.30.0/go.mod h1:N9ogQFGcrbWqAY9p2mUAL5mGxsLqwgtUce127VtRX5Y= +k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA= +k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ= +k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY= k8s.io/gengo v0.0.0-20201203183100-97869a43a9d9/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 h1:pWEwq4Asjm4vjW7vcsmijwBhOr1/shsbSYiWXmNGlks= k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= @@ -796,8 +794,8 @@ k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240103051144-eec4567ac022 h1:avRdiaB03v88Mfvum2S3BBwkNuTlmuar4LlfO9Hajko= -k8s.io/kube-openapi v0.0.0-20240103051144-eec4567ac022/go.mod h1:sIV51WBTkZrlGOJMCDZDA1IaPBUDTulPpD4y7oe038k= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= k8s.io/utils v0.0.0-20240102154912-e7106e64919e h1:eQ/4ljkx21sObifjzXwlPKpdGLrCfRziVtos3ofG/sQ= k8s.io/utils v0.0.0-20240102154912-e7106e64919e/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= @@ -805,16 +803,16 @@ rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= 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.17.2 h1:FwHwD1CTUemg0pW2otk7/U5/i5m2ymzvOXdbeGOUvw0= -sigs.k8s.io/controller-runtime v0.17.2/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s= -sigs.k8s.io/controller-tools v0.14.0 h1:rnNoCC5wSXlrNoBKKzL70LNJKIQKEzT6lloG6/LF73A= -sigs.k8s.io/controller-tools v0.14.0/go.mod h1:TV7uOtNNnnR72SpzhStvPkoS/U5ir0nMudrkrC4M9Sc= +sigs.k8s.io/controller-runtime v0.18.0 h1:Z7jKuX784TQSUL1TIyeuF7j8KXZ4RtSX0YgtjKcSTME= +sigs.k8s.io/controller-runtime v0.18.0/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw= +sigs.k8s.io/controller-tools v0.15.0 h1:4dxdABXGDhIa68Fiwaif0vcu32xfwmgQ+w8p+5CxoAI= +sigs.k8s.io/controller-tools v0.15.0/go.mod h1:8zUSS2T8Hx0APCNRhJWbS3CAQEbIxLa07khzh7pZmXM= sigs.k8s.io/gateway-api v1.0.0 h1:iPTStSv41+d9p0xFydll6d7f7MOBGuqXM6p2/zVYMAs= sigs.k8s.io/gateway-api v1.0.0/go.mod h1:4cUgr0Lnp5FZ0Cdq8FdRwCvpiWws7LVhLHGIudLlf4c= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/kustomize/kyaml v0.16.0 h1:6J33uKSoATlKZH16unr2XOhDI+otoe2sR3M8PDzW3K0= -sigs.k8s.io/kustomize/kyaml v0.16.0/go.mod h1:xOK/7i+vmE14N2FdFyugIshB8eF6ALpy7jI87Q2nRh4= +sigs.k8s.io/kustomize/kyaml v0.17.0 h1:G2bWs03V9Ur2PinHLzTUJ8Ded+30SzXZKiO92SRDs3c= +sigs.k8s.io/kustomize/kyaml v0.17.0/go.mod h1:6lxkYF1Cv9Ic8g/N7I86cvxNc5iinUo/P2vKsHNmpyE= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/internal/contour/handler.go b/internal/contour/handler.go index 2e9e6bee6eb..a66d590f158 100644 --- a/internal/contour/handler.go +++ b/internal/contour/handler.go @@ -153,6 +153,12 @@ func (e *EventHandler) Start(ctx context.Context) error { return } + // It may be that there are no resources at all to process in watched namespaces. + // Initial (empty) DAG build is not needed and we can mark it as built immediately to allow the XDS server to start. + if e.syncTracker.HasSynced() { + e.initialDagBuilt.Store(true) + } + for { // In the main loop one of four things can happen. // 1. We're waiting for an event on op, stop, or pending, noting that @@ -190,6 +196,12 @@ func (e *EventHandler) Start(ctx context.Context) error { if updateOpAdd, ok := op.(opAdd); ok { if updateOpAdd.isInInitialList { e.syncTracker.Finished() + + // If this was the last event in the initial list but none of the events triggered DAG rebuild, + // then we can mark the (empty) DAG as built to allow the XDS server to start. + if e.syncTracker.HasSynced() && timer == nil { + e.initialDagBuilt.Store(true) + } } } case <-pending: diff --git a/internal/dag/builder_test.go b/internal/dag/builder_test.go index fb7a8996dce..4ef75d0f2d6 100644 --- a/internal/dag/builder_test.go +++ b/internal/dag/builder_test.go @@ -3398,9 +3398,24 @@ func TestDAGInsertGatewayAPI(t *testing.T) { gateway: gatewayHTTPAllNamespaces, objs: []any{ kuardService, - makeHTTPRouteWithTimeouts("5s", "5s"), + makeHTTPRouteWithTimeouts("5s", "4s"), }, - want: listeners(), + want: listeners( + &Listener{ + Name: "http-80", + VirtualHosts: virtualhosts( + virtualhost("test.projectcontour.io", + &Route{ + PathMatchCondition: prefixString("/"), + Clusters: clustersWeight(service(kuardService)), + TimeoutPolicy: RouteTimeoutPolicy{ + ResponseTimeout: timeout.DurationSetting(4 * time.Second), + }, + }, + ), + ), + }, + ), }, "HTTPRoute rule with backendRequest timeout only": { @@ -3410,7 +3425,22 @@ func TestDAGInsertGatewayAPI(t *testing.T) { kuardService, makeHTTPRouteWithTimeouts("", "5s"), }, - want: listeners(), + want: listeners( + &Listener{ + Name: "http-80", + VirtualHosts: virtualhosts( + virtualhost("test.projectcontour.io", + &Route{ + PathMatchCondition: prefixString("/"), + Clusters: clustersWeight(service(kuardService)), + TimeoutPolicy: RouteTimeoutPolicy{ + ResponseTimeout: timeout.DurationSetting(5 * time.Second), + }, + }, + ), + ), + }, + ), }, "HTTPRoute rule with invalid request timeout": { gatewayclass: validClass, @@ -3421,6 +3451,25 @@ func TestDAGInsertGatewayAPI(t *testing.T) { }, want: listeners(), }, + "HTTPRoute rule with invalid backend request timeout": { + gatewayclass: validClass, + gateway: gatewayHTTPAllNamespaces, + objs: []any{ + kuardService, + makeHTTPRouteWithTimeouts("5s", "invalid"), + }, + want: listeners(), + }, + + "HTTPRoute rule with backend request timeout greater than request timeout": { + gatewayclass: validClass, + gateway: gatewayHTTPAllNamespaces, + objs: []any{ + kuardService, + makeHTTPRouteWithTimeouts("5s", "6s"), + }, + want: listeners(), + }, "HTTPRoute with BackendTLSPolicy": { gatewayclass: validClass, diff --git a/internal/dag/dag.go b/internal/dag/dag.go index dd93cf127f2..03b9b5fc580 100644 --- a/internal/dag/dag.go +++ b/internal/dag/dag.go @@ -773,13 +773,25 @@ type VirtualHost struct { Routes map[string]*Route } +// Add route to VirtualHosts.Routes map. func (v *VirtualHost) AddRoute(route *Route) { if v.Routes == nil { v.Routes = make(map[string]*Route) } + v.Routes[conditionsToString(route)] = route } +// HasConflictRoute returns true if there is existing Path + Headers +// + QueryParams combination match this route candidate and also they are same kind of Route. +func (v *VirtualHost) HasConflictRoute(route *Route) bool { + // If match exist and kind is the same kind, return true. + if r, ok := v.Routes[conditionsToString(route)]; ok && r.Kind == route.Kind { + return true + } + return false +} + func conditionsToString(r *Route) string { s := []string{r.PathMatchCondition.String()} for _, cond := range r.HeaderMatchConditions { diff --git a/internal/dag/dag_test.go b/internal/dag/dag_test.go index 3849ce71e50..3e161d26274 100644 --- a/internal/dag/dag_test.go +++ b/internal/dag/dag_test.go @@ -265,3 +265,200 @@ func TestServiceClusterRebalance(t *testing.T) { }) } } + +func TestHasConflictRouteForVirtualHost(t *testing.T) { + cases := map[string]struct { + vHost VirtualHost + rs []Route + expectConflict bool + }{ + "2 different routes with same path and header, with same kind, expect conflict": { + vHost: VirtualHost{ + Routes: map[string]*Route{}, + }, + rs: []Route{ + { + Kind: KindHTTPRoute, + Name: "a", + Namespace: "b", + PathMatchCondition: prefixSegment("/path1"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: ":authority", MatchType: HeaderMatchTypeRegex, Value: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?\\.example\\.com(:[0-9]+)?"}, + }, + }, + { + Kind: KindHTTPRoute, + Name: "b", + Namespace: "c", + PathMatchCondition: prefixSegment("/path1"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: ":authority", MatchType: HeaderMatchTypeRegex, Value: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?\\.example\\.com(:[0-9]+)?"}, + }, + }, + }, + expectConflict: true, + }, + "2 different routes with same path and header and query params, with same kind, expect conflict": { + vHost: VirtualHost{ + Routes: map[string]*Route{}, + }, + rs: []Route{ + { + Kind: KindHTTPRoute, + Name: "a", + Namespace: "b", + PathMatchCondition: prefixSegment("/path1"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: ":authority", MatchType: HeaderMatchTypeRegex, Value: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?\\.example\\.com(:[0-9]+)?"}, + }, + QueryParamMatchConditions: []QueryParamMatchCondition{ + {Name: "param-1", Value: "value-1", MatchType: QueryParamMatchTypeExact}, + }, + }, + { + Kind: KindHTTPRoute, + Name: "c", + Namespace: "d", + PathMatchCondition: prefixSegment("/path1"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: ":authority", MatchType: HeaderMatchTypeRegex, Value: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?\\.example\\.com(:[0-9]+)?"}, + }, + QueryParamMatchConditions: []QueryParamMatchCondition{ + {Name: "param-1", Value: "value-1", MatchType: QueryParamMatchTypeExact}, + }, + }, + }, + expectConflict: true, + }, + "2 different routes with same path and header, but different kind, expect no conflict": { + vHost: VirtualHost{ + Routes: map[string]*Route{}, + }, + rs: []Route{ + { + Kind: KindGRPCRoute, + Name: "a", + Namespace: "b", + PathMatchCondition: prefixSegment("/path1"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: ":authority", MatchType: HeaderMatchTypeRegex, Value: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?\\.example\\.com(:[0-9]+)?"}, + }, + }, + { + Kind: KindHTTPRoute, + Name: "b", + Namespace: "c", + PathMatchCondition: prefixSegment("/path1"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: ":authority", MatchType: HeaderMatchTypeRegex, Value: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?\\.example\\.com(:[0-9]+)?"}, + }, + }, + }, + expectConflict: false, + }, + "2 different routes with same path and header and query params, but different kind, expect no conflict": { + vHost: VirtualHost{ + Routes: map[string]*Route{}, + }, + rs: []Route{ + { + Kind: KindHTTPRoute, + Name: "a", + Namespace: "b", + PathMatchCondition: prefixSegment("/path1"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: ":authority", MatchType: HeaderMatchTypeRegex, Value: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?\\.example\\.com(:[0-9]+)?"}, + }, + QueryParamMatchConditions: []QueryParamMatchCondition{ + {Name: "param-1", Value: "value-1", MatchType: QueryParamMatchTypeExact}, + }, + }, + { + Kind: KindGRPCRoute, + Name: "c", + Namespace: "d", + PathMatchCondition: prefixSegment("/path1"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: ":authority", MatchType: HeaderMatchTypeRegex, Value: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?\\.example\\.com(:[0-9]+)?"}, + }, + QueryParamMatchConditions: []QueryParamMatchCondition{ + {Name: "param-1", Value: "value-1", MatchType: QueryParamMatchTypeExact}, + }, + }, + }, + expectConflict: false, + }, + "2 different routes with same path and header, but different query params, with same kind, don't expect conflict": { + vHost: VirtualHost{ + Routes: map[string]*Route{}, + }, + rs: []Route{ + { + Kind: KindHTTPRoute, + Name: "a", + Namespace: "b", + PathMatchCondition: prefixSegment("/path1"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: ":authority", MatchType: HeaderMatchTypeRegex, Value: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?\\.example\\.com(:[0-9]+)?"}, + }, + QueryParamMatchConditions: []QueryParamMatchCondition{ + {Name: "param-1", Value: "value-1", MatchType: QueryParamMatchTypeExact}, + }, + }, + { + Kind: KindHTTPRoute, + Name: "c", + Namespace: "d", + PathMatchCondition: prefixSegment("/path1"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: ":authority", MatchType: HeaderMatchTypeRegex, Value: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?\\.example\\.com(:[0-9]+)?"}, + }, + QueryParamMatchConditions: []QueryParamMatchCondition{ + {Name: "param-2different", Value: "value-2different", MatchType: QueryParamMatchTypePrefix}, + }, + }, + }, + expectConflict: false, + }, + "2 different routes with different path, with same kind, don't expect conflict": { + vHost: VirtualHost{ + Routes: map[string]*Route{}, + }, + rs: []Route{ + { + Kind: KindHTTPRoute, + Name: "a", + Namespace: "b", + PathMatchCondition: prefixSegment("/path2"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: ":authority", MatchType: HeaderMatchTypeRegex, Value: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?\\.example\\.com(:[0-9]+)?"}, + }, + QueryParamMatchConditions: []QueryParamMatchCondition{ + {Name: "param-1", Value: "value-1", MatchType: QueryParamMatchTypeExact}, + }, + }, + { + Kind: KindHTTPRoute, + Name: "c", + Namespace: "d", + PathMatchCondition: prefixSegment("/path1"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: ":authority", MatchType: HeaderMatchTypeRegex, Value: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?\\.example\\.com(:[0-9]+)?"}, + }, + QueryParamMatchConditions: []QueryParamMatchCondition{ + {Name: "param-1", Value: "value-1", MatchType: QueryParamMatchTypeExact}, + }, + }, + }, + expectConflict: false, + }, + } + + for n, c := range cases { + t.Run(n, func(t *testing.T) { + c.vHost.AddRoute(&c.rs[0]) + + assert.Equal(t, c.vHost.HasConflictRoute(&c.rs[1]), c.expectConflict) + }) + } +} diff --git a/internal/dag/gatewayapi_processor.go b/internal/dag/gatewayapi_processor.go index 58c4479b589..662d8ed1ffc 100644 --- a/internal/dag/gatewayapi_processor.go +++ b/internal/dag/gatewayapi_processor.go @@ -14,10 +14,10 @@ package dag import ( - "errors" "fmt" "net/http" "regexp" + "sort" "strings" "time" @@ -154,8 +154,10 @@ func (p *GatewayAPIProcessor) Run(dag *DAG, source *KubernetesCache) { // to each Listener so we can set status properly. listenerAttachedRoutes := map[string]int{} + // sort httproutes based on age/name first + sortedHTTPRoutes := sortRoutes(p.source.httproutes) // Process HTTPRoutes. - for _, httpRoute := range p.source.httproutes { + for _, httpRoute := range sortedHTTPRoutes { p.processRoute(KindHTTPRoute, httpRoute, httpRoute.Spec.ParentRefs, gatewayNotProgrammedCondition, listenerInfos, listenerAttachedRoutes, &gatewayapi_v1.HTTPRoute{}) } @@ -1117,28 +1119,42 @@ func (p *GatewayAPIProcessor) resolveRouteRefs(route any, routeAccessor *status. } func parseHTTPRouteTimeouts(httpRouteTimeouts *gatewayapi_v1.HTTPRouteTimeouts) (*RouteTimeoutPolicy, error) { - if httpRouteTimeouts == nil { + if httpRouteTimeouts == nil || (httpRouteTimeouts.Request == nil && httpRouteTimeouts.BackendRequest == nil) { return nil, nil } - // Since Gateway API doesn't yet support retries, this timeout setting - // is functionally equivalent to httpRouteTimeouts.Request, so we're - // not implementing it for now. Once retries are added to Gateway API, - // support for backend request timeouts can be added. - if httpRouteTimeouts.BackendRequest != nil { - return nil, errors.New("HTTPRoute.Spec.Rules.Timeouts.BackendRequest is not supported, use HTTPRoute.Spec.Rules.Timeouts.Request instead") - } - if httpRouteTimeouts.Request == nil { - return nil, nil + var responseTimeout timeout.Setting + + if httpRouteTimeouts.Request != nil { + requestTimeout, err := timeout.Parse(string(*httpRouteTimeouts.Request)) + if err != nil { + return nil, fmt.Errorf("invalid HTTPRoute.Spec.Rules.Timeouts.Request: %v", err) + } + + responseTimeout = requestTimeout } - requestTimeout, err := timeout.Parse(string(*httpRouteTimeouts.Request)) - if err != nil { - return nil, fmt.Errorf("invalid HTTPRoute.Spec.Rules.Timeouts.Request: %v", err) + // Note, since retries are not yet implemented in Gateway API, the backend + // request timeout is functionally equivalent to the request timeout for now. + // The API spec requires that it be less than/equal to the request timeout if + // both are specified. This implementation will change when retries are implemented. + if httpRouteTimeouts.BackendRequest != nil { + backendRequestTimeout, err := timeout.Parse(string(*httpRouteTimeouts.BackendRequest)) + if err != nil { + return nil, fmt.Errorf("invalid HTTPRoute.Spec.Rules.Timeouts.BackendRequest: %v", err) + } + + // If Timeouts.Request was specified, then Timeouts.BackendRequest must be + // less than/equal to it. + if responseTimeout.Duration() > 0 && backendRequestTimeout.Duration() > responseTimeout.Duration() { + return nil, fmt.Errorf("HTTPRoute.Spec.Rules.Timeouts.BackendRequest must be less than/equal to HTTPRoute.Spec.Rules.Timeouts.Request when both are specified") + } + + responseTimeout = backendRequestTimeout } return &RouteTimeoutPolicy{ - ResponseTimeout: requestTimeout, + ResponseTimeout: responseTimeout, }, nil } @@ -1149,6 +1165,8 @@ func (p *GatewayAPIProcessor) computeHTTPRouteForListener( listener *listenerInfo, hosts sets.Set[string], ) { + // Count number of rules under this Route that are invalid. + invalidRuleCnt := 0 for ruleIndex, rule := range route.Spec.Rules { // Get match conditions for the rule. var matchconditions []*matchConditions @@ -1425,21 +1443,66 @@ func (p *GatewayAPIProcessor) computeHTTPRouteForListener( timeoutPolicy) } - // Add each route to the relevant vhost(s)/svhosts(s). - for host := range hosts { - for _, route := range routes { - switch { - case listener.tlsSecret != nil: - svhost := p.dag.EnsureSecureVirtualHost(listener.dagListenerName, host) - svhost.Secret = listener.tlsSecret - svhost.AddRoute(route) - default: - vhost := p.dag.EnsureVirtualHost(listener.dagListenerName, host) - vhost.AddRoute(route) + // check all the routes whether there is conflict against previous rules + if !p.hasConflictRoute(listener, hosts, routes) { + // add the route if there is conflict + // Add each route to the relevant vhost(s)/svhosts(s). + for host := range hosts { + for _, route := range routes { + switch { + case listener.tlsSecret != nil: + svhost := p.dag.EnsureSecureVirtualHost(listener.dagListenerName, host) + svhost.Secret = listener.tlsSecret + svhost.AddRoute(route) + default: + vhost := p.dag.EnsureVirtualHost(listener.dagListenerName, host) + vhost.AddRoute(route) + } } } + } else { + // Skip adding the routes under this rule. + invalidRuleCnt++ } } + + if invalidRuleCnt == len(route.Spec.Rules) { + // No rules under the route is valid, mark it as not accepted. + routeAccessor.AddCondition( + gatewayapi_v1.RouteConditionAccepted, + meta_v1.ConditionFalse, + status.ReasonRouteRuleMatchConflict, + status.MessageRouteRuleMatchConflict, + ) + } else if invalidRuleCnt > 0 { + routeAccessor.AddCondition( + gatewayapi_v1.RouteConditionPartiallyInvalid, + meta_v1.ConditionTrue, + status.ReasonRouteRuleMatchPartiallyConflict, + status.MessageRouteRuleMatchPartiallyConflict, + ) + } +} + +func (p *GatewayAPIProcessor) hasConflictRoute(listener *listenerInfo, hosts sets.Set[string], routes []*Route) bool { + // check if there is conflict match first + for host := range hosts { + for _, route := range routes { + switch { + case listener.tlsSecret != nil: + svhost := p.dag.EnsureSecureVirtualHost(listener.dagListenerName, host) + if svhost.HasConflictRoute(route) { + return true + } + default: + vhost := p.dag.EnsureVirtualHost(listener.dagListenerName, host) + if vhost.HasConflictRoute(route) { + return true + } + } + } + } + return false } func (p *GatewayAPIProcessor) computeGRPCRouteForListener(route *gatewayapi_v1alpha2.GRPCRoute, routeAccessor *status.RouteParentStatusUpdate, listener *listenerInfo, hosts sets.Set[string]) bool { @@ -2397,3 +2460,22 @@ func handlePathRewritePrefixRemoval(p *PathRewritePolicy, mc *matchConditions) * return p } + +// sort routes based on creationTimestamp in ascending order +// if creationTimestamps are the same, sort based on namespaced name ("/") in alphetical ascending order +func sortRoutes(m map[types.NamespacedName]*gatewayapi_v1.HTTPRoute) []*gatewayapi_v1.HTTPRoute { + routes := []*gatewayapi_v1.HTTPRoute{} + for _, r := range m { + routes = append(routes, r) + } + sort.SliceStable(routes, func(i, j int) bool { + // if the creation time is the same, compare the route name + if routes[i].CreationTimestamp.Equal(&routes[j].CreationTimestamp) { + return k8s.NamespacedNameOf(routes[i]).String() < + k8s.NamespacedNameOf(routes[j]).String() + } + return routes[i].CreationTimestamp.Before(&routes[j].CreationTimestamp) + }) + + return routes +} diff --git a/internal/dag/gatewayapi_processor_test.go b/internal/dag/gatewayapi_processor_test.go index bf7e5cf71f3..c78ef4142c6 100644 --- a/internal/dag/gatewayapi_processor_test.go +++ b/internal/dag/gatewayapi_processor_test.go @@ -16,12 +16,14 @@ package dag import ( "fmt" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" core_v1 "k8s.io/api/core/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/utils/ptr" gatewayapi_v1 "sigs.k8s.io/gateway-api/apis/v1" @@ -768,3 +770,534 @@ func TestGetListenersForRouteParentRef(t *testing.T) { }) } } + +func TestSortRoutes(t *testing.T) { + time1 := time.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC) + time2 := time.Date(2022, time.Month(2), 21, 1, 10, 30, 0, time.UTC) + time3 := time.Date(2023, time.Month(2), 21, 1, 10, 30, 0, time.UTC) + tests := []struct { + name string + m map[types.NamespacedName]*gatewayapi_v1.HTTPRoute + expected []*gatewayapi_v1.HTTPRoute + }{ + { + name: "3 httproutes, with different timestamp, earlier one should be first ", + m: map[types.NamespacedName]*gatewayapi_v1.HTTPRoute{ + { + Namespace: "ns", Name: "name1", + }: { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns", + Name: "name3", + CreationTimestamp: meta_v1.NewTime(time3), + }, + }, + { + Namespace: "ns", Name: "name2", + }: { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns", + Name: "name2", + CreationTimestamp: meta_v1.NewTime(time2), + }, + }, + { + Namespace: "ns", Name: "name3", + }: { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns", + Name: "name1", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + }, + expected: []*gatewayapi_v1.HTTPRoute{ + { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns", + Name: "name1", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns", + Name: "name2", + CreationTimestamp: meta_v1.NewTime(time2), + }, + }, + { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns", + Name: "name3", + CreationTimestamp: meta_v1.NewTime(time3), + }, + }, + }, + }, + { + name: "3 httproutes with same creation timestamps, same namespaces, smaller name comes first", + m: map[types.NamespacedName]*gatewayapi_v1.HTTPRoute{ + { + Namespace: "ns", Name: "name3", + }: { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns", + Name: "name3", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + { + Namespace: "ns", Name: "name2", + }: { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns", + Name: "name2", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + { + Namespace: "ns", Name: "name1", + }: { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns", + Name: "name1", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + }, + expected: []*gatewayapi_v1.HTTPRoute{ + { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns", + Name: "name1", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns", + Name: "name2", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns", + Name: "name3", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + }, + }, + { + name: "3 httproutes with same creation timestamp, smaller namespaces comes first", + m: map[types.NamespacedName]*gatewayapi_v1.HTTPRoute{ + { + Namespace: "ns3", Name: "name1", + }: { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns3", + Name: "name3", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + { + Namespace: "ns2", Name: "name2", + }: { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns2", + Name: "name2", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + { + Namespace: "ns1", Name: "name3", + }: { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns1", + Name: "name3", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + }, + expected: []*gatewayapi_v1.HTTPRoute{ + { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns1", + Name: "name3", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns2", + Name: "name2", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns3", + Name: "name3", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + }, + }, + { + name: "mixed order, two with same creation timestamp, two with same name", + m: map[types.NamespacedName]*gatewayapi_v1.HTTPRoute{ + { + Namespace: "ns1", Name: "name2", + }: { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns1", + Name: "name2", + CreationTimestamp: meta_v1.NewTime(time2), + }, + }, + { + Namespace: "ns2", Name: "name2", + }: { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns2", + Name: "name2", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + { + Namespace: "ns1", Name: "name1", + }: { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns1", + Name: "name1", + CreationTimestamp: meta_v1.NewTime(time2), + }, + }, + }, + expected: []*gatewayapi_v1.HTTPRoute{ + { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns2", + Name: "name2", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns1", + Name: "name1", + CreationTimestamp: meta_v1.NewTime(time2), + }, + }, + { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns1", + Name: "name2", + CreationTimestamp: meta_v1.NewTime(time2), + }, + }, + }, + }, + { + name: "same name, same timestamp, different namespace", + m: map[types.NamespacedName]*gatewayapi_v1.HTTPRoute{ + { + Namespace: "ns3", Name: "name", + }: { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns3", + Name: "name", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + { + Namespace: "ns2", Name: "name", + }: { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns2", + Name: "name", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + { + Namespace: "ns1", Name: "name", + }: { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns1", + Name: "name", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + }, + expected: []*gatewayapi_v1.HTTPRoute{ + { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns1", + Name: "name", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns2", + Name: "name", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + { + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: "ns3", + Name: "name", + CreationTimestamp: meta_v1.NewTime(time1), + }, + }, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + res := sortRoutes(tc.m) + assert.Equal(t, tc.expected, res) + }) + } +} + +func TestHasConflictRoute(t *testing.T) { + host1 := "test.projectcontour.io" + host2 := "test1.projectcontour.io" + hosts := sets.New( + host1, + host2, + ) + listener := &listenerInfo{ + listener: gatewayapi_v1.Listener{ + Name: "l1", + AllowedRoutes: &gatewayapi_v1.AllowedRoutes{ + Namespaces: &gatewayapi_v1.RouteNamespaces{ + From: ptr.To(gatewayapi_v1.NamespacesFromSame), + }, + }, + }, + dagListenerName: "l1", + allowedKinds: []gatewayapi_v1.Kind{KindHTTPRoute}, + ready: true, + } + tlsListener := &listenerInfo{ + listener: gatewayapi_v1.Listener{ + Name: "ltls", + AllowedRoutes: &gatewayapi_v1.AllowedRoutes{ + Namespaces: &gatewayapi_v1.RouteNamespaces{ + From: ptr.To(gatewayapi_v1.NamespacesFromSame), + }, + }, + }, + allowedKinds: []gatewayapi_v1.Kind{KindHTTPRoute}, + ready: true, + dagListenerName: "ltls", + tlsSecret: &Secret{}, + } + tests := []struct { + name string + existingRoutes []*Route + routes []*Route + listener *listenerInfo + expectedConflict bool + }{ + { + name: "There are 2 existing route, the 3rd route to add doesn't have conflict, listen doesn't have tls, no conflict expected", + existingRoutes: []*Route{ + { + Name: "route1", + Namespace: "default", + PathMatchCondition: prefixSegment("/path1"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: ":authority", MatchType: HeaderMatchTypeRegex, Value: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?\\.example\\.com(:[0-9]+)?"}, + }, + QueryParamMatchConditions: []QueryParamMatchCondition{ + {Name: "param-1", Value: "value-1", MatchType: QueryParamMatchTypeExact}, + }, + }, + { + Kind: KindHTTPRoute, + Name: "route2", + Namespace: "default", + PathMatchCondition: prefixSegment("/path2"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: "version", Value: "2", MatchType: "exact", Invert: false}, + }, + }, + }, + routes: []*Route{ + { + Kind: KindHTTPRoute, + Name: "route3", + Namespace: "default", + PathMatchCondition: prefixSegment("/path2"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: "e-tag", Value: "abc", MatchType: "contains", Invert: true}, + }, + }, + }, + listener: listener, + }, + { + name: "There are 2 existing route, the 3rd route to add doesn't have conflict, listen has tls, no conflict expected", + existingRoutes: []*Route{ + { + Name: "route1", + Namespace: "default", + PathMatchCondition: prefixSegment("/path1"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: ":authority", MatchType: HeaderMatchTypeRegex, Value: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?\\.example\\.com(:[0-9]+)?"}, + }, + QueryParamMatchConditions: []QueryParamMatchCondition{ + {Name: "param-1", Value: "value-1", MatchType: QueryParamMatchTypeExact}, + }, + }, + { + Kind: KindHTTPRoute, + Name: "route2", + Namespace: "default", + PathMatchCondition: prefixSegment("/path2"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: "version", Value: "2", MatchType: "exact", Invert: false}, + }, + }, + }, + routes: []*Route{ + { + Kind: KindHTTPRoute, + Name: "route3", + Namespace: "default", + PathMatchCondition: prefixSegment("/path2"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: "e-tag", Value: "abc", MatchType: "contains", Invert: true}, + }, + }, + }, + listener: tlsListener, + }, + { + name: "There are 2 existing route, the 3rd route to add is conflict with 2nd route, listen doesn't have tls, expect conflicts", + existingRoutes: []*Route{ + { + Name: "route1", + Namespace: "default", + PathMatchCondition: prefixSegment("/path1"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: ":authority", MatchType: HeaderMatchTypeRegex, Value: "^[a-z0-9]([-a-z0-9]*[a-z0-9])?\\.example\\.com(:[0-9]+)?"}, + }, + }, + { + Kind: KindHTTPRoute, + Name: "route2", + Namespace: "default", + PathMatchCondition: prefixSegment("/path2"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: "version", Value: "2", MatchType: "exact", Invert: false}, + }, + QueryParamMatchConditions: []QueryParamMatchCondition{ + {Name: "param-1", Value: "value-1", MatchType: QueryParamMatchTypeExact}, + }, + }, + }, + routes: []*Route{ + { + Kind: KindHTTPRoute, + Name: "route3", + Namespace: "default", + PathMatchCondition: prefixSegment("/path2"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: "version", Value: "2", MatchType: "exact", Invert: false}, + }, + QueryParamMatchConditions: []QueryParamMatchCondition{ + {Name: "param-1", Value: "value-1", MatchType: QueryParamMatchTypeExact}, + }, + }, + }, + listener: listener, + expectedConflict: true, + }, + { + name: "There are 1 existing route, the 2nd route to add is conflict with 1st route, listen has tls, expect conflicts", + existingRoutes: []*Route{ + { + Kind: KindHTTPRoute, + Name: "route1", + Namespace: "default", + PathMatchCondition: prefixSegment("/path2"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: "version", Value: "2", MatchType: "exact", Invert: false}, + }, + QueryParamMatchConditions: []QueryParamMatchCondition{ + {Name: "param-1", Value: "value-1", MatchType: QueryParamMatchTypeExact}, + }, + }, + }, + routes: []*Route{ + { + Kind: KindHTTPRoute, + Name: "route2", + Namespace: "default", + PathMatchCondition: prefixSegment("/path2"), + HeaderMatchConditions: []HeaderMatchCondition{ + {Name: "version", Value: "2", MatchType: "exact", Invert: false}, + }, + QueryParamMatchConditions: []QueryParamMatchCondition{ + {Name: "param-1", Value: "value-1", MatchType: QueryParamMatchTypeExact}, + }, + }, + }, + listener: tlsListener, + expectedConflict: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + processor := &GatewayAPIProcessor{ + FieldLogger: fixture.NewTestLogger(t), + dag: &DAG{ + Listeners: map[string]*Listener{ + tlsListener.dagListenerName: { + svhostsByName: map[string]*SecureVirtualHost{ + host1: { + VirtualHost: VirtualHost{ + Name: host1, + Routes: make(map[string]*Route), + }, + }, + }, + }, + listener.dagListenerName: { + vhostsByName: map[string]*VirtualHost{ + host2: { + Routes: make(map[string]*Route), + }, + }, + }, + }, + }, + } + for host := range hosts { + for _, route := range tc.existingRoutes { + switch { + case tc.listener.tlsSecret != nil: + svhost := processor.dag.EnsureSecureVirtualHost(tc.listener.dagListenerName, host) + svhost.Secret = listener.tlsSecret + svhost.AddRoute(route) + default: + vhost := processor.dag.EnsureVirtualHost(tc.listener.dagListenerName, host) + vhost.AddRoute(route) + } + } + } + + res := processor.hasConflictRoute(tc.listener, hosts, tc.routes) + assert.Equal(t, tc.expectedConflict, res) + }) + } +} diff --git a/internal/dag/status_test.go b/internal/dag/status_test.go index 4272f49b29e..c6307b46d2d 100644 --- a/internal/dag/status_test.go +++ b/internal/dag/status_test.go @@ -16,6 +16,7 @@ package dag import ( "fmt" "testing" + "time" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" @@ -5510,7 +5511,8 @@ func TestGatewayAPIHTTPRouteDAGStatus(t *testing.T) { "test.projectcontour.io", }, Rules: []gatewayapi_v1.HTTPRouteRule{{ - Matches: gatewayapi.HTTPRouteMatch(gatewayapi_v1.PathMatchPathPrefix, "/"), + // changed to different matches in case there is conflict with above HTTPRoute + Matches: gatewayapi.HTTPRouteMatch(gatewayapi_v1.PathMatchExact, "/"), BackendRefs: gatewayapi.HTTPBackendRef("kuard", 8080, 1), }}, }, @@ -5545,6 +5547,193 @@ func TestGatewayAPIHTTPRouteDAGStatus(t *testing.T) { wantGatewayStatusUpdate: validGatewayStatusUpdate("http", gatewayapi_v1.HTTPProtocolType, 2), }) + run(t, "3 httproutes, but duplicate match condition between these 3. The 2 rank lower gets Conflict condition ", testcase{ + objs: []any{ + kuardService, + // first HTTPRoute with oldest creationTimestamp + &gatewayapi_v1.HTTPRoute{ + TypeMeta: meta_v1.TypeMeta{ + Kind: KindHTTPRoute, + }, + ObjectMeta: meta_v1.ObjectMeta{ + Name: "basic-1", + Namespace: "default", + CreationTimestamp: meta_v1.Date(2020, time.Month(2), 21, 1, 10, 30, 0, time.UTC), + }, + Spec: gatewayapi_v1.HTTPRouteSpec{ + CommonRouteSpec: gatewayapi_v1.CommonRouteSpec{ + ParentRefs: []gatewayapi_v1.ParentReference{gatewayapi.GatewayParentRef("projectcontour", "contour")}, + }, + Hostnames: []gatewayapi_v1.Hostname{ + "test.projectcontour.io", + }, + Rules: []gatewayapi_v1.HTTPRouteRule{{ + Matches: []gatewayapi_v1.HTTPRouteMatch{ + { + Path: &gatewayapi_v1.HTTPPathMatch{ + Type: ptr.To(gatewayapi_v1.PathMatchPathPrefix), + Value: ptr.To("/"), + }, + Headers: []gatewayapi_v1.HTTPHeaderMatch{ + { + Type: ptr.To(gatewayapi_v1.HeaderMatchExact), + Name: gatewayapi_v1.HTTPHeaderName("foo"), + Value: "bar", + }, + }, + QueryParams: []gatewayapi_v1.HTTPQueryParamMatch{ + { + Type: ptr.To(gatewayapi_v1.QueryParamMatchRegularExpression), + Name: "param-1", + Value: "valid-[a-z]?-[A-Za-z]+-[0=9]+-\\d+", + }, + }, + }, + { + Path: &gatewayapi_v1.HTTPPathMatch{ + Type: ptr.To(gatewayapi_v1.PathMatchPathPrefix), + Value: ptr.To("/"), + }, + Headers: []gatewayapi_v1.HTTPHeaderMatch{ + { + Type: ptr.To(gatewayapi_v1.HeaderMatchExact), + Name: gatewayapi_v1.HTTPHeaderName("a"), + Value: "b", + }, + }, + }, + }, + + BackendRefs: gatewayapi.HTTPBackendRef("kuard", 8080, 1), + }}, + }, + }, + // second HTTPRoute with 2nd oldest creationTimestamp, conflict with 1st HTTPRoute + &gatewayapi_v1.HTTPRoute{ + TypeMeta: meta_v1.TypeMeta{ + Kind: KindHTTPRoute, + }, + ObjectMeta: meta_v1.ObjectMeta{ + Name: "basic-2", + Namespace: "default", + CreationTimestamp: meta_v1.Date(2021, time.Month(2), 21, 1, 10, 30, 0, time.UTC), + }, + Spec: gatewayapi_v1.HTTPRouteSpec{ + CommonRouteSpec: gatewayapi_v1.CommonRouteSpec{ + ParentRefs: []gatewayapi_v1.ParentReference{gatewayapi.GatewayParentRef("projectcontour", "contour")}, + }, + Hostnames: []gatewayapi_v1.Hostname{ + "test.projectcontour.io", + }, + Rules: []gatewayapi_v1.HTTPRouteRule{{ + Matches: []gatewayapi_v1.HTTPRouteMatch{ + { + Path: &gatewayapi_v1.HTTPPathMatch{ + Type: ptr.To(gatewayapi_v1.PathMatchPathPrefix), + Value: ptr.To("/"), + }, + Headers: []gatewayapi_v1.HTTPHeaderMatch{ + { + Type: ptr.To(gatewayapi_v1.HeaderMatchExact), + Name: gatewayapi_v1.HTTPHeaderName("a"), + Value: "b", + }, + }, + }, + }, + + BackendRefs: gatewayapi.HTTPBackendRef("kuard", 8080, 1), + }}, + }, + }, + // 3rd HTTPRoute with newest creationTimestamp, conflict with 1st HTTPRoute + &gatewayapi_v1.HTTPRoute{ + TypeMeta: meta_v1.TypeMeta{ + Kind: KindHTTPRoute, + }, + ObjectMeta: meta_v1.ObjectMeta{ + Name: "basic-3", + Namespace: "default", + CreationTimestamp: meta_v1.Date(2022, time.Month(2), 21, 1, 10, 30, 0, time.UTC), + }, + Spec: gatewayapi_v1.HTTPRouteSpec{ + CommonRouteSpec: gatewayapi_v1.CommonRouteSpec{ + ParentRefs: []gatewayapi_v1.ParentReference{gatewayapi.GatewayParentRef("projectcontour", "contour")}, + }, + Hostnames: []gatewayapi_v1.Hostname{ + "test.projectcontour.io", + }, + Rules: []gatewayapi_v1.HTTPRouteRule{{ + Matches: []gatewayapi_v1.HTTPRouteMatch{ + { + Path: &gatewayapi_v1.HTTPPathMatch{ + Type: ptr.To(gatewayapi_v1.PathMatchPathPrefix), + Value: ptr.To("/"), + }, + Headers: []gatewayapi_v1.HTTPHeaderMatch{ + { + Type: ptr.To(gatewayapi_v1.HeaderMatchExact), + Name: gatewayapi_v1.HTTPHeaderName("foo"), + Value: "bar", + }, + }, + QueryParams: []gatewayapi_v1.HTTPQueryParamMatch{ + { + Type: ptr.To(gatewayapi_v1.QueryParamMatchRegularExpression), + Name: "param-1", + Value: "valid-[a-z]?-[A-Za-z]+-[0=9]+-\\d+", + }, + }, + }, + }, + + BackendRefs: gatewayapi.HTTPBackendRef("kuard", 8080, 1), + }}, + }, + }, + }, + wantRouteConditions: []*status.RouteStatusUpdate{ + { + FullName: types.NamespacedName{Namespace: "default", Name: "basic-1"}, + RouteParentStatuses: []*gatewayapi_v1.RouteParentStatus{ + { + ParentRef: gatewayapi.GatewayParentRef("projectcontour", "contour"), + Conditions: []meta_v1.Condition{ + routeResolvedRefsCondition(), + routeAcceptedHTTPRouteCondition(), + }, + }, + }, + }, + { + FullName: types.NamespacedName{Namespace: "default", Name: "basic-2"}, + RouteParentStatuses: []*gatewayapi_v1.RouteParentStatus{ + { + ParentRef: gatewayapi.GatewayParentRef("projectcontour", "contour"), + Conditions: []meta_v1.Condition{ + routeAcceptedFalse(status.ReasonRouteRuleMatchConflict, "HTTPRoute's Match has conflict with other HTTPRoute's Match"), + routeResolvedRefsCondition(), + }, + }, + }, + }, + { + FullName: types.NamespacedName{Namespace: "default", Name: "basic-3"}, + RouteParentStatuses: []*gatewayapi_v1.RouteParentStatus{ + { + ParentRef: gatewayapi.GatewayParentRef("projectcontour", "contour"), + Conditions: []meta_v1.Condition{ + routeAcceptedFalse(status.ReasonRouteRuleMatchConflict, "HTTPRoute's Match has conflict with other HTTPRoute's Match"), + routeResolvedRefsCondition(), + }, + }, + }, + }, + }, + // is it ok to show the listeners are attached, just it's not accepted because of the conflict + wantGatewayStatusUpdate: validGatewayStatusUpdate("http", gatewayapi_v1.HTTPProtocolType, 3), + }) + run(t, "prefix path match not starting with '/' for httproute", testcase{ objs: []any{ kuardService, @@ -9441,7 +9630,7 @@ func TestGatewayAPIHTTPRouteDAGStatus(t *testing.T) { }, }) - run(t, "route rule with timeouts.request and timeouts.backendRequest specified", testcase{ + run(t, "route rule with timeouts.backendRequest greater than timeouts.request", testcase{ objs: []any{ kuardService, &gatewayapi_v1.HTTPRoute{ @@ -9462,7 +9651,7 @@ func TestGatewayAPIHTTPRouteDAGStatus(t *testing.T) { BackendRefs: gatewayapi.HTTPBackendRef("kuard", 8080, 1), Timeouts: &gatewayapi_v1.HTTPRouteTimeouts{ Request: ptr.To(gatewayapi_v1.Duration("30s")), - BackendRequest: ptr.To(gatewayapi_v1.Duration("30s")), + BackendRequest: ptr.To(gatewayapi_v1.Duration("60s")), }, }, }, @@ -9477,7 +9666,7 @@ func TestGatewayAPIHTTPRouteDAGStatus(t *testing.T) { ParentRef: gatewayapi.GatewayParentRef("projectcontour", "contour"), Conditions: []meta_v1.Condition{ routeResolvedRefsCondition(), - routeAcceptedFalse(gatewayapi_v1.RouteReasonUnsupportedValue, "HTTPRoute.Spec.Rules.Timeouts.BackendRequest is not supported, use HTTPRoute.Spec.Rules.Timeouts.Request instead"), + routeAcceptedFalse(gatewayapi_v1.RouteReasonUnsupportedValue, "HTTPRoute.Spec.Rules.Timeouts.BackendRequest must be less than/equal to HTTPRoute.Spec.Rules.Timeouts.Request when both are specified"), }, }, }, @@ -9486,7 +9675,7 @@ func TestGatewayAPIHTTPRouteDAGStatus(t *testing.T) { wantGatewayStatusUpdate: validGatewayStatusUpdate("http", gatewayapi_v1.HTTPProtocolType, 1), }) - run(t, "route rule with only timeouts.backendRequest specified", testcase{ + run(t, "route rule with invalid timeouts.backendRequest specified", testcase{ objs: []any{ kuardService, &gatewayapi_v1.HTTPRoute{ @@ -9506,7 +9695,7 @@ func TestGatewayAPIHTTPRouteDAGStatus(t *testing.T) { Matches: gatewayapi.HTTPRouteMatch(gatewayapi_v1.PathMatchPathPrefix, "/"), BackendRefs: gatewayapi.HTTPBackendRef("kuard", 8080, 1), Timeouts: &gatewayapi_v1.HTTPRouteTimeouts{ - BackendRequest: ptr.To(gatewayapi_v1.Duration("30s")), + BackendRequest: ptr.To(gatewayapi_v1.Duration("invalid")), }, }, }, @@ -9520,7 +9709,7 @@ func TestGatewayAPIHTTPRouteDAGStatus(t *testing.T) { ParentRef: gatewayapi.GatewayParentRef("projectcontour", "contour"), Conditions: []meta_v1.Condition{ routeResolvedRefsCondition(), - routeAcceptedFalse(gatewayapi_v1.RouteReasonUnsupportedValue, "HTTPRoute.Spec.Rules.Timeouts.BackendRequest is not supported, use HTTPRoute.Spec.Rules.Timeouts.Request instead"), + routeAcceptedFalse(gatewayapi_v1.RouteReasonUnsupportedValue, "invalid HTTPRoute.Spec.Rules.Timeouts.BackendRequest: unable to parse timeout string \"invalid\": time: invalid duration \"invalid\""), }, }, }, diff --git a/internal/envoy/v3/bootstrap.go b/internal/envoy/v3/bootstrap.go index 2552dee7602..a70d3268c8f 100644 --- a/internal/envoy/v3/bootstrap.go +++ b/internal/envoy/v3/bootstrap.go @@ -219,12 +219,14 @@ func bootstrapConfig(c *envoy.BootstrapConfig) *envoy_config_bootstrap_v3.Bootst MaxPendingRequests: wrapperspb.UInt32(100000), MaxRequests: wrapperspb.UInt32(60000000), MaxRetries: wrapperspb.UInt32(50), + TrackRemaining: true, }, { Priority: envoy_config_core_v3.RoutingPriority_DEFAULT, MaxConnections: wrapperspb.UInt32(100000), MaxPendingRequests: wrapperspb.UInt32(100000), MaxRequests: wrapperspb.UInt32(60000000), MaxRetries: wrapperspb.UInt32(50), + TrackRemaining: true, }}, }, }, { diff --git a/internal/envoy/v3/bootstrap_test.go b/internal/envoy/v3/bootstrap_test.go index 2012179f900..cc1c9a9fd87 100644 --- a/internal/envoy/v3/bootstrap_test.go +++ b/internal/envoy/v3/bootstrap_test.go @@ -74,13 +74,15 @@ func TestBootstrap(t *testing.T) { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true }, { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true } ] }, @@ -253,13 +255,15 @@ func TestBootstrap(t *testing.T) { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true }, { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true } ] }, @@ -432,13 +436,15 @@ func TestBootstrap(t *testing.T) { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true }, { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true } ] }, @@ -612,13 +618,15 @@ func TestBootstrap(t *testing.T) { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true }, { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true } ] }, @@ -792,13 +800,15 @@ func TestBootstrap(t *testing.T) { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true }, { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true } ] }, @@ -972,13 +982,15 @@ func TestBootstrap(t *testing.T) { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true }, { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true } ] }, @@ -1153,13 +1165,15 @@ func TestBootstrap(t *testing.T) { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true }, { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true } ] }, @@ -1336,13 +1350,15 @@ func TestBootstrap(t *testing.T) { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true }, { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true } ] }, @@ -1553,13 +1569,15 @@ func TestBootstrap(t *testing.T) { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true }, { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true } ] }, @@ -1810,13 +1828,15 @@ func TestBootstrap(t *testing.T) { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true }, { "max_connections": 100000, "max_pending_requests": 100000, "max_requests": 60000000, - "max_retries": 50 + "max_retries": 50, + "track_remaining": true } ] }, diff --git a/internal/envoy/v3/cluster.go b/internal/envoy/v3/cluster.go index 8a388799b06..40fe15afd52 100644 --- a/internal/envoy/v3/cluster.go +++ b/internal/envoy/v3/cluster.go @@ -86,9 +86,11 @@ func Cluster(c *dag.Cluster) *envoy_config_cluster_v3.Cluster { MaxPendingRequests: protobuf.UInt32OrNil(service.MaxPendingRequests), MaxRequests: protobuf.UInt32OrNil(service.MaxRequests), MaxRetries: protobuf.UInt32OrNil(service.MaxRetries), + TrackRemaining: true, }}, PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ MaxConnections: protobuf.UInt32OrNil(service.PerHostMaxConnections), + TrackRemaining: true, }}, } } diff --git a/internal/envoy/v3/cluster_test.go b/internal/envoy/v3/cluster_test.go index 79ea1525873..ff6bf79a012 100644 --- a/internal/envoy/v3/cluster_test.go +++ b/internal/envoy/v3/cluster_test.go @@ -427,8 +427,11 @@ func TestCluster(t *testing.T) { CircuitBreakers: &envoy_config_cluster_v3.CircuitBreakers{ Thresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ MaxConnections: wrapperspb.UInt32(9000), + TrackRemaining: true, + }}, + PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ + TrackRemaining: true, }}, - PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{}}, }, }, }, @@ -456,8 +459,11 @@ func TestCluster(t *testing.T) { CircuitBreakers: &envoy_config_cluster_v3.CircuitBreakers{ Thresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ MaxPendingRequests: wrapperspb.UInt32(4096), + TrackRemaining: true, + }}, + PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ + TrackRemaining: true, }}, - PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{}}, }, }, }, @@ -484,9 +490,12 @@ func TestCluster(t *testing.T) { }, CircuitBreakers: &envoy_config_cluster_v3.CircuitBreakers{ Thresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ - MaxRequests: wrapperspb.UInt32(404), + MaxRequests: wrapperspb.UInt32(404), + TrackRemaining: true, + }}, + PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ + TrackRemaining: true, }}, - PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{}}, }, }, }, @@ -513,9 +522,12 @@ func TestCluster(t *testing.T) { }, CircuitBreakers: &envoy_config_cluster_v3.CircuitBreakers{ Thresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ - MaxRetries: wrapperspb.UInt32(7), + MaxRetries: wrapperspb.UInt32(7), + TrackRemaining: true, + }}, + PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ + TrackRemaining: true, }}, - PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{}}, }, }, }, @@ -541,9 +553,12 @@ func TestCluster(t *testing.T) { ServiceName: "default/kuard/http", }, CircuitBreakers: &envoy_config_cluster_v3.CircuitBreakers{ - Thresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{}}, + Thresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ + TrackRemaining: true, + }}, PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ MaxConnections: wrapperspb.UInt32(45), + TrackRemaining: true, }}, }, }, diff --git a/internal/featuretests/v3/cluster_test.go b/internal/featuretests/v3/cluster_test.go index 6569461cf04..ff4eb77c07d 100644 --- a/internal/featuretests/v3/cluster_test.go +++ b/internal/featuretests/v3/cluster_test.go @@ -461,9 +461,11 @@ func TestClusterCircuitbreakerAnnotationsIngress(t *testing.T) { MaxPendingRequests: wrapperspb.UInt32(4096), MaxRequests: wrapperspb.UInt32(404), MaxRetries: wrapperspb.UInt32(7), + TrackRemaining: true, }}, PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ MaxConnections: wrapperspb.UInt32(45), + TrackRemaining: true, }}, }, }), @@ -497,8 +499,11 @@ func TestClusterCircuitbreakerAnnotationsIngress(t *testing.T) { MaxConnections: wrapperspb.UInt32(13), MaxRequests: wrapperspb.UInt32(15), MaxRetries: wrapperspb.UInt32(17), + TrackRemaining: true, + }}, + PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ + TrackRemaining: true, }}, - PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{}}, }, }), ), @@ -531,8 +536,11 @@ func TestClusterCircuitbreakerAnnotationsIngress(t *testing.T) { MaxPendingRequests: wrapperspb.UInt32(14), MaxRequests: wrapperspb.UInt32(15), MaxRetries: wrapperspb.UInt32(17), + TrackRemaining: true, + }}, + PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ + TrackRemaining: true, }}, - PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{}}, }, }), ), @@ -597,8 +605,11 @@ func TestClusterCircuitbreakerAnnotationsHTTPProxy(t *testing.T) { MaxPendingRequests: wrapperspb.UInt32(4096), MaxRequests: wrapperspb.UInt32(404), MaxRetries: wrapperspb.UInt32(7), + TrackRemaining: true, + }}, + PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ + TrackRemaining: true, }}, - PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{}}, }, }), ), @@ -631,8 +642,11 @@ func TestClusterCircuitbreakerAnnotationsHTTPProxy(t *testing.T) { MaxConnections: wrapperspb.UInt32(13), MaxRequests: wrapperspb.UInt32(15), MaxRetries: wrapperspb.UInt32(17), + TrackRemaining: true, + }}, + PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ + TrackRemaining: true, }}, - PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{}}, }, }), ), @@ -665,8 +679,11 @@ func TestClusterCircuitbreakerAnnotationsHTTPProxy(t *testing.T) { MaxPendingRequests: wrapperspb.UInt32(14), MaxRequests: wrapperspb.UInt32(15), MaxRetries: wrapperspb.UInt32(17), + TrackRemaining: true, + }}, + PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ + TrackRemaining: true, }}, - PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{}}, }, }), ), @@ -774,8 +791,11 @@ func TestClusterCircuitbreakerAnnotationsGateway(t *testing.T) { MaxPendingRequests: wrapperspb.UInt32(4096), MaxRequests: wrapperspb.UInt32(404), MaxRetries: wrapperspb.UInt32(7), + TrackRemaining: true, + }}, + PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ + TrackRemaining: true, }}, - PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{}}, }, }), ), @@ -808,8 +828,11 @@ func TestClusterCircuitbreakerAnnotationsGateway(t *testing.T) { MaxConnections: wrapperspb.UInt32(13), MaxRequests: wrapperspb.UInt32(15), MaxRetries: wrapperspb.UInt32(17), + TrackRemaining: true, + }}, + PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ + TrackRemaining: true, }}, - PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{}}, }, }), ), @@ -842,8 +865,11 @@ func TestClusterCircuitbreakerAnnotationsGateway(t *testing.T) { MaxPendingRequests: wrapperspb.UInt32(14), MaxRequests: wrapperspb.UInt32(15), MaxRetries: wrapperspb.UInt32(17), + TrackRemaining: true, + }}, + PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ + TrackRemaining: true, }}, - PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{}}, }, }), ), diff --git a/internal/provisioner/controller/gateway.go b/internal/provisioner/controller/gateway.go index 4de51141b6c..b1daaf114d1 100644 --- a/internal/provisioner/controller/gateway.go +++ b/internal/provisioner/controller/gateway.go @@ -68,9 +68,9 @@ func NewGatewayController(mgr manager.Manager, gatewayController, contourImage, } if err := c.Watch( - source.Kind(mgr.GetCache(), &gatewayapi_v1.Gateway{}), - &handler.EnqueueRequestForObject{}, - predicate.NewPredicateFuncs(r.forReconcilableGatewayClass), + source.Kind(mgr.GetCache(), &gatewayapi_v1.Gateway{}, + &handler.TypedEnqueueRequestForObject[*gatewayapi_v1.Gateway]{}, + predicate.NewTypedPredicateFuncs(r.forReconcilableGatewayClass)), ); err != nil { return nil, err } @@ -79,9 +79,9 @@ func NewGatewayController(mgr manager.Manager, gatewayController, contourImage, // Gateways when a provisioner-controlled GatewayClass becomes // "Accepted: true". if err := c.Watch( - source.Kind(mgr.GetCache(), &gatewayapi_v1.GatewayClass{}), - handler.EnqueueRequestsFromMapFunc(r.getGatewayClassGateways), - predicate.NewPredicateFuncs(r.isGatewayClassReconcilable), + source.Kind(mgr.GetCache(), &gatewayapi_v1.GatewayClass{}, + handler.TypedEnqueueRequestsFromMapFunc(r.getGatewayClassGateways), + predicate.NewTypedPredicateFuncs(r.isGatewayClassReconcilable)), ); err != nil { return nil, err } @@ -92,14 +92,9 @@ func NewGatewayController(mgr manager.Manager, gatewayController, contourImage, // forReconcilableGatewayClass returns true if the provided Gateway uses a GatewayClass // controlled by the provisioner, and that GatewayClass has a condition of // "Accepted: true". -func (r *gatewayReconciler) forReconcilableGatewayClass(obj client.Object) bool { - gw, ok := obj.(*gatewayapi_v1.Gateway) - if !ok { - return false - } - +func (r *gatewayReconciler) forReconcilableGatewayClass(gateway *gatewayapi_v1.Gateway) bool { gatewayClass := &gatewayapi_v1.GatewayClass{} - if err := r.client.Get(context.Background(), client.ObjectKey{Name: string(gw.Spec.GatewayClassName)}, gatewayClass); err != nil { + if err := r.client.Get(context.Background(), client.ObjectKey{Name: string(gateway.Spec.GatewayClassName)}, gatewayClass); err != nil { return false } @@ -109,12 +104,7 @@ func (r *gatewayReconciler) forReconcilableGatewayClass(obj client.Object) bool // isGatewayClassReconcilable returns true if the provided object is a // GatewayClass controlled by the provisioner that has an "Accepted: true" // condition. -func (r *gatewayReconciler) isGatewayClassReconcilable(obj client.Object) bool { - gatewayClass, ok := obj.(*gatewayapi_v1.GatewayClass) - if !ok { - return false - } - +func (r *gatewayReconciler) isGatewayClassReconcilable(gatewayClass *gatewayapi_v1.GatewayClass) bool { if gatewayClass.Spec.ControllerName != r.gatewayController { return false } @@ -132,7 +122,7 @@ func (r *gatewayReconciler) isGatewayClassReconcilable(obj client.Object) bool { return accepted } -func (r *gatewayReconciler) getGatewayClassGateways(ctx context.Context, gatewayClass client.Object) []reconcile.Request { +func (r *gatewayReconciler) getGatewayClassGateways(ctx context.Context, gatewayClass *gatewayapi_v1.GatewayClass) []reconcile.Request { var gateways gatewayapi_v1.GatewayList if err := r.client.List(ctx, &gateways); err != nil { r.log.Error(err, "error listing gateways") diff --git a/internal/provisioner/controller/gatewayclass.go b/internal/provisioner/controller/gatewayclass.go index 030fb2c821d..a4a09d91ea2 100644 --- a/internal/provisioner/controller/gatewayclass.go +++ b/internal/provisioner/controller/gatewayclass.go @@ -62,9 +62,9 @@ func NewGatewayClassController(mgr manager.Manager, gatewayController string) (c } if err := c.Watch( - source.Kind(mgr.GetCache(), &gatewayapi_v1.GatewayClass{}), - &handler.EnqueueRequestForObject{}, - predicate.NewPredicateFuncs(r.hasMatchingController), + source.Kind(mgr.GetCache(), &gatewayapi_v1.GatewayClass{}, + &handler.TypedEnqueueRequestForObject[*gatewayapi_v1.GatewayClass]{}, + predicate.NewTypedPredicateFuncs(r.hasMatchingController)), ); err != nil { return nil, err } @@ -72,8 +72,8 @@ func NewGatewayClassController(mgr manager.Manager, gatewayController string) (c // Watch ContourDeployments since they can be used as parameters for // GatewayClasses. if err := c.Watch( - source.Kind(mgr.GetCache(), &contour_v1alpha1.ContourDeployment{}), - handler.EnqueueRequestsFromMapFunc(r.mapContourDeploymentToGatewayClasses), + source.Kind(mgr.GetCache(), &contour_v1alpha1.ContourDeployment{}, + handler.TypedEnqueueRequestsFromMapFunc(r.mapContourDeploymentToGatewayClasses)), ); err != nil { return nil, err } @@ -81,19 +81,14 @@ func NewGatewayClassController(mgr manager.Manager, gatewayController string) (c return c, nil } -func (r *gatewayClassReconciler) hasMatchingController(obj client.Object) bool { - gatewayClass, ok := obj.(*gatewayapi_v1.GatewayClass) - if !ok { - return false - } - +func (r *gatewayClassReconciler) hasMatchingController(gatewayClass *gatewayapi_v1.GatewayClass) bool { return gatewayClass.Spec.ControllerName == r.gatewayController } // mapContourDeploymentToGatewayClasses returns a list of reconcile requests // for all provisioner-controlled GatewayClasses that have a ParametersRef to // the specified ContourDeployment object. -func (r *gatewayClassReconciler) mapContourDeploymentToGatewayClasses(ctx context.Context, contourDeployment client.Object) []reconcile.Request { +func (r *gatewayClassReconciler) mapContourDeploymentToGatewayClasses(ctx context.Context, contourDeployment *contour_v1alpha1.ContourDeployment) []reconcile.Request { var gatewayClasses gatewayapi_v1.GatewayClassList if err := r.client.List(ctx, &gatewayClasses); err != nil { r.log.Error(err, "error listing gateway classes") diff --git a/internal/status/routeconditions.go b/internal/status/routeconditions.go index b90d212d9fb..fa9bb809366 100644 --- a/internal/status/routeconditions.go +++ b/internal/status/routeconditions.go @@ -32,11 +32,16 @@ const ( ) const ( - ReasonDegraded gatewayapi_v1.RouteConditionReason = "Degraded" - ReasonAllBackendRefsHaveZeroWeights gatewayapi_v1.RouteConditionReason = "AllBackendRefsHaveZeroWeights" - ReasonInvalidPathMatch gatewayapi_v1.RouteConditionReason = "InvalidPathMatch" - ReasonInvalidMethodMatch gatewayapi_v1.RouteConditionReason = "InvalidMethodMatch" - ReasonInvalidGateway gatewayapi_v1.RouteConditionReason = "InvalidGateway" + ReasonDegraded gatewayapi_v1.RouteConditionReason = "Degraded" + ReasonAllBackendRefsHaveZeroWeights gatewayapi_v1.RouteConditionReason = "AllBackendRefsHaveZeroWeights" + ReasonInvalidPathMatch gatewayapi_v1.RouteConditionReason = "InvalidPathMatch" + ReasonInvalidMethodMatch gatewayapi_v1.RouteConditionReason = "InvalidMethodMatch" + ReasonInvalidGateway gatewayapi_v1.RouteConditionReason = "InvalidGateway" + ReasonRouteRuleMatchConflict gatewayapi_v1.RouteConditionReason = "RuleMatchConflict" + ReasonRouteRuleMatchPartiallyConflict gatewayapi_v1.RouteConditionReason = "RuleMatchPartiallyConflict" + + MessageRouteRuleMatchConflict string = "HTTPRoute's Match has conflict with other HTTPRoute's Match" + MessageRouteRuleMatchPartiallyConflict string = "Dropped Rule: HTTPRoute's rule(s) has(ve) been dropped because of conflict against other HTTPRoute's rule(s)" ) // RouteStatusUpdate represents an atomic update to a diff --git a/internal/xdscache/v3/cluster_test.go b/internal/xdscache/v3/cluster_test.go index fca970072af..585c3bfea57 100644 --- a/internal/xdscache/v3/cluster_test.go +++ b/internal/xdscache/v3/cluster_test.go @@ -772,9 +772,11 @@ func TestClusterVisit(t *testing.T) { MaxPendingRequests: wrapperspb.UInt32(4096), MaxRequests: wrapperspb.UInt32(404), MaxRetries: wrapperspb.UInt32(7), + TrackRemaining: true, }}, PerHostThresholds: []*envoy_config_cluster_v3.CircuitBreakers_Thresholds{{ MaxConnections: wrapperspb.UInt32(45), + TrackRemaining: true, }}, }, }, diff --git a/pkg/config/parameters.go b/pkg/config/parameters.go index d0e4fbcba6b..d429197f2d2 100644 --- a/pkg/config/parameters.go +++ b/pkg/config/parameters.go @@ -709,8 +709,8 @@ type Parameters struct { // FeatureFlags defines toggle to enable new contour features. // available toggles are // useEndpointSlices - configures contour to fetch endpoint data - // from k8s endpoint slices. defaults to false and reading endpoint - // data from the k8s endpoints. + // from k8s endpoint slices. defaults to true, + // if false then reading endpoint data from the k8s endpoints. FeatureFlags []string `yaml:"featureFlags,omitempty"` // GlobalExternalProcessing optionally holds properties of the global external processing configurations. diff --git a/site/content/docs/main/config/api-reference.html b/site/content/docs/main/config/api-reference.html index 042a690b198..b9b43c1959c 100644 --- a/site/content/docs/main/config/api-reference.html +++ b/site/content/docs/main/config/api-reference.html @@ -5670,9 +5670,9 @@

ContourConfiguration

FeatureFlags defines toggle to enable new contour features. Available toggles are: -useEndpointSlices - configures contour to fetch endpoint data -from k8s endpoint slices. defaults to false and reading endpoint -data from the k8s endpoints.

+useEndpointSlices - Configures contour to fetch endpoint data +from k8s endpoint slices. defaults to true, +If false then reads endpoint data from the k8s endpoints.

@@ -6479,9 +6479,9 @@

ContourConfiguratio

FeatureFlags defines toggle to enable new contour features. Available toggles are: -useEndpointSlices - configures contour to fetch endpoint data -from k8s endpoint slices. defaults to false and reading endpoint -data from the k8s endpoints.

+useEndpointSlices - Configures contour to fetch endpoint data +from k8s endpoint slices. defaults to true, +If false then reads endpoint data from the k8s endpoints.

diff --git a/site/content/resources/compatibility-matrix.md b/site/content/resources/compatibility-matrix.md index 7cb2b608af2..96b865c3ad5 100644 --- a/site/content/resources/compatibility-matrix.md +++ b/site/content/resources/compatibility-matrix.md @@ -10,12 +10,15 @@ These combinations of versions are specifically tested in CI and supported by th | Contour Version | Envoy Version | Kubernetes Versions | Gateway API Version | | --------------- | :------------------- | ------------------- | --------------------| -| main | [1.29.3][50] | 1.29, 1.28, 1.27 | [1.0.0][110] | +| main | [1.30.1][53] | 1.29, 1.28, 1.27 | [1.0.0][110] | +| 1.28.3 | [1.29.3][50] | 1.29, 1.28, 1.27 | [1.0.0][110] | | 1.28.2 | [1.29.2][49] | 1.29, 1.28, 1.27 | [1.0.0][110] | | 1.28.1 | [1.29.1][46] | 1.29, 1.28, 1.27 | [1.0.0][110] | | 1.28.0 | [1.29.1][46] | 1.29, 1.28, 1.27 | [1.0.0][110] | +| 1.27.2 | [1.28.2][52] | 1.28, 1.27, 1.26 | [0.8.1][109] | | 1.27.1 | [1.28.1][47] | 1.28, 1.27, 1.26 | [0.8.1][109] | | 1.27.0 | [1.28.0][45] | 1.28, 1.27, 1.26 | [0.8.1][109] | +| 1.26.3 | [1.27.4][51] | 1.28, 1.27, 1.26 | [0.8.1][109] | | 1.26.2 | [1.27.3][48] | 1.28, 1.27, 1.26 | [0.8.1][109] | | 1.26.1 | [1.27.2][42] | 1.28, 1.27, 1.26 | [0.8.1][109] | | 1.26.0 | [1.27.0][41] | 1.28, 1.27, 1.26 | [0.8.0][108] | @@ -185,6 +188,9 @@ __Note:__ This list of extensions was last verified to be complete with Envoy v1 [48]: https://www.envoyproxy.io/docs/envoy/v1.27.3/version_history/v1.27/v1.27.3 [49]: https://www.envoyproxy.io/docs/envoy/v1.29.2/version_history/v1.29/v1.29.2 [50]: https://www.envoyproxy.io/docs/envoy/v1.29.3/version_history/v1.29/v1.29.3 +[51]: https://www.envoyproxy.io/docs/envoy/v1.27.4/version_history/v1.27/v1.27.4 +[52]: https://www.envoyproxy.io/docs/envoy/v1.28.2/version_history/v1.28/v1.28.2 +[53]: https://www.envoyproxy.io/docs/envoy/v1.30.1/version_history/v1.30/v1.30.1 [98]: https://github.com/kubernetes/client-go [99]: https://github.com/kubernetes/client-go#compatibility-matrix diff --git a/site/content/resources/release-process.md b/site/content/resources/release-process.md index 4a3057c4d52..d5264c22d95 100644 --- a/site/content/resources/release-process.md +++ b/site/content/resources/release-process.md @@ -283,10 +283,7 @@ Now you have a tag pushed to Github, go to the release tab on github, select the After any release, a few communications should be sent out to notify users. - Post a note to the #contour channel on k8s slack, also update the /topic with the current release number -- Post a note to the #project-contour channel on the vmware slack, also update the /topic with the current release number -- Send an update to the [cncf-contour-users mailing list][4] -- Send an update to the [cncf-contour-distributors-announce mailing list][5] -- Post a blog entry to projectcontour.io +- Send an update to the [Project Contour mailing list][4] # File issues @@ -295,7 +292,6 @@ If you encountered any problems or areas for improvement while executing the rel [1]: #minor-release-process [2]: #patch-release-process [3]: #rc-release-process -[4]: https://lists.cncf.io/g/cncf-contour-users/ -[5]: https://lists.cncf.io/g/cncf-contour-distributors-announce/ +[4]: https://groups.google.com/g/project-contour [6]: https://github.com/projectcontour/contour/actions/workflows/build_tag.yaml -[7]: https://github.com/kubernetes-sigs/gateway-api/pull/2556 \ No newline at end of file +[7]: https://github.com/kubernetes-sigs/gateway-api/pull/2884 \ No newline at end of file diff --git a/test/conformance/gatewayapi/gateway_conformance_test.go b/test/conformance/gatewayapi/gateway_conformance_test.go index 361bab69311..86bf2238fc0 100644 --- a/test/conformance/gatewayapi/gateway_conformance_test.go +++ b/test/conformance/gatewayapi/gateway_conformance_test.go @@ -74,12 +74,6 @@ func TestGatewayConformance(t *testing.T) { // See: https://github.com/envoyproxy/envoy/issues/17318 tests.HTTPRouteRedirectPortAndScheme.ShortName, - // Not implemented yet since it's functionally equivalent - // to Timeouts.Request, to be enabled once Gateway API - // supports retries. - // See: https://github.com/projectcontour/contour/issues/6000 - tests.HTTPRouteTimeoutBackendRequest.ShortName, - // Contour supports the positive-case functionality, // but there are some negative cases that aren't fully // implemented plus complications with the test setup itself. diff --git a/test/e2e/fixtures.go b/test/e2e/fixtures.go index bb8e9bfc0ea..4cc20831bf6 100644 --- a/test/e2e/fixtures.go +++ b/test/e2e/fixtures.go @@ -651,9 +651,9 @@ func XDSServerTypeFromEnv() contour_v1alpha1.XDSServerType { func UseFeatureFlagsFromEnv() []string { flags := make([]string, 0) - _, found := os.LookupEnv("CONTOUR_E2E_USE_ENDPOINT_SLICES") + _, found := os.LookupEnv("CONTOUR_E2E_USE_ENDPOINTS") if found { - flags = append(flags, "useEndpointSlices") + flags = append(flags, "useEndpointSlices=false") } return flags } diff --git a/test/e2e/gateway/gateway_test.go b/test/e2e/gateway/gateway_test.go index c0287e53279..506a414ed1a 100644 --- a/test/e2e/gateway/gateway_test.go +++ b/test/e2e/gateway/gateway_test.go @@ -181,6 +181,10 @@ var _ = Describe("Gateway API", func() { f.NamespacedTest("gateway-request-redirect-rule", testWithHTTPGateway(testRequestRedirectRule)) f.NamespacedTest("gateway-backend-tls-policy", testWithHTTPGateway(testBackendTLSPolicy)) + + f.NamespacedTest("gateway-httproute-conflict-match", testWithHTTPGateway(testHTTPRouteConflictMatch)) + + f.NamespacedTest("gateway-httproute-partially-conflict-match", testWithHTTPGateway(testHTTPRoutePartiallyConflictMatch)) }) Describe("Gateway with one HTTP listener and one HTTPS listener", func() { diff --git a/test/e2e/gateway/http_route_conflict_match_test.go b/test/e2e/gateway/http_route_conflict_match_test.go new file mode 100644 index 00000000000..a06458e19b4 --- /dev/null +++ b/test/e2e/gateway/http_route_conflict_match_test.go @@ -0,0 +1,89 @@ +// Copyright Project Contour 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. + +//go:build e2e + +package gateway + +import ( + . "github.com/onsi/ginkgo/v2" + "github.com/stretchr/testify/require" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + gatewayapi_v1 "sigs.k8s.io/gateway-api/apis/v1" + + "github.com/projectcontour/contour/internal/gatewayapi" + "github.com/projectcontour/contour/test/e2e" +) + +func testHTTPRouteConflictMatch(namespace string, gateway types.NamespacedName) { + Specify("Creates two http routes, second one has conflict match against the first one, report Accepted: false", func() { + By("create httproute-1 first") + route1 := &gatewayapi_v1.HTTPRoute{ + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: namespace, + Name: "httproute-1", + }, + Spec: gatewayapi_v1.HTTPRouteSpec{ + Hostnames: []gatewayapi_v1.Hostname{"queryparams.gateway.projectcontour.io"}, + CommonRouteSpec: gatewayapi_v1.CommonRouteSpec{ + ParentRefs: []gatewayapi_v1.ParentReference{ + gatewayapi.GatewayParentRef(gateway.Namespace, gateway.Name), + }, + }, + Rules: []gatewayapi_v1.HTTPRouteRule{ + { + Matches: []gatewayapi_v1.HTTPRouteMatch{ + {QueryParams: gatewayapi.HTTPQueryParamMatches(map[string]string{"animal": "whale"})}, + }, + BackendRefs: gatewayapi.HTTPBackendRef("echo-1", 80, 1), + }, + { + Matches: []gatewayapi_v1.HTTPRouteMatch{ + {QueryParams: gatewayapi.HTTPQueryParamMatches(map[string]string{"animal": "dolphin"})}, + }, + BackendRefs: gatewayapi.HTTPBackendRef("echo-2", 80, 1), + }, + }, + }, + } + _, ok := f.CreateHTTPRouteAndWaitFor(route1, e2e.HTTPRouteAccepted) + require.True(f.T(), ok) + + By("create httproute-2 with conflicted matches") + route2 := &gatewayapi_v1.HTTPRoute{ + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: namespace, + Name: "httproute-2", + }, + Spec: gatewayapi_v1.HTTPRouteSpec{ + Hostnames: []gatewayapi_v1.Hostname{"queryparams.gateway.projectcontour.io"}, + CommonRouteSpec: gatewayapi_v1.CommonRouteSpec{ + ParentRefs: []gatewayapi_v1.ParentReference{ + gatewayapi.GatewayParentRef(gateway.Namespace, gateway.Name), + }, + }, + Rules: []gatewayapi_v1.HTTPRouteRule{ + { + Matches: []gatewayapi_v1.HTTPRouteMatch{ + {QueryParams: gatewayapi.HTTPQueryParamMatches(map[string]string{"animal": "whale"})}, + }, + BackendRefs: gatewayapi.HTTPBackendRef("echo-1", 80, 1), + }, + }, + }, + } + _, ok = f.CreateHTTPRouteAndWaitFor(route2, e2e.HTTPRouteNotAcceptedDueToConflict) + require.True(f.T(), ok) + }) +} diff --git a/test/e2e/gateway/http_route_partially_conflict_match_test.go b/test/e2e/gateway/http_route_partially_conflict_match_test.go new file mode 100644 index 00000000000..4e3ae38d71a --- /dev/null +++ b/test/e2e/gateway/http_route_partially_conflict_match_test.go @@ -0,0 +1,97 @@ +// Copyright Project Contour 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. + +//go:build e2e + +package gateway + +import ( + . "github.com/onsi/ginkgo/v2" + "github.com/stretchr/testify/require" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + gatewayapi_v1 "sigs.k8s.io/gateway-api/apis/v1" + + "github.com/projectcontour/contour/internal/gatewayapi" + "github.com/projectcontour/contour/test/e2e" +) + +func testHTTPRoutePartiallyConflictMatch(namespace string, gateway types.NamespacedName) { + Specify("Creates two http routes, second one has partial conflict match against the first one, has partially match condition", func() { + By("create httproute-1 first") + route1 := &gatewayapi_v1.HTTPRoute{ + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: namespace, + Name: "httproute-1", + }, + Spec: gatewayapi_v1.HTTPRouteSpec{ + Hostnames: []gatewayapi_v1.Hostname{"queryparams.gateway.projectcontour.io"}, + CommonRouteSpec: gatewayapi_v1.CommonRouteSpec{ + ParentRefs: []gatewayapi_v1.ParentReference{ + gatewayapi.GatewayParentRef(gateway.Namespace, gateway.Name), + }, + }, + Rules: []gatewayapi_v1.HTTPRouteRule{ + { + Matches: []gatewayapi_v1.HTTPRouteMatch{ + {QueryParams: gatewayapi.HTTPQueryParamMatches(map[string]string{"animal": "whale"})}, + }, + BackendRefs: gatewayapi.HTTPBackendRef("echo-1", 80, 1), + }, + { + Matches: []gatewayapi_v1.HTTPRouteMatch{ + {QueryParams: gatewayapi.HTTPQueryParamMatches(map[string]string{"animal": "dolphin"})}, + }, + BackendRefs: gatewayapi.HTTPBackendRef("echo-2", 80, 1), + }, + }, + }, + } + _, ok := f.CreateHTTPRouteAndWaitFor(route1, e2e.HTTPRouteAccepted) + require.True(f.T(), ok) + + By("create httproute-2 with only partially conflicted matches") + route2 := &gatewayapi_v1.HTTPRoute{ + ObjectMeta: meta_v1.ObjectMeta{ + Namespace: namespace, + Name: "httproute-2", + }, + Spec: gatewayapi_v1.HTTPRouteSpec{ + Hostnames: []gatewayapi_v1.Hostname{"queryparams.gateway.projectcontour.io"}, + CommonRouteSpec: gatewayapi_v1.CommonRouteSpec{ + ParentRefs: []gatewayapi_v1.ParentReference{ + gatewayapi.GatewayParentRef(gateway.Namespace, gateway.Name), + }, + }, + Rules: []gatewayapi_v1.HTTPRouteRule{ + { + Matches: []gatewayapi_v1.HTTPRouteMatch{ + {QueryParams: gatewayapi.HTTPQueryParamMatches(map[string]string{"animal": "whale"})}, + }, + BackendRefs: gatewayapi.HTTPBackendRef("echo-1", 80, 1), + }, + { + Matches: []gatewayapi_v1.HTTPRouteMatch{ + {QueryParams: gatewayapi.HTTPQueryParamMatches(map[string]string{"no-conflict": "no-joke", "no-conflict-again": "no-joke"})}, + }, + BackendRefs: gatewayapi.HTTPBackendRef("echo-2", 80, 1), + }, + }, + }, + } + // Partially accepted + f.CreateHTTPRouteAndWaitFor(route2, func(*gatewayapi_v1.HTTPRoute) bool { + return e2e.HTTPRoutePartiallyInvalid(route2) && e2e.HTTPRouteAccepted(route2) + }) + }) +} diff --git a/test/e2e/gatewayapi_predicates.go b/test/e2e/gatewayapi_predicates.go index a9ac70d2ee6..22194a67e85 100644 --- a/test/e2e/gatewayapi_predicates.go +++ b/test/e2e/gatewayapi_predicates.go @@ -19,6 +19,8 @@ import ( meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" gatewayapi_v1 "sigs.k8s.io/gateway-api/apis/v1" gatewayapi_v1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2" + + "github.com/projectcontour/contour/internal/status" ) // GatewayClassAccepted returns true if the gateway has a .status.conditions @@ -119,6 +121,40 @@ func HTTPRouteAccepted(route *gatewayapi_v1.HTTPRoute) bool { return false } +// HTTPRouteNotAcceptedDueToConflict returns true if the route has a .status.conditions +// entry of "Accepted: false" && "Reason: RouteMatchConflict" && "Message: HTTPRoute's Match has +// conflict with other HTTPRoute's Match". +func HTTPRouteNotAcceptedDueToConflict(route *gatewayapi_v1.HTTPRoute) bool { + if route == nil { + return false + } + + for _, gw := range route.Status.Parents { + if conditionExistsWithAllKeys(gw.Conditions, string(gatewayapi_v1.RouteConditionAccepted), meta_v1.ConditionFalse, string(status.ReasonRouteRuleMatchConflict), status.MessageRouteRuleMatchConflict) { + return true + } + } + + return false +} + +// HTTPRoutePartiallyInvalid returns true if the route has a .status.conditions +// entry of "PartiallyInvalid: true" && "Reason: RuleMatchPartiallyConflict" && "Message: +// HTTPRoute's Match has partial conflict with other HTTPRoute's Match". +func HTTPRoutePartiallyInvalid(route *gatewayapi_v1.HTTPRoute) bool { + if route == nil { + return false + } + + for _, gw := range route.Status.Parents { + if conditionExistsWithAllKeys(gw.Conditions, string(gatewayapi_v1.RouteConditionPartiallyInvalid), meta_v1.ConditionTrue, string(status.ReasonRouteRuleMatchPartiallyConflict), status.MessageRouteRuleMatchPartiallyConflict) { + return true + } + } + + return false +} + // HTTPRouteIgnoredByContour returns true if the route has an empty .status.parents.conditions list func HTTPRouteIgnoredByContour(route *gatewayapi_v1.HTTPRoute) bool { if route == nil { @@ -194,3 +230,13 @@ func conditionExists(conditions []meta_v1.Condition, conditionType string, condi return false } + +func conditionExistsWithAllKeys(conditions []meta_v1.Condition, conditionType string, conditionStatus meta_v1.ConditionStatus, reason, message string) bool { + for _, cond := range conditions { + if cond.Type == conditionType && cond.Status == conditionStatus && cond.Reason == reason && cond.Message == message { + return true + } + } + + return false +} diff --git a/test/scripts/run-gateway-conformance.sh b/test/scripts/run-gateway-conformance.sh index f8b54d995b6..bdf77b7bc2a 100755 --- a/test/scripts/run-gateway-conformance.sh +++ b/test/scripts/run-gateway-conformance.sh @@ -58,7 +58,7 @@ GO_MOD_GATEWAY_API_VERSION=$(grep "sigs.k8s.io/gateway-api" go.mod | awk '{print if [ "$GATEWAY_API_VERSION" = "$GO_MOD_GATEWAY_API_VERSION" ]; then go test -timeout=40m -tags conformance ./test/conformance/gatewayapi --gateway-class=contour -else +else cd $(mktemp -d) git clone https://github.com/kubernetes-sigs/gateway-api cd gateway-api @@ -67,5 +67,5 @@ else # test/conformance/gatewayapi/gateway_conformance_test.go. go test -timeout=40m ./conformance -run TestConformance -gateway-class=contour -all-features \ -exempt-features=Mesh \ - -skip-tests=HTTPRouteRedirectPortAndScheme,HTTPRouteTimeoutBackendRequest,GatewayStaticAddresses + -skip-tests=HTTPRouteRedirectPortAndScheme,GatewayStaticAddresses fi diff --git a/versions.yaml b/versions.yaml index 5b82e48d79b..dd0910c4751 100644 --- a/versions.yaml +++ b/versions.yaml @@ -6,6 +6,16 @@ metadata: versions: - version: main supported: "false" + dependencies: + envoy: "1.30.1" + kubernetes: + - "1.29" + - "1.28" + - "1.27" + gateway-api: + - "1.0.0" + - version: v1.28.3 + supported: "true" dependencies: envoy: "1.29.3" kubernetes: @@ -15,7 +25,7 @@ versions: gateway-api: - "1.0.0" - version: v1.28.2 - supported: "true" + supported: "false" dependencies: envoy: "1.29.2" kubernetes: @@ -44,8 +54,18 @@ versions: - "1.27" gateway-api: - "1.0.0" - - version: v1.27.1 + - version: v1.27.2 supported: "true" + dependencies: + envoy: "1.28.2" + kubernetes: + - "1.28" + - "1.27" + - "1.26" + gateway-api: + - "0.8.1" + - version: v1.27.1 + supported: "false" dependencies: envoy: "1.28.1" kubernetes: @@ -64,8 +84,18 @@ versions: - "1.26" gateway-api: - "0.8.1" - - version: v1.26.2 + - version: v1.26.3 supported: "true" + dependencies: + envoy: "1.27.4" + kubernetes: + - "1.28" + - "1.27" + - "1.26" + gateway-api: + - "0.8.1" + - version: v1.26.2 + supported: "false" dependencies: envoy: "1.27.3" kubernetes: