Repository: kubernetes-sigs/cloud-provider-kind Branch: main Commit: dbb1ed65c024 Files: 138 Total size: 2.7 MB Directory structure: gitextract_ix7p8aoy/ ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ ├── bats.yml │ ├── gateway.yml │ ├── ingress.yml │ ├── k8s.yml │ ├── release.yml │ └── test.yaml ├── .gitignore ├── .golangci.yaml ├── .goreleaser.yaml ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── OWNERS ├── README.md ├── SECURITY.md ├── SECURITY_CONTACTS ├── cloudbuild.yaml ├── cmd/ │ └── app.go ├── code-of-conduct.md ├── compose.yml ├── docs/ │ ├── .nojekyll │ ├── HOWTO.md │ ├── README.md │ ├── _coverpage.md │ ├── _sidebar.md │ ├── code-of-conduct.md │ ├── contributing/ │ │ └── CONTRIBUTING.md │ ├── index.html │ └── user/ │ ├── example/ │ │ ├── creating_gateway_http_route.md │ │ ├── enable_lb_port_mapping.md │ │ └── service_expose_via_loadbalancer.md │ ├── gateway/ │ │ ├── allow_load_balancer_access_control_plane.md │ │ ├── configure_proxy_image_registry.md │ │ ├── gatewayapi.md │ │ └── running_the_provider.md │ ├── howto.md │ ├── ingress/ │ │ └── ingress.md │ ├── install/ │ │ ├── install_docker.md │ │ └── install_go.md │ ├── os_support.md │ └── support/ │ └── os_support.md ├── examples/ │ ├── gateway_httproute_simple.yaml │ ├── ingress_foo_bar.yaml │ ├── loadbalancer_affinity.yaml │ ├── loadbalancer_deployment.yaml │ ├── loadbalancer_etp_cluster.yaml │ ├── loadbalancer_etp_local.yaml │ ├── loadbalancer_multiport.yaml │ ├── loadbalancer_static_ip.yaml │ ├── loadbalancer_stts.yaml │ └── loadbalancer_udp_tcp.yaml ├── go.mod ├── go.sum ├── hack/ │ ├── build-route-adder.sh │ ├── ci/ │ │ ├── add-kubernetes-to-workspace.sh │ │ └── e2e.sh │ ├── download-gateway-crds.sh │ ├── init-buildx.sh │ └── lint.sh ├── internal/ │ └── routeadder/ │ └── main.go ├── kind.yaml ├── main.go ├── pkg/ │ ├── config/ │ │ └── config.go │ ├── constants/ │ │ └── constants.go │ ├── container/ │ │ └── container.go │ ├── controller/ │ │ ├── controller.go │ │ ├── http.go │ │ └── http_test.go │ ├── gateway/ │ │ ├── backendref.go │ │ ├── backendref_test.go │ │ ├── controller.go │ │ ├── controller_test.go │ │ ├── crd_manager.go │ │ ├── crd_manager_test.go │ │ ├── crds/ │ │ │ ├── README.md │ │ │ ├── experimental/ │ │ │ │ ├── gateway.networking.k8s.io_backendtlspolicies.yaml │ │ │ │ ├── gateway.networking.k8s.io_gatewayclasses.yaml │ │ │ │ ├── gateway.networking.k8s.io_gateways.yaml │ │ │ │ ├── gateway.networking.k8s.io_grpcroutes.yaml │ │ │ │ ├── gateway.networking.k8s.io_httproutes.yaml │ │ │ │ ├── gateway.networking.k8s.io_listenersets.yaml │ │ │ │ ├── gateway.networking.k8s.io_referencegrants.yaml │ │ │ │ ├── gateway.networking.k8s.io_tcproutes.yaml │ │ │ │ ├── gateway.networking.k8s.io_tlsroutes.yaml │ │ │ │ ├── gateway.networking.k8s.io_udproutes.yaml │ │ │ │ ├── gateway.networking.k8s.io_vap_safeupgrades.yaml │ │ │ │ ├── gateway.networking.x-k8s.io_xbackendtrafficpolicies.yaml │ │ │ │ ├── gateway.networking.x-k8s.io_xlistenersets.yaml │ │ │ │ ├── gateway.networking.x-k8s.io_xmeshes.yaml │ │ │ │ └── kustomization.yaml │ │ │ ├── kustomization.yaml │ │ │ └── standard/ │ │ │ ├── gateway.networking.k8s.io_backendtlspolicies.yaml │ │ │ ├── gateway.networking.k8s.io_gatewayclasses.yaml │ │ │ ├── gateway.networking.k8s.io_gateways.yaml │ │ │ ├── gateway.networking.k8s.io_grpcroutes.yaml │ │ │ ├── gateway.networking.k8s.io_httproutes.yaml │ │ │ ├── gateway.networking.k8s.io_listenersets.yaml │ │ │ ├── gateway.networking.k8s.io_referencegrants.yaml │ │ │ ├── gateway.networking.k8s.io_tlsroutes.yaml │ │ │ └── gateway.networking.k8s.io_vap_safeupgrades.yaml │ │ ├── envoy.go │ │ ├── envoy_test.go │ │ ├── gateway.go │ │ ├── gateway_test.go │ │ ├── grpcroute.go │ │ ├── httproute.go │ │ ├── kindcluster.go │ │ ├── listener.go │ │ ├── referencegrant.go │ │ ├── referencegrant_test.go │ │ ├── routeadder/ │ │ │ ├── README.md │ │ │ ├── route-adder-amd64 │ │ │ └── route-adder-arm64 │ │ ├── routes.go │ │ └── routes_test.go │ ├── ingress/ │ │ ├── README.md │ │ ├── controller.go │ │ └── controller_test.go │ ├── loadbalancer/ │ │ ├── proxy.go │ │ ├── proxy_test.go │ │ ├── server.go │ │ └── server_test.go │ ├── provider/ │ │ ├── cloud.go │ │ ├── clusters.go │ │ ├── instances.go │ │ └── loadbalancer.go │ └── tunnels/ │ ├── address_darwin.go │ ├── address_other.go │ ├── address_windows.go │ └── tunnel.go └── tests/ ├── README.md ├── custom-network/ │ ├── setup_suite.bash │ └── tests.bats ├── kind.yaml ├── setup_suite.bash └── tests.bats ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/dependabot.yml ================================================ # To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file version: 2 updates: - package-ecosystem: "gomod" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "weekly" groups: k8s-deps: patterns: - "*k8s.io*" envoy-deps: patterns: - "*envoyproxy*" labels: - "area/dependency" - "ok-to-test" - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" labels: - "area/dependency" - "ok-to-test" - package-ecosystem: "docker" directories: - "**/*" schedule: interval: "weekly" labels: - "area/dependency" - "ok-to-test" ================================================ FILE: .github/workflows/bats.yml ================================================ name: bats on: push: branches: - 'main' tags: - 'v*' pull_request: branches: [ main ] workflow_dispatch: env: GO_VERSION: "1.25" K8S_VERSION: "v1.35.0" KIND_VERSION: "v0.31.0" jobs: bats_tests: runs-on: ubuntu-22.04 name: Bats e2e tests steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Setup Bats and bats libs id: setup-bats uses: bats-core/bats-action@77d6fb60505b4d0d1d73e48bd035b55074bbfb43 # 4.0.0 - name: Bats tests shell: bash env: BATS_LIB_PATH: ${{ steps.setup-bats.outputs.lib-path }} TERM: xterm run: | bats -o _artifacts tests/ - name: Bats tests (custom network) shell: bash env: BATS_LIB_PATH: ${{ steps.setup-bats.outputs.lib-path }} TERM: xterm run: | bats -o _artifacts-custom-network tests/custom-network/ - name: Upload logs if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v5 with: name: kind-logs-${{ env.JOB_NAME }}-${{ github.run_id }} path: | ./_artifacts ./_artifacts-custom-network bats_mac_tests: runs-on: macos-15-intel name: Bats e2e tests on Mac # Skip until runner does not timeout on kind create cluster if: false steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 # c.f. https://github.com/actions/runner-images/issues/13358 - name: Work around x86 macOS bug for multiple CPU cores run: | sudo defaults -currentHost write /Library/Preferences/com.apple.powerlogd SMCMonitorCadence 0 sudo killall PerfPowerServices || true - name: Set up environment (download dependencies) run: | brew install docker colima brew install kind brew install golang brew install bats-core brew install kubectl LIMA_SSH_PORT_FORWARDER=true colima start - name: Bats tests env: TERM: linux run: | bash -c "time bats -o _artifacts --trace --formatter tap tests/" - name: Debug info if: always() run: | docker ps kubectl get pods -v7 kubectl get services -v7 - name: Upload logs if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v5 with: name: kind-logs-mac-${{ github.run_id }} path: ./_artifacts ================================================ FILE: .github/workflows/gateway.yml ================================================ name: gateway on: push: pull_request: workflow_dispatch: env: GO_VERSION: "1.25" GATEWAY_VERSION: "v1.4.1" K8S_VERSION: "v1.35.0" KIND_VERSION: "v0.31.0" KIND_CLUSTER_NAME: "kind-cloud" jobs: gateway: name: gateway runs-on: ubuntu-latest timeout-minutes: 100 env: JOB_NAME: "cloud-provider-kind-e2e-gateway" steps: - name: Set up Go uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version: ${{ env.GO_VERSION }} id: go - name: Check out code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Enable ipv4 and ipv6 forwarding run: | sudo sysctl -w net.ipv6.conf.all.forwarding=1 sudo sysctl -w net.ipv4.ip_forward=1 - name: Set up environment (download dependencies) run: | TMP_DIR=$(mktemp -d) # Test binaries git clone --branch ${{ env.GATEWAY_VERSION }} --depth 1 https://github.com/kubernetes-sigs/gateway-api.git ${TMP_DIR} cd ${TMP_DIR} go test ./conformance/ -c cd - sudo cp ${TMP_DIR}/conformance.test /usr/local/bin/conformance.test sudo chmod +x /usr/local/bin/conformance.test # kubectl curl -L https://dl.k8s.io/${{ env.K8S_VERSION }}/bin/linux/amd64/kubectl -o ${TMP_DIR}/kubectl # kind curl -Lo ${TMP_DIR}/kind https://kind.sigs.k8s.io/dl/${{ env.KIND_VERSION }}/kind-linux-amd64 # Install sudo cp ${TMP_DIR}/kubectl /usr/local/bin/kubectl sudo cp ${TMP_DIR}/kind /usr/local/bin/kind sudo chmod +x /usr/local/bin/kubectl sudo chmod +x /usr/local/bin/kind # Create folder to store artifacts mkdir -p _artifacts - name: Run cloud-provider-kind run: | make nohup bin/cloud-provider-kind -v 2 --enable-log-dumping --logs-dir ./_artifacts/loadbalancers > ./_artifacts/ccm-kind.log 2>&1 & - name: Create multi node cluster run: | # create cluster cat < _artifacts/kubeconfig.conf - name: Get Cluster status run: | /usr/local/bin/kubectl get nodes -o yaml /usr/local/bin/kubectl get pods -A -o wide # wait network is ready /usr/local/bin/kubectl wait --for=condition=ready pods --namespace=kube-system -l k8s-app=kube-dns --timeout=3m /usr/local/bin/kubectl get nodes -o wide /usr/local/bin/kubectl get pods -A - name: Run tests run: | /usr/local/bin/conformance.test -test.run TestConformance \ --debug=true \ --kubeconfig=_artifacts/kubeconfig.conf \ --organization=sigs.k8s.io \ --project=cloud-provider-kind \ --url=https://github.com/kubernetes-sigs/cloud-provider-kind \ --version=${{ github.sha }} \ --contact=https://github.com/kubernetes-sigs/cloud-provider-kind/issues/new \ --gateway-class=cloud-provider-kind \ --conformance-profiles=GATEWAY-HTTP \ --supported-features=Gateway,HTTPRoute,ReferenceGrant \ --report-output=./_artifacts/conformance.yaml - name: Export logs if: always() run: | /usr/local/bin/kind export logs --name ${{ env.KIND_CLUSTER_NAME}} ./_artifacts/logs cp ./_artifacts/ccm-kind.log ./_artifacts/logs - name: Upload logs if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v5 with: name: kind-logs-${{ env.JOB_NAME }}-${{ github.run_id }} path: ./_artifacts ================================================ FILE: .github/workflows/ingress.yml ================================================ name: ingress on: push: pull_request: workflow_dispatch: env: GO_VERSION: "1.25" K8S_VERSION: "v1.35.0" KIND_VERSION: "v0.31.0" KIND_CLUSTER_NAME: "kind-cloud" jobs: ingress: name: ingress runs-on: ubuntu-latest timeout-minutes: 100 env: JOB_NAME: "cloud-provider-kind-e2e-ingress" steps: - name: Set up Go uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6 with: go-version: ${{ env.GO_VERSION }} id: go - name: Check out code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Enable ipv4 and ipv6 forwarding run: | sudo sysctl -w net.ipv6.conf.all.forwarding=1 sudo sysctl -w net.ipv4.ip_forward=1 - name: Set up environment (download dependencies) run: | TMP_DIR=$(mktemp -d) # kubectl curl -L https://dl.k8s.io/${{ env.K8S_VERSION }}/bin/linux/amd64/kubectl -o ${TMP_DIR}/kubectl # kind curl -Lo ${TMP_DIR}/kind https://kind.sigs.k8s.io/dl/${{ env.KIND_VERSION }}/kind-linux-amd64 # Install sudo cp ${TMP_DIR}/kubectl /usr/local/bin/kubectl sudo cp ${TMP_DIR}/kind /usr/local/bin/kind sudo chmod +x /usr/local/bin/kubectl sudo chmod +x /usr/local/bin/kind # Create folder to store artifacts mkdir -p _artifacts - name: Run cloud-provider-kind run: | make nohup bin/cloud-provider-kind -v 2 --enable-log-dumping --logs-dir ./_artifacts/loadbalancers > ./_artifacts/ccm-kind.log 2>&1 & - name: Create multi node cluster run: | # create cluster cat < _artifacts/kubeconfig.conf - name: Get Cluster status run: | /usr/local/bin/kubectl get nodes -o yaml /usr/local/bin/kubectl get pods -A -o wide # wait network is ready /usr/local/bin/kubectl wait --for=condition=ready pods --namespace=kube-system -l k8s-app=kube-dns --timeout=3m /usr/local/bin/kubectl get nodes -o wide /usr/local/bin/kubectl get pods -A - name: Run tests run: | TMP_DIR=$(mktemp -d) # Test binaries git clone --depth 1 https://github.com/kubernetes-sigs/ingress-controller-conformance.git ${TMP_DIR}/ingress-controller-conformance cd ${TMP_DIR}/ingress-controller-conformance # Add the @skip tag to the incompatible test # The Gateway spec for spec.hostnames: ["*.foo.com"] states it matches all subdomains. # The Ingress spec for spec.rules.host: "*.foo.com" states it only matches a single subdomain level. echo "Skipping incompatible test: 'An Ingress with a wildcard host rule should not route traffic matching on more than a single dns label'" sed -i '/Scenario: An Ingress with a wildcard host rule should not route traffic matching on more than a single dns label/i @skip' features/host_rules.feature echo "Skipping load balancing spread test: 'An Ingress with no rules should send all requests to the default backend'" sed -i '/Scenario Outline: An Ingress with no rules should send all requests to the default backend/i @skip' features/load_balancing.feature make build chmod +x ingress-controller-conformance ./ingress-controller-conformance \ --ingress-class=cloud-provider-kind \ --no-colors \ --output-directory="${PWD}/_artifacts" \ --tags="~@skip" \ --wait-time-for-ingress-status=65s - name: Export logs if: always() run: | /usr/local/bin/kind export logs --name ${{ env.KIND_CLUSTER_NAME}} ./_artifacts/logs cp ./_artifacts/ccm-kind.log ./_artifacts/logs - name: Upload logs if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v5 with: name: kind-logs-${{ env.JOB_NAME }}-${{ github.run_id }} path: ./_artifacts ================================================ FILE: .github/workflows/k8s.yml ================================================ name: k8s on: push: pull_request: workflow_dispatch: env: K8S_VERSION: "v1.34.0" KIND_VERSION: "v0.30.0" KIND_CLUSTER_NAME: "kind-cloud" jobs: k8s: name: k8s runs-on: ubuntu-latest timeout-minutes: 100 strategy: fail-fast: false matrix: # TODO add "dual", waiting on KEP https://github.com/kubernetes/enhancements/tree/master/keps/sig-network/3705-cloud-node-ips ipFamily: ["ipv4", "ipv6"] env: JOB_NAME: "cloud-provider-kind-e2e-${{ matrix.ipFamily }}" IP_FAMILY: ${{ matrix.ipFamily }} steps: - name: Set up Go uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version: stable - name: Check out code uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Enable ipv4 and ipv6 forwarding run: | sudo sysctl -w net.ipv6.conf.all.forwarding=1 sudo sysctl -w net.ipv4.ip_forward=1 - name: Set up environment (download dependencies) run: | TMP_DIR=$(mktemp -d) # Test binaries curl -L https://dl.k8s.io/${{ env.K8S_VERSION }}/kubernetes-test-linux-amd64.tar.gz -o ${TMP_DIR}/kubernetes-test-linux-amd64.tar.gz tar xvzf ${TMP_DIR}/kubernetes-test-linux-amd64.tar.gz \ --directory ${TMP_DIR} \ --strip-components=3 kubernetes/test/bin/ginkgo kubernetes/test/bin/e2e.test # kubectl curl -L https://dl.k8s.io/${{ env.K8S_VERSION }}/bin/linux/amd64/kubectl -o ${TMP_DIR}/kubectl # kind curl -Lo ${TMP_DIR}/kind https://kind.sigs.k8s.io/dl/${{ env.KIND_VERSION }}/kind-linux-amd64 # Install sudo cp ${TMP_DIR}/ginkgo /usr/local/bin/ginkgo sudo cp ${TMP_DIR}/e2e.test /usr/local/bin/e2e.test sudo cp ${TMP_DIR}/kubectl /usr/local/bin/kubectl sudo cp ${TMP_DIR}/kind /usr/local/bin/kind sudo chmod +x /usr/local/bin/ginkgo sudo chmod +x /usr/local/bin/e2e.test sudo chmod +x /usr/local/bin/kubectl sudo chmod +x /usr/local/bin/kind # Create folder to store artifacts mkdir -p _artifacts - name: Run cloud-provider-kind run: | make nohup bin/cloud-provider-kind -v 2 --enable-log-dumping --logs-dir ./_artifacts/loadbalancers > ./_artifacts/ccm-kind.log 2>&1 & - name: Create multi node cluster run: | # create cluster cat < _artifacts/kubeconfig.conf - name: Workaround CoreDNS for IPv6 airgapped if: ${{ matrix.ipFamily == 'ipv6' }} run: | # Patch CoreDNS to work in Github CI # 1. Github CI doesn´t offer IPv6 connectivity, so CoreDNS should be configured # to work in an offline environment: # https://github.com/coredns/coredns/issues/2494#issuecomment-457215452 # 2. Github CI adds following domains to resolv.conf search field: # .net. # CoreDNS should handle those domains and answer with NXDOMAIN instead of SERVFAIL # otherwise pods stops trying to resolve the domain. # Get the current config original_coredns=$(/usr/local/bin/kubectl get -oyaml -n=kube-system configmap/coredns) echo "Original CoreDNS config:" echo "${original_coredns}" # Patch it fixed_coredns=$( printf '%s' "${original_coredns}" | sed \ -e 's/^.*kubernetes cluster\.local/& net/' \ -e '/^.*upstream$/d' \ -e '/^.*fallthrough.*$/d' \ -e '/^.*forward . \/etc\/resolv.conf$/d' \ -e '/^.*loop$/d' \ ) echo "Patched CoreDNS config:" echo "${fixed_coredns}" printf '%s' "${fixed_coredns}" | /usr/local/bin/kubectl apply -f - - name: Get Cluster status run: | /usr/local/bin/kubectl get nodes -o yaml /usr/local/bin/kubectl get pods -A -o wide # wait network is ready /usr/local/bin/kubectl wait --for=condition=ready pods --namespace=kube-system -l k8s-app=kube-dns --timeout=3m /usr/local/bin/kubectl get nodes -o wide /usr/local/bin/kubectl get pods -A - name: Run tests run: | export KUBERNETES_CONFORMANCE_TEST='y' export E2E_REPORT_DIR=${PWD}/_artifacts # Run tests and use aws (creates a null e2e provider) to not skip the loadbalancer tests # "should be able to create LoadBalancer Service without NodePort and change it" : the LB implementation uses haproxy and NodePorts can not forward directly to the Pods # "should be able to change the type and ports of a TCP service" : the test expects a connection refused haproxy seems to return EOF # "loadbalancer source ranges" : fails on IPv6 only /usr/local/bin/ginkgo --nodes=25 \ --focus="sig-network" \ --skip="Feature|Federation|PerformanceDNS|DualStack|Disruptive|Serial|KubeProxy|GCE|Netpol|NetworkPolicy|256.search.list.characters|LoadBalancer.Service.without.NodePort|type.and.ports.of.a.TCP.service|loadbalancer.source.ranges" \ /usr/local/bin/e2e.test \ -- \ --kubeconfig=${PWD}/_artifacts/kubeconfig.conf \ --provider=aws \ --dump-logs-on-failure=false \ --report-dir=${E2E_REPORT_DIR} \ --disable-log-dump=true - name: Upload Junit Reports if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v5 with: name: kind-junit-${{ env.JOB_NAME }}-${{ github.run_id }} path: './_artifacts/*.xml' - name: Export logs if: always() run: | /usr/local/bin/kind export logs --name ${{ env.KIND_CLUSTER_NAME}} ./_artifacts/logs cp ./_artifacts/ccm-kind.log ./_artifacts/logs cp ./_artifacts/loadbalancers/* ./_artifacts/logs - name: Upload logs if: always() uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v5 with: name: kind-logs-${{ env.JOB_NAME }}-${{ github.run_id }} path: ./_artifacts/logs - name: Publish Test Report uses: mikepenz/action-junit-report@bccf2e31636835cf0874589931c4116687171386 # v6 if: always() with: report_paths: './_artifacts/*.xml' ================================================ FILE: .github/workflows/release.yml ================================================ # .github/workflows/release.yml name: goreleaser on: push: # run only against tags tags: - "*" permissions: contents: write packages: write # issues: write jobs: goreleaser: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 - name: Set up Go uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version: stable - name: Run GoReleaser uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0 with: distribution: goreleaser # 'latest', 'nightly', or a semver version: latest args: release --clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .github/workflows/test.yaml ================================================ name: Test on: [push, pull_request] env: REGISTRY: ghcr.io IMAGE_NAME: sigs.k8s.io/cloud-provider-kind permissions: write-all jobs: test: strategy: fail-fast: false runs-on: ubuntu-latest steps: - name: Set up Go uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version: stable - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - run: make test - run: make lint ================================================ FILE: .gitignore ================================================ # Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out # Dependency directories (remove the comment below to include it) # vendor/ bin/ dist/ # macOS .DS_Store ================================================ FILE: .golangci.yaml ================================================ version: "2" run: tests: false linters: default: none enable: - errcheck - gocritic - govet - ineffassign - staticcheck exclusions: generated: lax presets: - comments - common-false-positives - legacy - std-error-handling paths: - third_party$ - builtin$ - examples$ formatters: exclusions: generated: lax paths: - third_party$ - builtin$ - examples$ ================================================ FILE: .goreleaser.yaml ================================================ version: 2 project_name: cloud-provider-kind builds: - env: [CGO_ENABLED=0] goos: - linux - windows - darwin goarch: - amd64 - arm64 ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing Guidelines Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://git.k8s.io/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md). Here is an excerpt: _As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._ ## Getting Started We have full documentation on how to get started contributing here: - [Contributor License Agreement](https://git.k8s.io/community/CLA.md) - Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests - [Kubernetes Contributor Guide](https://k8s.dev/guide) - Main contributor documentation, or you can just jump directly to the [contributing page](https://k8s.dev/docs/guide/contributing/) - [Contributor Cheat Sheet](https://k8s.dev/cheatsheet) - Common resources for existing developers ## Mentorship - [Mentoring Initiatives](https://k8s.dev/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers! - [Slack channel](https://kubernetes.slack.com/messages/kind) - [Mailing list](https://groups.google.com/forum/#!forum/kubernetes-sig-testing) ================================================ FILE: Dockerfile ================================================ FROM --platform=$BUILDPLATFORM golang:1.25 WORKDIR /go/src # make deps fetching cacheable COPY go.mod go.sum ./ RUN go mod download # build COPY . . ARG TARGETARCH RUN GOARCH=$TARGETARCH make build # build real cloud-provider-kind image FROM docker:29-cli COPY --from=0 --chown=root:root ./go/src/bin/cloud-provider-kind /bin/cloud-provider-kind ENTRYPOINT ["/bin/cloud-provider-kind"] ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} 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. ================================================ FILE: Makefile ================================================ REPO_ROOT:=${CURDIR} OUT_DIR=$(REPO_ROOT)/bin KIND_CLOUD_BINARY_NAME?=cloud-provider-kind # go1.9+ can autodetect GOROOT, but if some other tool sets it ... GOROOT:= # enable modules GO111MODULE=on # disable CGO by default for static binaries CGO_ENABLED=0 export GOROOT GO111MODULE CGO_ENABLED build: go build -v -o "$(OUT_DIR)/$(KIND_CLOUD_BINARY_NAME)" $(KIND_CLOUD_BUILD_FLAGS) main.go clean: rm -rf "$(OUT_DIR)/" test: CGO_ENABLED=1 go test -v -race -count 1 ./... e2e: cd tests && bats tests.bats # code linters lint: hack/lint.sh update: go mod tidy # get image name from directory we're building IMAGE_NAME?=cloud-provider-kind # docker image registry, default to upstream REGISTRY?=gcr.io/k8s-staging-kind # tag based on date-sha TAG?=$(shell echo "$$(date +v%Y%m%d)-$$(git describe --always --dirty)") # the full image tag CPK_IMAGE?=$(REGISTRY)/$(IMAGE_NAME):$(TAG) PLATFORMS?=linux/amd64,linux/arm64 .PHONY: ensure-buildx ensure-buildx: ./hack/init-buildx.sh image-build: docker buildx build . \ --tag="${CPK_IMAGE}" \ --load image-push: docker buildx build . \ --platform="${PLATFORMS}" \ --tag="${CPK_IMAGE}" \ --push .PHONY: release # Build a multi-arch docker image release: ensure-buildx image-push ================================================ FILE: OWNERS ================================================ # See the OWNERS docs at https://go.k8s.io/owners approvers: - aojea - bentheelder - stmcginnis ================================================ FILE: README.md ================================================ # Kubernetes Cloud Provider for KIND KIND has demonstrated to be a very versatile, efficient, cheap and very useful tool for Kubernetes testing. However, KIND doesn't offer capabilities for testing all the features that depend on cloud-providers, specifically the Load Balancers, causing a gap on testing and a bad user experience, since is not easy to connect to the applications running on the cluster. `cloud-provider-kind` aims to fill this gap and provide an agnostic and cheap solution for all the Kubernetes features that depend on a cloud-provider using KIND. - [Slack channel](https://kubernetes.slack.com/messages/kind) - [Mailing list](https://groups.google.com/forum/#!forum/kubernetes-sig-testing) ## Talks Kubecon EU 2024 - [Keep Calm and Load Balance on KIND - Antonio Ojea & Benjamin Elder, Google](https://sched.co/1YhhY) [![Keep Calm and Load Balance on KIND](https://img.youtube.com/vi/U6_-y24rJnI/0.jpg)](https://www.youtube.com/watch?v=U6_-y24rJnI) ## Install ### Installing with `go install` You can install `cloud-provider-kind` using `go install`: ```sh go install sigs.k8s.io/cloud-provider-kind@latest ``` This will install the binary in `$GOBIN` (typically `~/go/bin`); you can make it available elsewhere if appropriate: ```sh sudo install ~/go/bin/cloud-provider-kind /usr/local/bin ``` ### Installing With A Package Manager The cloud-provider-kind community has enabled installation via the following package managers. > [!NOTE] > The following are community supported efforts. The `cloud-provider-kind` maintainers are not involved in the creation of these packages, and the upstream community makes no claims on the validity, safety, or content of them. On macOS via Homebrew: ```sh brew install cloud-provider-kind ``` ### Running via Docker Image Starting with v0.4.0, the docker image for cloud-provider-kind is available at `registry.k8s.io/cloud-provider-kind/cloud-controller-manager` You can also build it locally: ```sh git clone https://github.com/kubernetes-sigs/cloud-provider-kind.git Cloning into 'cloud-provider-kind'... remote: Enumerating objects: 6779, done. remote: Counting objects: 100% (6779/6779), done. remote: Compressing objects: 100% (4225/4225), done.q remote: Total 6779 (delta 2150), reused 6755 (delta 2135), pack-reused 0 Receiving objects: 100% (6779/6779), 9.05 MiB | 1.83 MiB/s, done. Resolving deltas: 100% (2150/2150), done. cd cloud-provider-kind && make sudo mv ./bin/cloud-provider-kind /usr/local/bin/cloud-provider-kind ``` Another alternative is to run it as a container, but this will require to mount the docker socket inside the container: ```sh docker build . -t cloud-provider-kind # using the host network docker run --rm --network host -v /var/run/docker.sock:/var/run/docker.sock cloud-provider-kind # or the kind network docker run --rm --network kind -v /var/run/docker.sock:/var/run/docker.sock cloud-provider-kind ``` Or using `compose.yaml` file: ```sh # using the `kind` network (`host` is the default value for NET_MODE) NET_MODE=kind docker compose up -d ``` ## Gateway API support This provider has support for the [Gateway API](https://gateway-api.sigs.k8s.io/). It implements the `Gateway` and `HTTPRoute` functionalities and passes the community conformance tests. The Gateway API controller is enabled by default using the standard channel, but you can select the Gateway API release channel (standard/experimental) or just disable the feature completely using the flag `gateway-channel`: ```sh cloud-provider-kind --gateway-channel standard|experimental|disabled ``` ## How to use it Run a KIND cluster: ```sh $ kind create cluster Creating cluster "kind" ... ✓ Ensuring node image (kindest/node:v1.26.0) 🖼 ✓ Preparing nodes 📦 ✓ Writing configuration 📜 ✓ Starting control-plane 🕹️ ✓ Installing CNI 🔌 ✓ Installing StorageClass 💾 Set kubectl context to "kind-kind" You can now use your cluster with: kubectl cluster-info --context kind-kind Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂 ``` ### Allowing load balancers access to control plane nodes By default, [Kubernetes expects workloads will not run on control plane nodes](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/#control-plane-node-isolation) and labels them with [`node.kubernetes.io/exclude-from-external-load-balancers`](https://kubernetes.io/docs/reference/labels-annotations-taints/#node-kubernetes-io-exclude-from-external-load-balancers), which stops load balancers from accessing them. If you are running workloads on control plane nodes, as is the [default kind configuration](https://kind.sigs.k8s.io/docs/user/configuration/#nodes), you will need to remove this label to access them using a LoadBalancer: ```sh $ kubectl label node kind-control-plane node.kubernetes.io/exclude-from-external-load-balancers- ``` ### Running the provider Once the cluster is running, we need to run the `cloud-provider-kind` in a terminal and keep it running. The `cloud-provider-kind` will monitor all your KIND clusters and `Services` with Type `LoadBalancer` and create the corresponding LoadBalancer containers that will expose those Services. ```sh bin/cloud-provider-kind I0416 19:58:18.391222 2526219 controller.go:98] Creating new cloud provider for cluster kind I0416 19:58:18.398569 2526219 controller.go:105] Starting service controller for cluster kind I0416 19:58:18.399421 2526219 controller.go:227] Starting service controller I0416 19:58:18.399582 2526219 shared_informer.go:273] Waiting for caches to sync for service I0416 19:58:18.500460 2526219 shared_informer.go:280] Caches are synced for service ... ``` ### Configuring Proxy Image Registry > [!WARNING] > The proxy image is an implementation detail of `cloud-provider-kind` and it is not guaranteed to be stable. > Changing the image version or tag is not supported and may break the cloud provider. > Use the following instructions only for mirroring the image to a private registry. You can check the image used by the current version of `cloud-provider-kind` running: ```sh bin/cloud-provider-kind list-images ``` If you need to mirror the image to a private registry, you can override the registry URL using the `CLOUD_PROVIDER_KIND_REGISTRY_URL` environment variable. This will use the same image name and tag but with the specified registry. Example of use mirror registry: ```sh CLOUD_PROVIDER_KIND_REGISTRY_URL="" bin/cloud-provider-kind ``` ### Creating a Service and exposing it via a LoadBalancer Let's create an application that listens on port 8080 and expose it in the port 80 using a LoadBalancer. ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: policy-local labels: app: MyLocalApp spec: replicas: 1 selector: matchLabels: app: MyLocalApp template: metadata: labels: app: MyLocalApp spec: containers: - name: agnhost image: registry.k8s.io/e2e-test-images/agnhost:2.40 args: - netexec - --http-port=8080 - --udp-port=8080 ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: lb-service-local spec: type: LoadBalancer externalTrafficPolicy: Local selector: app: MyLocalApp ports: - protocol: TCP port: 80 targetPort: 8080 ``` ```sh $ kubectl apply -f examples/loadbalancer_etp_local.yaml deployment.apps/policy-local created service/lb-service-local created $ kubectl get service/lb-service-local NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE lb-service-local LoadBalancer 10.96.207.137 192.168.8.7 80:31215/TCP 57s ``` We can see how the `EXTERNAL-IP` field contains an IP, and we can use it to connect to our application. ``` $ curl 192.168.8.7:80/hostname policy-local-59854877c9-xwtfk $ kubectl get pods NAME READY STATUS RESTARTS AGE policy-local-59854877c9-xwtfk 1/1 Running 0 2m38s ``` ### Creating a Gateway and a HTTPRoute Similar to Services with LoadBalancers we can use Gateway API ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: prod-web spec: gatewayClassName: cloud-provider-kind listeners: - protocol: HTTP port: 80 name: prod-web-gw allowedRoutes: namespaces: from: Same --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: foo spec: parentRefs: - name: prod-web rules: - backendRefs: - name: myapp-svc port: 8080 --- apiVersion: apps/v1 kind: Deployment metadata: name: myapp spec: selector: matchLabels: app: MyApp replicas: 1 template: metadata: labels: app: MyApp spec: containers: - name: myapp image: registry.k8s.io/e2e-test-images/agnhost:2.39 args: - netexec - --http-port=80 - --delay-shutdown=30 ports: - name: httpd containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: myapp-svc spec: type: ClusterIP selector: app: MyApp ports: - name: httpd port: 8080 targetPort: 80 ``` We can get the external IP associated to the gateway: ```sh kubectl get gateway NAME CLASS ADDRESS PROGRAMMED AGE prod-web cloud-provider-kind 192.168.8.5 True 3d21h ``` and the HTTPRoutes ```sh kubectl get httproutes NAME HOSTNAMES AGE foo 3d21h ``` and test that works: ```sh $ curl 192.168.8.5/hostname myapp-7dcffbf547-9kl2d ``` ### Enabling Load Balancer Port Mapping When running `cloud-provider-kind` in a container on Windows or macOS, accessing `LoadBalancer` services can be challenging. Similar problems occur when running Podman as root, since Podman does not allow binding to privileged ports (e.g., 1-1024). The `--enable-lb-port-mapping` flag provides a solution by enabling the necessary port mapping, allowing host access to these services. It is automatically enabled on platforms where this is required. See [Mac, Windows and WSL2 support](#mac-windows-and-wsl2-support) section for more details. To connect to your service in these cases, run `cloud-provider-kind` with the `--enable-lb-port-mapping` option. This configures the Envoy container with an ephemeral host port that maps to the port the `LoadBalancer`'s external IP is listening on. ```sh bin/cloud-provider-kind --enable-lb-port-mapping ``` For example, given a `LoadBalancer` listening on port `5678`. ```sh > kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE foo-service LoadBalancer 10.96.240.105 10.89.0.10 5678:31889/TCP 14m ``` The Envoy container will have an ephemeral port (e.g., `42381`) mapped to the `LoadBalancer`'s port `5678`. ```sh > podman ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d261abc4b540 docker.io/envoyproxy/envoy:v1.30.1 bash -c echo -en ... 21 seconds ago Up 22 seconds 0.0.0.0:42381->5678/tcp, 0.0.0.0:36673->10000/tcp kindccm-TLRDKPBWWH4DUSI7J7BNE3ABETEPCKSYA6UIWR5B ``` Use this ephemeral port to connect to the service. ```sh curl localhost:42381 ``` ### Mac, Windows and WSL2 support Mac and Windows run the containers inside a VM and, on the contrary to Linux, the KIND nodes are not reachable from the host, so the LoadBalancer assigned IP is not working for users. To solve this problem, cloud-provider-kind, leverages the existing docker portmap capabilities to expose the Loadbalancer IP and Ports on the host. When you start cloud-provider-kind and create a LoadBalancer service, you will notice that a container named `kindccm-...` is launched within Docker. You can access the service by using the port exposed from the container to the host machine with `localhost`. For WSL2, it is recommended to use the default [NAT](https://learn.microsoft.com/en-us/windows/wsl/networking) network mode with Docker Desktop on Windows. On WSL2, you can access the service via the external IP. On Windows, you can access the service by leveraging Docker's portmap capabilities. Limitations: - Mutation of Services, adding or removing ports to an existing Services, is not supported. - cloud-provider-kind binary needs permissions to add IP address to interfaces and to listen on privileged ports. - Overlapping IP between the containers and the host can break connectivity. Mainly tested with `docker` and `Linux`, though `Windows`, `Mac` and `WSL2` are also basically supported: - On macOS and WSL2 you must run cloud-provider-kind using `sudo` - On Windows you must run cloud-provider-kind from a shell that uses `Run as administrator` - Further feedback from users will be helpful to support other related platforms. **Note** The project is still in very alpha state, bugs are expected, please report them back opening a Github issue. ### Code of conduct Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md). [owners]: https://git.k8s.io/community/contributors/guide/owners.md [Creative Commons 4.0]: https://git.k8s.io/website/LICENSE ================================================ FILE: SECURITY.md ================================================ # Security Policy ## Security Announcements Join the [kubernetes-security-announce] group for security and vulnerability announcements. ## Reporting a Vulnerability Instructions for reporting a vulnerability can be found on the [Kubernetes Security and Disclosure Information] page. ## Supported Versions Information about supported Kubernetes versions can be found on the [Kubernetes version and version skew support policy] page on the Kubernetes website. [kubernetes-security-announce]: https://groups.google.com/forum/#!forum/kubernetes-security-announce [Kubernetes version and version skew support policy]: https://kubernetes.io/docs/setup/release/version-skew-policy/#supported-versions [Kubernetes Security and Disclosure Information]: https://kubernetes.io/docs/reference/issues-security/security/#report-a-vulnerability ================================================ FILE: SECURITY_CONTACTS ================================================ # Defined below are the security contacts for this repo. # # They are the contact point for the Security Response Committee to reach out # to for triaging and handling of incoming issues. # # The below names agree to abide by the # [Embargo Policy](https://git.k8s.io/security/private-distributors-list.md#embargo-policy) # and will be removed and replaced if they violate that agreement. # # DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE # INSTRUCTIONS AT https://kubernetes.io/security/ aojea bentheelder ================================================ FILE: cloudbuild.yaml ================================================ # See https://cloud.google.com/cloud-build/docs/build-config options: substitution_option: ALLOW_LOOSE machineType: E2_HIGHCPU_32 steps: - name: gcr.io/k8s-staging-test-infra/gcb-docker-gcloud:v20240718-5ef92b5c36 entrypoint: make env: - REGISTRY=us-central1-docker.pkg.dev/k8s-staging-images/cloud-provider-kind - IMAGE_NAME=cloud-controller-manager args: ['release'] ================================================ FILE: cmd/app.go ================================================ package cmd import ( "context" "fmt" "os" "os/signal" "runtime" "strconv" "strings" "syscall" "github.com/spf13/cobra" "github.com/spf13/pflag" "k8s.io/component-base/logs" "k8s.io/klog/v2" "sigs.k8s.io/cloud-provider-kind/pkg/config" "sigs.k8s.io/cloud-provider-kind/pkg/container" "sigs.k8s.io/cloud-provider-kind/pkg/controller" "sigs.k8s.io/kind/pkg/cluster" kindcmd "sigs.k8s.io/kind/pkg/cmd" ) var ( flagV int enableLogDump bool logDumpDir string enableLBPortMapping bool gatewayChannel string enableDefaultIngress bool ) func Main() { cmd := NewCommand() if err := cmd.Execute(); err != nil { os.Exit(1) } } func NewCommand() *cobra.Command { cmd := &cobra.Command{ Use: "cloud-provider-kind", Short: "cloud-provider-kind is a cloud provider for kind clusters", RunE: runE, // We silence usage on error because we don't want to print usage // when the command fails due to a runtime error. SilenceUsage: true, } // Register flags cmd.Flags().IntVarP(&flagV, "verbosity", "v", 2, "Verbosity level") cmd.Flags().BoolVar(&enableLogDump, "enable-log-dumping", false, "store logs to a temporal directory or to the directory specified using the logs-dir flag") cmd.Flags().StringVar(&logDumpDir, "logs-dir", "", "store logs to the specified directory") cmd.Flags().BoolVar(&enableLBPortMapping, "enable-lb-port-mapping", false, "enable port-mapping on the load balancer ports") cmd.Flags().StringVar(&gatewayChannel, "gateway-channel", "standard", "define the gateway API release channel to be used (standard, experimental, disabled), by default is standard") cmd.Flags().BoolVar(&enableDefaultIngress, "enable-default-ingress", true, "enable default ingress for the cloud provider kind ingress") cmd.AddCommand(newListImagesCommand()) return cmd } func newListImagesCommand() *cobra.Command { return &cobra.Command{ Use: "list-images", Short: "list images used by cloud-provider-kind", Run: func(cmd *cobra.Command, args []string) { fmt.Println(config.DefaultConfig.ProxyImage) }, } } func runE(cmd *cobra.Command, args []string) error { // Log flags cmd.Flags().VisitAll(func(flag *pflag.Flag) { klog.Infof("FLAG: --%s=%q", flag.Name, flag.Value) }) // Process on macOS must run using sudo if runtime.GOOS == "darwin" && syscall.Geteuid() != 0 { return fmt.Errorf("please run this again with `sudo`") } // trap Ctrl+C and call cancel on the context ctx := context.Background() ctx, cancel := context.WithCancel(ctx) // Enable signal handler signalCh := make(chan os.Signal, 2) defer func() { close(signalCh) cancel() }() signal.Notify(signalCh, syscall.SIGTERM, syscall.SIGINT) go func() { select { case <-signalCh: klog.Infof("Exiting: received signal") cancel() case <-ctx.Done(): // cleanup } }() // initialize loggers, kind logger and klog logger := kindcmd.NewLogger() type verboser interface { SetVerbosity(int) } v, ok := logger.(verboser) if ok { v.SetVerbosity(flagV) } _, err := logs.GlogSetter(strconv.Itoa(flagV)) if err != nil { logger.Errorf("error setting klog verbosity to %d : %v", flagV, err) } config.DefaultConfig.IngressDefault = enableDefaultIngress // Validate gateway channel channel := config.GatewayReleaseChannel(gatewayChannel) if channel != config.Standard && channel != config.Experimental && channel != config.Disabled { return fmt.Errorf("unknown Gateway API release channel %q", gatewayChannel) } config.DefaultConfig.GatewayReleaseChannel = channel // initialize log directory if enableLogDump { if logDumpDir == "" { dir, err := os.MkdirTemp(os.TempDir(), "kind-provider-") if err != nil { return err } logDumpDir = dir } if _, err := os.Stat(logDumpDir); os.IsNotExist(err) { if err := os.MkdirAll(logDumpDir, 0755); err != nil { return fmt.Errorf("directory %s does not exist: %v", logDumpDir, err) } } config.DefaultConfig.EnableLogDump = true config.DefaultConfig.LogDir = logDumpDir klog.Infof("**** Dumping load balancers logs to: %s", logDumpDir) } // some platforms require to enable tunneling for the LoadBalancers if runtime.GOOS == "darwin" || runtime.GOOS == "windows" || isWSL2() { config.DefaultConfig.LoadBalancerConnectivity = config.Tunnel } // flag overrides autodetection if enableLBPortMapping { config.DefaultConfig.LoadBalancerConnectivity = config.Portmap } // default control plane connectivity to portmap, it will be // overriden if the first cluster added detects direct // connecitivity config.DefaultConfig.ControlPlaneConnectivity = config.Portmap // initialize kind provider var option cluster.ProviderOption switch p := container.Runtime(); p { case "podman": option = cluster.ProviderWithPodman() case "nerdctl", "finch", "nerdctl.lima": option = cluster.ProviderWithNerdctl(p) default: option = cluster.ProviderWithDocker() } kindProvider := cluster.NewProvider( option, cluster.ProviderWithLogger(logger), ) controller.New(kindProvider).Run(ctx) return nil } func isWSL2() bool { if v, err := os.ReadFile("/proc/version"); err == nil { return strings.Contains(string(v), "WSL2") } return false } ================================================ FILE: code-of-conduct.md ================================================ # Kubernetes Community Code of Conduct Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md) ================================================ FILE: compose.yml ================================================ services: cloud-provider: build: . network_mode: "${NET_MODE-host}" volumes: - /var/run/docker.sock:/var/run/docker.sock ================================================ FILE: docs/.nojekyll ================================================ ================================================ FILE: docs/HOWTO.md ================================================ # Documentation Guide This documentation site is generated and served using **Docsify**, a lightweight documentation site generator that renders Markdown files on the fly. ## Serving the Docsify Site To run the documentation locally: ```bash docsify serve ./docs ``` This command starts a local server and serves the documentation from the current directory. Open the provided URL in your browser to view the site. --- ## Adding a New Link to the Sidebar The sidebar content is managed by the `_sidebar.md` file. To add a new link: 1. Open `_sidebar.md` 2. Insert a new list item pointing to your Markdown file, for example: ```markdown - [Install Docker](user/install/install_docker.md) - [Gateway API Overview](user/gateway/gatewayapi.md) ``` Docsify will automatically load the linked page when clicked. --- ## Adding a New Page To create a new documentation page: 1. Add a new `.md` file under the appropriate folder. Example: ``` user/example/new_feature_guide.md ``` 2. Add a link to this file in `_sidebar.md` so it appears in navigation. --- ## Folder Structure Overview Below is the current folder hierarchy used for organizing the docs: ``` ├── code-of-conduct.md # Code of Conduct guidelines ├── contributing/ │ └── CONTRIBUTING.md # Contribution guide ├── _coverpage.md # Docsify cover page configuration ├── design/ │ └── images # Design-related assets ├── HOWTO.md # General how-to guide ├── index.html # Docsify entry point ├── README.md # Project introduction ├── _sidebar.md # Sidebar navigation config └── user/ ├── example/ # Example implementation guides ├── gateway/ # Gateway-related configuration guides ├── howto.md # Additional user how-tos ├── images/ # Image assets for user docs ├── ingress/ # Ingress-related guides ├── install/ # Installation instructions ├── os_support.md # OS support documentation └── support/ # Support-related docs ``` This structure helps keep the content organized by feature areas and documentation types. --- ## Further Configuration For more advanced configuration, theming, plugins, and deployment options, please refer to the official Docsify documentation: [https://docsify.js.org](https://docsify.js.org) ================================================ FILE: docs/README.md ================================================ # Kubernetes Cloud Provider for KIND KIND has demonstrated to be a very versatile, efficient, cheap and very useful tool for Kubernetes testing. However, KIND doesn't offer capabilities for testing all the features that depend on cloud-providers, specifically the Load Balancers, causing a gap on testing and a bad user experience, since is not easy to connect to the applications running on the cluster. `cloud-provider-kind` aims to fill this gap and provide an agnostic and cheap solution for all the Kubernetes features that depend on a cloud-provider using KIND. - [Slack channel](https://kubernetes.slack.com/messages/kind) - [Mailing list](https://groups.google.com/forum/#!forum/kubernetes-sig-testing) ## Talks Kubecon EU 2024 - [Keep Calm and Load Balance on KIND - Antonio Ojea & Benjamin Elder, Google](https://sched.co/1YhhY) [![Keep Calm and Load Balance on KIND](https://img.youtube.com/vi/U6_-y24rJnI/0.jpg)](https://www.youtube.com/watch?v=U6_-y24rJnI) ### Code of conduct Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md). [owners]: https://git.k8s.io/community/contributors/guide/owners.md [Creative Commons 2.0]: https://git.k8s.io/website/LICENSE ================================================ FILE: docs/_coverpage.md ================================================ # Kubernetes Cloud Provider for KIND > cloud-provider-kind aims to fill this gap and provide an agnostic and cheap solution for all the Kubernetes features that depend on a cloud-provider using KIND. [GitHub](//github.com/kubernetes-sigs/cloud-provider-kind) [Get Started](#main) ================================================ FILE: docs/_sidebar.md ================================================ - Install - [Installation with go ](user/install/install_go.md) - [Running with docker](user/install/install_docker.md) - Ingress - [Setting up Ingress](user/ingress/ingress.md) - Gateway API - [Gateway API Support (Alpha)](user/gateway/gatewayapi.md) - [LoadBalancer Access to ControlPlane](user/gateway/allow_load_balancer_access_control_plane.md) - [Running the Provider](user/gateway/running_the_provider.md) - [Configuring Proxy Image Registry](user/gateway/configure_proxy_image_registry.md) - Usage Examples - [Creating service via a LoadBalancer](user/example/service_expose_via_loadbalancer.md) - [Creating a Gateway and a HTTPRoute](user/example/creating_gateway_http_route.md.md) - [Enabling Load Balancer Port Mapping](user/example/enable_lb_port_mapping.md) - Support - [Mac, Windows and WSL2 support](user/support/os_support.md) - Contributing - [Contribution Guide Lines](contributing/CONTRIBUTING.md) ================================================ FILE: docs/code-of-conduct.md ================================================ # Kubernetes Community Code of Conduct Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md) ================================================ FILE: docs/contributing/CONTRIBUTING.md ================================================ # Contributing Guidelines Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://git.k8s.io/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md). Here is an excerpt: _As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._ ## Getting Started We have full documentation on how to get started contributing here: - [Contributor License Agreement](https://git.k8s.io/community/CLA.md) - Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests - [Kubernetes Contributor Guide](https://k8s.dev/guide) - Main contributor documentation, or you can just jump directly to the [contributing page](https://k8s.dev/docs/guide/contributing/) - [Contributor Cheat Sheet](https://k8s.dev/cheatsheet) - Common resources for existing developers ## Mentorship - [Mentoring Initiatives](https://k8s.dev/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers! - [Slack channel](https://kubernetes.slack.com/messages/kind) - [Mailing list](https://groups.google.com/forum/#!forum/kubernetes-sig-testing) ================================================ FILE: docs/index.html ================================================ Kubernetes Cloud Provider for KIND
================================================ FILE: docs/user/example/creating_gateway_http_route.md ================================================ ### Creating a Gateway and a HTTPRoute Similar to Services with LoadBalancers we can use Gateway API ```yaml apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: prod-web spec: gatewayClassName: cloud-provider-kind listeners: - protocol: HTTP port: 80 name: prod-web-gw allowedRoutes: namespaces: from: Same --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: foo spec: parentRefs: - name: prod-web rules: - backendRefs: - name: myapp-svc port: 8080 --- apiVersion: apps/v1 kind: Deployment metadata: name: myapp spec: selector: matchLabels: app: MyApp replicas: 1 template: metadata: labels: app: MyApp spec: containers: - name: myapp image: registry.k8s.io/e2e-test-images/agnhost:2.39 args: - netexec - --http-port=80 - --delay-shutdown=30 ports: - name: httpd containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: myapp-svc spec: type: ClusterIP selector: app: MyApp ports: - name: httpd port: 8080 targetPort: 80 ``` We can get the external IP associated to the gateway: ```sh kubectl get gateway NAME CLASS ADDRESS PROGRAMMED AGE prod-web cloud-provider-kind 192.168.8.5 True 3d21h ``` and the HTTPRoutes ```sh kubectl get httproutes NAME HOSTNAMES AGE foo 3d21h ``` and test that works: ```sh $ curl 192.168.8.5/hostname myapp-7dcffbf547-9kl2d ``` ================================================ FILE: docs/user/example/enable_lb_port_mapping.md ================================================ ### Enabling Load Balancer Port Mapping When running `cloud-provider-kind` in a container on Windows or macOS, accessing `LoadBalancer` services can be challenging. Similar problems occur when running Podman as root, since Podman does not allow binding to privileged ports (e.g., 1-1024). The `-enable-lb-port-mapping` flag provides a solution by enabling the necessary port mapping, allowing host access to these services. To connect to your service in these cases, run `cloud-provider-kind` with the `-enable-lb-port-mapping` option. This configures the Envoy container with an ephemeral host port that maps to the port the `LoadBalancer`'s external IP is listening on. ``` bin/cloud-provider-kind -enable-lb-port-mapping ``` For example, given a `LoadBalancer` listening on port `5678`. ``` > kubectl get service NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE foo-service LoadBalancer 10.96.240.105 10.89.0.10 5678:31889/TCP 14m ``` The Envoy container will have an ephemeral port (e.g., `42381`) mapped to the `LoadBalancer`'s port `5678`. ``` > podman ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d261abc4b540 docker.io/envoyproxy/envoy:v1.30.1 bash -c echo -en ... 21 seconds ago Up 22 seconds 0.0.0.0:42381->5678/tcp, 0.0.0.0:36673->10000/tcp kindccm-TLRDKPBWWH4DUSI7J7BNE3ABETEPCKSYA6UIWR5B ``` Use this ephemeral port to connect to the service. ``` curl localhost:42381 ``` ================================================ FILE: docs/user/example/service_expose_via_loadbalancer.md ================================================ ### Creating a Service and exposing it via a LoadBalancer Let's create an application that listens on port 8080 and expose it in the port 80 using a LoadBalancer. ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: policy-local labels: app: MyLocalApp spec: replicas: 1 selector: matchLabels: app: MyLocalApp template: metadata: labels: app: MyLocalApp spec: containers: - name: agnhost image: registry.k8s.io/e2e-test-images/agnhost:2.40 args: - netexec - --http-port=8080 - --udp-port=8080 ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: lb-service-local spec: type: LoadBalancer externalTrafficPolicy: Local selector: app: MyLocalApp ports: - protocol: TCP port: 80 targetPort: 8080 ``` ```sh $ kubectl apply -f examples/loadbalancer_etp_local.yaml deployment.apps/policy-local created service/lb-service-local created $ kubectl get service/lb-service-local NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE lb-service-local LoadBalancer 10.96.207.137 192.168.8.7 80:31215/TCP 57s ``` We can see how the `EXTERNAL-IP` field contains an IP, and we can use it to connect to our application. ``` $ curl 192.168.8.7:80/hostname policy-local-59854877c9-xwtfk $ kubectl get pods NAME READY STATUS RESTARTS AGE policy-local-59854877c9-xwtfk 1/1 Running 0 2m38s ``` ================================================ FILE: docs/user/gateway/allow_load_balancer_access_control_plane.md ================================================ ### Allowing load balancers access to control plane nodes By default, [Kubernetes expects workloads will not run on control plane nodes](https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/#control-plane-node-isolation) and labels them with [`node.kubernetes.io/exclude-from-external-load-balancers`](https://kubernetes.io/docs/reference/labels-annotations-taints/#node-kubernetes-io-exclude-from-external-load-balancers), which stops load balancers from accessing them. If you are running workloads on control plane nodes, as is the [default kind configuration](https://kind.sigs.k8s.io/docs/user/configuration/#nodes), you will need to remove this label to access them using a LoadBalancer: ```sh $ kubectl label node kind-control-plane node.kubernetes.io/exclude-from-external-load-balancers- ``` ================================================ FILE: docs/user/gateway/configure_proxy_image_registry.md ================================================ ### Configuring Proxy Image Registry > [!WARNING] > The proxy image is an implementation detail of `cloud-provider-kind` and it is not guaranteed to be stable. > Changing the image version or tag is not supported and may break the cloud provider. > Use the following instructions only for mirroring the image to a private registry. You can check the image used by the current version of `cloud-provider-kind` running: ```sh bin/cloud-provider-kind list-images ``` If you need to mirror the image to a private registry, you can override the registry URL using the `CLOUD_PROVIDER_KIND_REGISTRY_URL` environment variable. This will use the same image name and tag but with the specified registry. Example of use mirror registry: ```sh CLOUD_PROVIDER_KIND_REGISTRY_URL="" bin/cloud-provider-kind ``` ================================================ FILE: docs/user/gateway/gatewayapi.md ================================================ ## Gateway API support This provider has support for the [Gateway API](https://gateway-api.sigs.k8s.io/). It implements the `Gateway` and `HTTPRoute` functionalities and passes the community conformance tests. The Gateway API controller is enabled by default using the standard channel, but you can select the Gateway API release channel (standard/experimental) or just disable the feature completely using the flag `gateway-channel`: ```sh cloud-provider-kind --gateway-channel standard|experimental|disabled ``` ================================================ FILE: docs/user/gateway/running_the_provider.md ================================================ ### Running the provider Once the cluster is running, we need to run the `cloud-provider-kind` in a terminal and keep it running. The `cloud-provider-kind` will monitor all your KIND clusters and `Services` with Type `LoadBalancer` and create the corresponding LoadBalancer containers that will expose those Services. ```sh bin/cloud-provider-kind I0416 19:58:18.391222 2526219 controller.go:98] Creating new cloud provider for cluster kind I0416 19:58:18.398569 2526219 controller.go:105] Starting service controller for cluster kind I0416 19:58:18.399421 2526219 controller.go:227] Starting service controller I0416 19:58:18.399582 2526219 shared_informer.go:273] Waiting for caches to sync for service I0416 19:58:18.500460 2526219 shared_informer.go:280] Caches are synced for service ... ``` ================================================ FILE: docs/user/howto.md ================================================ ## How to use it Run a KIND cluster: ```sh $ kind create cluster Creating cluster "kind" ... ✓ Ensuring node image (kindest/node:v1.26.0) 🖼 ✓ Preparing nodes 📦 ✓ Writing configuration 📜 ✓ Starting control-plane 🕹️ ✓ Installing CNI 🔌 ✓ Installing StorageClass 💾 Set kubectl context to "kind-kind" You can now use your cluster with: kubectl cluster-info --context kind-kind Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂 ``` ================================================ FILE: docs/user/ingress/ingress.md ================================================ ## Compatibility: This guide applies to [cloud-provider-kind](https://github.com/kubernetes-sigs/cloud-provider-kind) v0.9.0+. For older versions, refer to historical docs. ## Setting Up Ingress Ingress exposes HTTP and HTTPS routes from outside the cluster to services within the cluster. Since cloud-provider-kind v0.9.0, it natively supports Ingress. No third-party ingress controllers are required by default. For third-party ingress solutions (e.g., Ingress NGINX, Contour), please follow their official documentation. > **NOTE**: Gateway API is also natively supported (along with Ingress). See the official [Ingress migration guide](https://gateway-api.sigs.k8s.io/guides/migrating-from-ingress/) for details. ## Create Cluster > **WARNING**: If you are using a [rootless container runtime], ensure your host is > properly configured before creating the KIND cluster. Most Ingress and Gateway controllers will > not work if these steps are skipped. Create a kind cluster and run [Cloud Provider KIND] that automatically enables LoadBalancer support for Ingress. Create a cluster as follows. ```bash kind create cluster ``` ## Using Ingress The following example creates simple http-echo services and an Ingress object to route to these services. ```yaml kind: Pod apiVersion: v1 metadata: name: foo-app labels: app: foo spec: containers: - command: - /agnhost - serve-hostname - --http=true - --port=8080 image: registry.k8s.io/e2e-test-images/agnhost:2.39 name: foo-app --- kind: Service apiVersion: v1 metadata: name: foo-service spec: selector: app: foo ports: # Default port used by the image - port: 8080 --- kind: Pod apiVersion: v1 metadata: name: bar-app labels: app: bar spec: containers: - command: - /agnhost - serve-hostname - --http=true - --port=8080 image: registry.k8s.io/e2e-test-images/agnhost:2.39 name: bar-app --- kind: Service apiVersion: v1 metadata: name: bar-service spec: selector: app: bar ports: # Default port used by the image - port: 8080 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress spec: rules: - http: paths: - pathType: Prefix path: /foo backend: service: name: foo-service port: number: 8080 - pathType: Prefix path: /bar backend: service: name: bar-service port: number: 8080 --- ``` Apply the configuration: ```bash kubectl apply -f https://kind.sigs.k8s.io/examples/ingress/usage.yaml ``` ### Verify Ingress Works Check the External IP assigned to the Ingress by the built-in LoadBalancer. ```bash kubectl get ingress NAME CLASS HOSTS ADDRESS PORTS AGE example-ingress example.com 172.18.0.5 80 10m ``` # get the Ingress IP ```bash INGRESS_IP=$(kubectl get ingress example-ingress -o jsonpath='{.status.loadBalancer.ingress[0].ip}') # should output "foo-app" curl ${INGRESS_IP}/foo # should output "bar-app" curl ${INGRESS_IP}/bar ``` [LoadBalancer]: /docs/user/loadbalancer/ [Cloud Provider KIND]: /docs/user/loadbalancer/ [rootless container runtime]: /docs/user/rootless/ ================================================ FILE: docs/user/install/install_docker.md ================================================ ### Running via Docker Image Starting with v0.4.0, the docker image for cloud-provider-kind is available at `registry.k8s.io/cloud-provider-kind/cloud-controller-manager` You can also build it locally: ```sh git clone https://github.com/kubernetes-sigs/cloud-provider-kind.git Cloning into 'cloud-provider-kind'... remote: Enumerating objects: 6779, done. remote: Counting objects: 100% (6779/6779), done. remote: Compressing objects: 100% (4225/4225), done.q remote: Total 6779 (delta 2150), reused 6755 (delta 2135), pack-reused 0 Receiving objects: 100% (6779/6779), 9.05 MiB | 1.83 MiB/s, done. Resolving deltas: 100% (2150/2150), done. cd cloud-provider-kind && make sudo mv ./bin/cloud-provider-kind /usr/local/bin/cloud-provider-kind ``` Another alternative is to run it as a container, but this will require to mount the docker socket inside the container: ```sh docker build . -t cloud-provider-kind # using the host network docker run --rm --network host -v /var/run/docker.sock:/var/run/docker.sock cloud-provider-kind # or the kind network docker run --rm --network kind -v /var/run/docker.sock:/var/run/docker.sock cloud-provider-kind ``` Or using `compose.yaml` file: ```sh # using the `kind` network (`host` is the default value for NET_MODE) NET_MODE=kind docker compose up -d ``` ================================================ FILE: docs/user/install/install_go.md ================================================ ### Installing with `go install` You can install `cloud-provider-kind` using `go install`: ```sh go install sigs.k8s.io/cloud-provider-kind@latest ``` This will install the binary in `$GOBIN` (typically `~/go/bin`); you can make it available elsewhere if appropriate: ```sh sudo install ~/go/bin/cloud-provider-kind /usr/local/bin ``` ### Installing With A Package Manager The cloud-provider-kind community has enabled installation via the following package managers. > [!NOTE] > The following are community supported efforts. The `cloud-provider-kind` maintainers are not involved in the creation of these packages, and the upstream community makes no claims on the validity, safety, or content of them. On macOS via Homebrew: ```sh brew install cloud-provider-kind ``` ================================================ FILE: docs/user/os_support.md ================================================ ### Mac, Windows and WSL2 support Mac and Windows run the containers inside a VM and, on the contrary to Linux, the KIND nodes are not reachable from the host, so the LoadBalancer assigned IP is not working for users. To solve this problem, cloud-provider-kind, leverages the existing docker portmap capabilities to expose the Loadbalancer IP and Ports on the host. When you start cloud-provider-kind and create a LoadBalancer service, you will notice that a container named `kindccm-...` is launched within Docker. You can access the service by using the port exposed from the container to the host machine with `localhost`. For WSL2, it is recommended to use the default [NAT](https://learn.microsoft.com/en-us/windows/wsl/networking) network mode with Docker Desktop on Windows. On WSL2, you can access the service via the external IP. On Windows, you can access the service by leveraging Docker's portmap capabilities. Limitations: - Mutation of Services, adding or removing ports to an existing Services, is not supported. - cloud-provider-kind binary needs permissions to add IP address to interfaces and to listen on privileged ports. - Overlapping IP between the containers and the host can break connectivity. Mainly tested with `docker` and `Linux`, though `Windows`, `Mac` and `WSL2` are also basically supported: - On macOS and WSL2 you must run cloud-provider-kind using `sudo` - On Windows you must run cloud-provider-kind from a shell that uses `Run as administrator` - Further feedback from users will be helpful to support other related platforms. ================================================ FILE: docs/user/support/os_support.md ================================================ ### Mac, Windows and WSL2 support Mac and Windows run the containers inside a VM and, on the contrary to Linux, the KIND nodes are not reachable from the host, so the LoadBalancer assigned IP is not working for users. To solve this problem, cloud-provider-kind, leverages the existing docker portmap capabilities to expose the Loadbalancer IP and Ports on the host. When you start cloud-provider-kind and create a LoadBalancer service, you will notice that a container named `kindccm-...` is launched within Docker. You can access the service by using the port exposed from the container to the host machine with `localhost`. For WSL2, it is recommended to use the default [NAT](https://learn.microsoft.com/en-us/windows/wsl/networking) network mode with Docker Desktop on Windows. On WSL2, you can access the service via the external IP. On Windows, you can access the service by leveraging Docker's portmap capabilities. Limitations: - Mutation of Services, adding or removing ports to an existing Services, is not supported. - cloud-provider-kind binary needs permissions to add IP address to interfaces and to listen on privileged ports. - Overlapping IP between the containers and the host can break connectivity. Mainly tested with `docker` and `Linux`, though `Windows`, `Mac` and `WSL2` are also basically supported: - On macOS and WSL2 you must run cloud-provider-kind using `sudo` - On Windows you must run cloud-provider-kind from a shell that uses `Run as administrator` - Further feedback from users will be helpful to support other related platforms. **Note** The project is still in very alpha state, bugs are expected, please report them back opening a Github issue. ================================================ FILE: examples/gateway_httproute_simple.yaml ================================================ apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: prod-web spec: gatewayClassName: cloud-provider-kind listeners: - protocol: HTTP port: 80 name: prod-web-gw allowedRoutes: namespaces: from: Same --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: foo spec: parentRefs: - name: prod-web rules: - backendRefs: - name: myapp-svc port: 8080 --- apiVersion: apps/v1 kind: Deployment metadata: name: myapp spec: selector: matchLabels: app: MyApp replicas: 1 template: metadata: labels: app: MyApp spec: containers: - name: myapp image: registry.k8s.io/e2e-test-images/agnhost:2.63.0 args: - netexec - --http-port=80 - --delay-shutdown=30 ports: - name: httpd containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: myapp-svc spec: type: ClusterIP selector: app: MyApp ports: - name: httpd port: 8080 targetPort: 80 ================================================ FILE: examples/ingress_foo_bar.yaml ================================================ kind: Pod apiVersion: v1 metadata: name: foo-app labels: app: foo spec: containers: - command: - /agnhost - netexec - --http-port=8080 image: registry.k8s.io/e2e-test-images/agnhost:2.63.0 name: foo-app --- kind: Service apiVersion: v1 metadata: name: foo-service spec: selector: app: foo ports: - port: 8080 --- kind: Pod apiVersion: v1 metadata: name: bar-app labels: app: bar spec: containers: - command: - /agnhost - netexec - --http-port=8080 image: registry.k8s.io/e2e-test-images/agnhost:2.63.0 name: bar-app --- kind: Service apiVersion: v1 metadata: name: bar-service spec: selector: app: bar ports: - port: 8080 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress spec: rules: - host: foo.example.com http: paths: - pathType: Prefix path: / backend: service: name: foo-service port: number: 8080 - host: bar.example.com http: paths: - pathType: Prefix path: / backend: service: name: bar-service port: number: 8080 --- kind: Pod apiVersion: v1 metadata: name: curl-pod spec: containers: - name: curl image: registry.k8s.io/e2e-test-images/agnhost:2.63.0 ================================================ FILE: examples/loadbalancer_affinity.yaml ================================================ apiVersion: apps/v1 kind: Deployment metadata: name: affinity spec: selector: matchLabels: app: affinity replicas: 2 strategy: rollingUpdate: maxSurge: 0 maxUnavailable: 1 type: RollingUpdate template: metadata: labels: app: affinity spec: terminationGracePeriodSeconds: 30 affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - affinity topologyKey: kubernetes.io/hostname containers: - name: affinity image: registry.k8s.io/e2e-test-images/agnhost:2.63.0 args: - netexec - --http-port=80 - --delay-shutdown=30 ports: - name: httpd containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: affinity spec: type: LoadBalancer sessionAffinity: ClientIP selector: app: affinity ports: - name: httpd port: 80 targetPort: 80 ================================================ FILE: examples/loadbalancer_deployment.yaml ================================================ apiVersion: apps/v1 kind: Deployment metadata: name: zeroapp spec: selector: matchLabels: app: zeroapp replicas: 2 strategy: rollingUpdate: maxSurge: 0 maxUnavailable: 1 type: RollingUpdate template: metadata: labels: app: zeroapp spec: terminationGracePeriodSeconds: 30 affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - zeroapp topologyKey: kubernetes.io/hostname containers: - name: zeroapp image: registry.k8s.io/e2e-test-images/agnhost:2.63.0 args: - netexec - --http-port=80 - --delay-shutdown=30 ports: - name: httpd containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: zeroapp spec: type: LoadBalancer externalTrafficPolicy: Local selector: app: zeroapp ports: - name: httpd port: 80 targetPort: 80 ================================================ FILE: examples/loadbalancer_etp_cluster.yaml ================================================ apiVersion: apps/v1 kind: Deployment metadata: name: policy-cluster labels: app: MyClusterApp spec: replicas: 1 selector: matchLabels: app: MyClusterApp template: metadata: labels: app: MyClusterApp spec: containers: - name: agnhost image: registry.k8s.io/e2e-test-images/agnhost:2.63.0 args: - netexec - --http-port=8080 - --udp-port=8080 ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: lb-service-cluster spec: type: LoadBalancer externalTrafficPolicy: Cluster selector: app: MyClusterApp ports: - protocol: TCP port: 80 targetPort: 8080 ================================================ FILE: examples/loadbalancer_etp_local.yaml ================================================ apiVersion: apps/v1 kind: Deployment metadata: name: policy-local labels: app: MyLocalApp spec: replicas: 1 selector: matchLabels: app: MyLocalApp template: metadata: labels: app: MyLocalApp spec: containers: - name: agnhost image: registry.k8s.io/e2e-test-images/agnhost:2.63.0 args: - netexec - --http-port=8080 - --udp-port=8080 ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: lb-service-local spec: type: LoadBalancer externalTrafficPolicy: Local selector: app: MyLocalApp ports: - protocol: TCP port: 80 targetPort: 8080 ================================================ FILE: examples/loadbalancer_multiport.yaml ================================================ apiVersion: apps/v1 kind: Deployment metadata: name: multiport spec: selector: matchLabels: app: multiport replicas: 2 strategy: rollingUpdate: maxSurge: 0 maxUnavailable: 1 type: RollingUpdate template: metadata: labels: app: multiport spec: terminationGracePeriodSeconds: 30 affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - multiport topologyKey: kubernetes.io/hostname containers: - name: multiport image: registry.k8s.io/e2e-test-images/agnhost:2.63.0 args: - porter env: - name: SERVE_PORT_80 value: "80" - name: SERVE_TLS_PORT_443 value: "443" ports: - name: http containerPort: 80 - name: https containerPort: 443 --- apiVersion: v1 kind: Service metadata: name: multiport spec: type: LoadBalancer externalTrafficPolicy: Local selector: app: multiport ports: - name: http port: 80 targetPort: 80 - name: https port: 443 targetPort: 443 ================================================ FILE: examples/loadbalancer_static_ip.yaml ================================================ apiVersion: apps/v1 kind: Deployment metadata: name: static-ip spec: selector: matchLabels: app: static-ip replicas: 1 strategy: rollingUpdate: maxSurge: 0 maxUnavailable: 1 type: RollingUpdate template: metadata: labels: app: static-ip spec: terminationGracePeriodSeconds: 30 affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - static-ip topologyKey: kubernetes.io/hostname containers: - name: static-ip image: registry.k8s.io/e2e-test-images/agnhost:2.63.0 args: - netexec - --http-port=80 ports: - name: tcp containerPort: 80 protocol: TCP --- apiVersion: v1 kind: Service metadata: name: static-ip spec: type: LoadBalancer loadBalancerIP: REPLACE_WITH_STATIC_IP selector: app: static-ip ports: - name: tcp port: 80 targetPort: 80 protocol: TCP ================================================ FILE: examples/loadbalancer_stts.yaml ================================================ apiVersion: apps/v1 kind: StatefulSet metadata: name: apache spec: selector: matchLabels: app: apache serviceName: apache replicas: 2 template: metadata: labels: app: apache spec: terminationGracePeriodSeconds: 30 affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - nginx topologyKey: kubernetes.io/hostname containers: - name: apache image: registry.k8s.io/e2e-test-images/agnhost:2.63.0 args: - netexec - --http-port=80 - --delay-shutdown=30 ports: - name: httpd containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: apache spec: type: LoadBalancer externalTrafficPolicy: Local selector: app: apache ports: - name: httpd port: 80 targetPort: 80 ================================================ FILE: examples/loadbalancer_udp_tcp.yaml ================================================ apiVersion: apps/v1 kind: Deployment metadata: name: multiprotocol spec: selector: matchLabels: app: multiprotocol replicas: 1 strategy: rollingUpdate: maxSurge: 0 maxUnavailable: 1 type: RollingUpdate template: metadata: labels: app: multiprotocol spec: terminationGracePeriodSeconds: 30 affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - multiprotocol topologyKey: kubernetes.io/hostname containers: - name: multiprotocol image: registry.k8s.io/e2e-test-images/agnhost:2.63.0 args: - netexec - --http-port=80 - --udp-port=80 ports: - name: tcp containerPort: 80 protocol: TCP - name: udp containerPort: 80 protocol: UDP --- apiVersion: v1 kind: Service metadata: name: multiprotocol spec: type: LoadBalancer externalTrafficPolicy: Local selector: app: multiprotocol ports: - name: tcp port: 80 targetPort: 80 protocol: TCP - name: udp port: 80 targetPort: 80 protocol: UDP ================================================ FILE: go.mod ================================================ module sigs.k8s.io/cloud-provider-kind go 1.25.5 require ( github.com/envoyproxy/go-control-plane v0.14.0 github.com/envoyproxy/go-control-plane/envoy v1.37.0 github.com/google/go-cmp v0.7.0 github.com/lithammer/dedent v1.1.0 github.com/pkg/errors v0.9.1 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/vishvananda/netlink v1.3.1 google.golang.org/grpc v1.80.0 google.golang.org/protobuf v1.36.11 k8s.io/api v0.35.3 k8s.io/apimachinery v0.35.3 k8s.io/apiserver v0.35.3 k8s.io/client-go v0.35.3 k8s.io/cloud-provider v0.35.3 k8s.io/component-base v0.35.3 k8s.io/controller-manager v0.35.3 k8s.io/klog/v2 v2.140.0 k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 sigs.k8s.io/gateway-api v1.5.1 sigs.k8s.io/kind v0.31.0 ) require ( al.essio.dev/pkg/shellescape v1.6.0 // indirect cel.dev/expr v0.25.1 // indirect github.com/BurntSushi/toml v1.6.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 // indirect github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-openapi/jsonpointer v0.22.4 // indirect github.com/go-openapi/jsonreference v0.21.4 // indirect github.com/go-openapi/swag v0.25.4 // indirect github.com/go-openapi/swag/cmdutils v0.25.4 // indirect github.com/go-openapi/swag/conv v0.25.4 // indirect github.com/go-openapi/swag/fileutils v0.25.4 // indirect github.com/go-openapi/swag/jsonname v0.25.4 // indirect github.com/go-openapi/swag/jsonutils v0.25.4 // indirect github.com/go-openapi/swag/loading v0.25.4 // indirect github.com/go-openapi/swag/mangling v0.25.4 // indirect github.com/go-openapi/swag/netutils v0.25.4 // indirect github.com/go-openapi/swag/stringutils v0.25.4 // indirect github.com/go-openapi/swag/typeutils v0.25.4 // indirect github.com/go-openapi/swag/yamlutils v0.25.4 // indirect github.com/google/gnostic-models v0.7.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect github.com/prometheus/procfs v0.19.2 // indirect github.com/vishvananda/netns v0.0.5 // indirect github.com/x448/float16 v0.8.4 // indirect go.opentelemetry.io/otel v1.40.0 // indirect go.opentelemetry.io/otel/trace v1.40.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect golang.org/x/net v0.50.0 // indirect golang.org/x/oauth2 v0.35.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/term v0.40.0 // indirect golang.org/x/text v0.34.0 // indirect golang.org/x/time v0.14.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect k8s.io/component-helpers v0.35.3 // indirect k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect sigs.k8s.io/yaml v1.6.0 // indirect ) ================================================ FILE: go.sum ================================================ al.essio.dev/pkg/shellescape v1.6.0 h1:NxFcEqzFSEVCGN2yq7Huv/9hyCEGVa/TncnOOBBeXHA= al.essio.dev/pkg/shellescape v1.6.0/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik= github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ= github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4= github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8= github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4= github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU= github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ= github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4= github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y= github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk= github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo= github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM= github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48= github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg= github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0= github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg= github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4= github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg= github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/onsi/ginkgo/v2 v2.28.0 h1:Rrf+lVLmtlBIKv6KrIGJCjyY8N36vDVcutbGJkyqjJc= github.com/onsi/ginkgo/v2 v2.28.0/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28= github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0= github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY= github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ= golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0= google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.35.3 h1:pA2fiBc6+N9PDf7SAiluKGEBuScsTzd2uYBkA5RzNWQ= k8s.io/api v0.35.3/go.mod h1:9Y9tkBcFwKNq2sxwZTQh1Njh9qHl81D0As56tu42GA4= k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8= k8s.io/apimachinery v0.35.3/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= k8s.io/apiserver v0.35.3 h1:D2eIcfJ05hEAEewoSDg+05e0aSRwx8Y4Agvd/wiomUI= k8s.io/apiserver v0.35.3/go.mod h1:JI0n9bHYzSgIxgIrfe21dbduJ9NHzKJ6RchcsmIKWKY= k8s.io/client-go v0.35.3 h1:s1lZbpN4uI6IxeTM2cpdtrwHcSOBML1ODNTCCfsP1pg= k8s.io/client-go v0.35.3/go.mod h1:RzoXkc0mzpWIDvBrRnD+VlfXP+lRzqQjCmKtiwZ8Q9c= k8s.io/cloud-provider v0.35.3 h1:8gmlIXd9LHhDOXfv0fqsM9KIgVbgl3fwtNI2lDL0qWE= k8s.io/cloud-provider v0.35.3/go.mod h1:bHkYmoCLBoyDkx8r8usg0sOo6PIzYrM5qYuDxCyuUxw= k8s.io/component-base v0.35.3 h1:mbKbzoIMy7JDWS/wqZobYW1JDVRn/RKRaoMQHP9c4P0= k8s.io/component-base v0.35.3/go.mod h1:IZ8LEG30kPN4Et5NeC7vjNv5aU73ku5MS15iZyvyMYk= k8s.io/component-helpers v0.35.3 h1:Rl2p3wNMC0YU21rziLkWXavr7MwkB5Td3lNZ/+gYGm8= k8s.io/component-helpers v0.35.3/go.mod h1:8BkyfcBA6XsCtFYxDB+mCfZqM6P39Aco12AKigNn0C8= k8s.io/controller-manager v0.35.3 h1:BlX95jtN41/vCwuTsmfzR9UpqweX7KDWdwm/mRHez/o= k8s.io/controller-manager v0.35.3/go.mod h1:OaG4bXsMfN5zpqowtdyfoRX20LrfwUh6V0zmpF7hw30= k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 h1:HhDfevmPS+OalTjQRKbTHppRIz01AWi8s45TMXStgYY= k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU= k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= sigs.k8s.io/gateway-api v1.5.1 h1:RqVRIlkhLhUO8wOHKTLnTJA6o/1un4po4/6M1nRzdd0= sigs.k8s.io/gateway-api v1.5.1/go.mod h1:GvCETiaMAlLym5CovLxGjS0NysqFk3+Yuq3/rh6QL2o= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/kind v0.31.0 h1:UcT4nzm+YM7YEbqiAKECk+b6dsvc/HRZZu9U0FolL1g= sigs.k8s.io/kind v0.31.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8= sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= ================================================ FILE: hack/build-route-adder.sh ================================================ #!/bin/bash set -o errexit set -o nounset set -o pipefail SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) REPO_ROOT=$( cd -- "$SCRIPT_DIR/.." &> /dev/null && pwd ) cd "$REPO_ROOT" export CGO_ENABLED=0 # Compile for amd64 (x64) GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o ./pkg/gateway/routeadder/route-adder-amd64 ./internal/routeadder/ # Compile for arm64 GOOS=linux GOARCH=arm64 go build -ldflags="-s -w" -o ./pkg/gateway/routeadder/route-adder-arm64 ./internal/routeadder/ # Cleanup is handled by the trap exit 0 ================================================ FILE: hack/ci/add-kubernetes-to-workspace.sh ================================================ #!/usr/bin/env bash # Copyright 2024 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -e set -x REPO_ROOT=$(git rev-parse --show-toplevel) KUBERNETES_ROOT=${KUBERNETES_ROOT:-$GOPATH/src/k8s.io/kubernetes} cd ${REPO_ROOT} # Set up go workspace to build with this version go work init go work use . # Add kubernetes to workspace go work use ${KUBERNETES_ROOT} for d in ${KUBERNETES_ROOT}/staging/src/k8s.io/*; do go work use $d done # Workaround for go.mod replacements sed -i 's/^\s*k8s.io.*//g' go.mod go work sync ================================================ FILE: hack/ci/e2e.sh ================================================ #!/bin/sh # Copyright 2018 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # hack script for running a kind e2e # must be run with a kubernetes checkout in $PWD (IE from the checkout) # Usage: SKIP="ginkgo skip regex" FOCUS="ginkgo focus regex" kind-e2e.sh set -o errexit -o nounset -o xtrace # Settings: # SKIP: ginkgo skip regex # FOCUS: ginkgo focus regex # cleanup logic for cleanup on exit CLEANED_UP=false cleanup() { if [ "$CLEANED_UP" = "true" ]; then return fi # KIND_CREATE_ATTEMPTED is true once we: kind create if [ "${KIND_CREATE_ATTEMPTED:-}" = true ]; then kind "export" logs "${ARTIFACTS}" || true kind delete cluster || true fi rm -f _output/bin/e2e.test || true # remove our tempdir, this needs to be last, or it will prevent kind delete if [ -n "${TMP_DIR:-}" ]; then rm -rf "${TMP_DIR:?}" fi CLEANED_UP=true } # setup signal handlers # shellcheck disable=SC2317 # this is not unreachable code signal_handler() { if [ -n "${GINKGO_PID:-}" ]; then kill -TERM "$GINKGO_PID" || true fi cleanup } trap signal_handler INT TERM # build kubernetes / node image, e2e binaries build() { # build the node image w/ kubernetes kind build node-image -v 1 # Ginkgo v1 is used by Kubernetes 1.24 and earlier, fallback if v2 is not available. GINKGO_SRC_DIR="vendor/github.com/onsi/ginkgo/v2/ginkgo" if [ ! -d "$GINKGO_SRC_DIR" ]; then GINKGO_SRC_DIR="vendor/github.com/onsi/ginkgo/ginkgo" fi # make sure we have e2e requirements make all WHAT="cmd/kubectl test/e2e/e2e.test ${GINKGO_SRC_DIR}" } # up a cluster with kind create_cluster() { # Grab the version of the cluster we're about to start KUBE_VERSION="$(docker run --rm --entrypoint=cat "kindest/node:latest" /kind/version)" # Default Log level for all components in test clusters KIND_CLUSTER_LOG_LEVEL=${KIND_CLUSTER_LOG_LEVEL:-4} # JSON or YAML map injected into featureGates config feature_gates="${FEATURE_GATES:-{\}}" # --runtime-config argument value passed to the API server, again as a map runtime_config="${RUNTIME_CONFIG:-{\}}" # create the config file cat < "${ARTIFACTS}/kind-config.yaml" # config for 1 control plane node and 2 workers (necessary for conformance) kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 networking: ipFamily: ${IP_FAMILY:-ipv4} kubeProxyMode: ${KUBE_PROXY_MODE:-iptables} # don't pass through host search paths # TODO: possibly a reasonable default in the future for kind ... dnsSearch: [] nodes: - role: control-plane - role: worker - role: worker featureGates: ${feature_gates} runtimeConfig: ${runtime_config} kubeadmConfigPatches: - | kind: ClusterConfiguration metadata: name: config apiServer: extraArgs: v: "${KIND_CLUSTER_LOG_LEVEL}" controllerManager: extraArgs: cloud-provider: "external" v: "${KIND_CLUSTER_LOG_LEVEL}" --- kind: InitConfiguration nodeRegistration: kubeletExtraArgs: cloud-provider: "external" v: "${KIND_CLUSTER_LOG_LEVEL}" --- kind: JoinConfiguration nodeRegistration: kubeletExtraArgs: cloud-provider: "external" v: "${KIND_CLUSTER_LOG_LEVEL}" EOF # NOTE: must match the number of workers above NUM_NODES=2 # actually create the cluster # TODO(BenTheElder): settle on verbosity for this script KIND_CREATE_ATTEMPTED=true kind create cluster \ --image=kindest/node:latest \ --retain \ --wait=1m \ -v=3 \ "--config=${ARTIFACTS}/kind-config.yaml" # debug cluster version kubectl version # Patch kube-proxy to set the verbosity level kubectl patch -n kube-system daemonset/kube-proxy \ --type='json' -p='[{"op": "add", "path": "/spec/template/spec/containers/0/command/-", "value": "--v='"${KIND_CLUSTER_LOG_LEVEL}"'" }]' } # run e2es with ginkgo-e2e.sh run_tests() { # IPv6 clusters need some CoreDNS changes in order to work in k8s CI: # 1. k8s CI doesn´t offer IPv6 connectivity, so CoreDNS should be configured # to work in an offline environment: # https://github.com/coredns/coredns/issues/2494#issuecomment-457215452 # 2. k8s CI adds following domains to resolv.conf search field: # c.k8s-prow-builds.internal google.internal. # CoreDNS should handle those domains and answer with NXDOMAIN instead of SERVFAIL # otherwise pods stops trying to resolve the domain. if [ "${IP_FAMILY:-ipv4}" = "ipv6" ]; then # Get the current config original_coredns=$(kubectl get -oyaml -n=kube-system configmap/coredns) echo "Original CoreDNS config:" echo "${original_coredns}" # Patch it fixed_coredns=$( printf '%s' "${original_coredns}" | sed \ -e 's/^.*kubernetes cluster\.local/& internal/' \ -e '/^.*upstream$/d' \ -e '/^.*fallthrough.*$/d' \ -e '/^.*forward . \/etc\/resolv.conf$/d' \ -e '/^.*loop$/d' \ ) echo "Patched CoreDNS config:" echo "${fixed_coredns}" printf '%s' "${fixed_coredns}" | kubectl apply -f - fi # ginkgo regexes SKIP="${SKIP:-"Feature|Federation|PerformanceDNS|DualStack|Disruptive|Serial|KubeProxy|GCE|Netpol|NetworkPolicy|256.search.list.characters|LoadBalancer.Service.without.NodePort|type.and.ports.of.a.TCP.service|loadbalancer.source.ranges"}" FOCUS="${FOCUS:-"\\[sig-network\\]"}" # if we set PARALLEL=true, skip serial tests set --ginkgo-parallel if [ "${PARALLEL:-false}" = "true" ]; then export GINKGO_PARALLEL=y if [ -z "${SKIP}" ]; then SKIP="\\[Serial\\]" else SKIP="\\[Serial\\]|${SKIP}" fi fi # setting this env prevents ginkgo e2e from trying to run provider setup export KUBERNETES_CONFORMANCE_TEST='y' # setting these is required to make RuntimeClass tests work ... :/ export KUBE_CONTAINER_RUNTIME=remote export KUBE_CONTAINER_RUNTIME_ENDPOINT=unix:///run/containerd/containerd.sock export KUBE_CONTAINER_RUNTIME_NAME=containerd # ginkgo can take forever to exit, so we run it in the background and save the # PID, bash will not run traps while waiting on a process, but it will while # running a builtin like `wait`, saving the PID also allows us to forward the # interrupt # use aws provider to enable cloud-provider tests, aws is just a nullprovider # without any custom logic ./hack/ginkgo-e2e.sh \ "--num-nodes=${NUM_NODES}" \ "--ginkgo.focus=${FOCUS}" "--ginkgo.skip=${SKIP}" \ "--report-dir=${ARTIFACTS}" '--disable-log-dump=true' & GINKGO_PID=$! wait "$GINKGO_PID" } main() { # create temp dir and setup cleanup TMP_DIR=$(mktemp -d) # ensure artifacts (results) directory exists when not in CI export ARTIFACTS="${ARTIFACTS:-${PWD}/_artifacts}" mkdir -p "${ARTIFACTS}" # export the KUBECONFIG to a unique path for testing KUBECONFIG="${HOME}/.kube/kind-test-config" export KUBECONFIG echo "exported KUBECONFIG=${KUBECONFIG}" # debug kind version kind version # build cloud-provider-kind make nohup bin/cloud-provider-kind --enable-log-dumping --logs-dir ${ARTIFACTS}/loadbalancers > ${ARTIFACTS}/ccm-kind.log 2>&1 & # build kubernetes K8S_PATH=$(find ${GOPATH} -path '*/k8s.io/kubernetes/go.mod' -print -quit) if [ -z "${K8S_PATH}" ]; then K8S_PATH=$(find / -path '*/kubernetes/go.mod' -print -quit) fi cd $(dirname ${K8S_PATH}) build # in CI attempt to release some memory after building if [ -n "${KUBETEST_IN_DOCKER:-}" ]; then sync || true echo 1 > /proc/sys/vm/drop_caches || true fi # create the cluster and run tests res=0 create_cluster || res=$? run_tests || res=$? cleanup || res=$? exit $res } main ================================================ FILE: hack/download-gateway-crds.sh ================================================ #!/bin/bash set -o errexit set -o nounset set -o pipefail SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) REPO_ROOT=$( cd -- "$SCRIPT_DIR/.." &> /dev/null && pwd ) repo_url="https://github.com/kubernetes-sigs/gateway-api" # Define the desired git version (tag or branch). Default to 'main'. # Can be overridden by setting the GIT_VERSION environment variable. GIT_VERSION="${GIT_VERSION:-main}" # Define the path within the repo where CRDs are located # Using 'standard' as it's common, adjust if needed for different versions crd_repo_subdir="config/crd" # Define the output directory relative to the REPO_ROOT output_dir_rel="pkg/gateway/crds" output_dir_abs="$REPO_ROOT/$output_dir_rel" # --- Temporary Directory and Cleanup --- TEMP_CLONE_DIR="" # Will hold the path to the temporary directory cleanup() { local exit_code=$? # Capture exit code echo "Cleaning up..." if [ -n "$TEMP_CLONE_DIR" ] && [ -d "$TEMP_CLONE_DIR" ]; then echo "Removing temporary clone directory $TEMP_CLONE_DIR..." rm -rf "$TEMP_CLONE_DIR" else echo "No temporary directory to remove." fi # Ensure we exit with the original script's exit code exit $exit_code } # Set trap for cleanup on EXIT, ERR, SIGINT, SIGTERM trap cleanup EXIT ERR SIGINT SIGTERM # Create a temporary directory for the clone TEMP_CLONE_DIR=$(mktemp -d -t gateway-api-clone-XXXXXX) # Define the actual path where the repo will be cloned inside the temp dir repo_local_path="$TEMP_CLONE_DIR/gateway-api" echo "Cloning $repo_url (version: $GIT_VERSION) into temporary directory $repo_local_path..." # Use --depth 1 for a shallow clone. # --branch works for tags and branches. # Use --no-tags to potentially speed up if tags aren't needed beyond the specific version tag. # Add --filter=blob:none if supported by git version and remote to further reduce clone size. git clone --branch "$GIT_VERSION" --depth 1 "$repo_url" "$repo_local_path" if [ $? -ne 0 ]; then echo "Error: Failed to clone repository for version '$GIT_VERSION'." # Cleanup function will handle removing TEMP_CLONE_DIR via trap exit 1 fi echo "Successfully cloned version '$GIT_VERSION'." # Create the output directory if it doesn't exist mkdir -p "$output_dir_abs" # Define the full path to the CRD source directory within the clone crd_source_dir="$repo_local_path/$crd_repo_subdir" # Check if the source directory exists if [ ! -d "$crd_source_dir" ]; then echo "Error: CRD source directory not found: $crd_source_dir (for version: $GIT_VERSION)" exit 1 fi echo "Copying files from: $crd_source_dir (Version: $GIT_VERSION)" cp -rf "$crd_source_dir"/* "$output_dir_abs" echo "Successfully copied files to the '$output_dir_abs' directory from version $GIT_VERSION." # Cleanup is handled by the trap exit 0 ================================================ FILE: hack/init-buildx.sh ================================================ #!/usr/bin/env bash # Copyright 2020 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -o errexit -o nounset -o pipefail # We can skip setup if the current builder already has multi-arch # AND if it isn't the docker driver, which doesn't work current_builder="$(docker buildx inspect)" # linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6 if ! grep -q "^Driver: docker$" <<<"${current_builder}" && \ grep -q "linux/amd64" <<<"${current_builder}" && \ grep -q "linux/arm64" <<<"${current_builder}"; then exit 0 fi # Ensure qemu is in binfmt_misc # Docker desktop already has these in versions recent enough to have buildx # We only need to do this setup on linux hosts if [ "$(uname)" == 'Linux' ]; then # NOTE: this is pinned to a digest for a reason! docker run --rm --privileged tonistiigi/binfmt:qemu-v7.0.0-28@sha256:66e11bea77a5ea9d6f0fe79b57cd2b189b5d15b93a2bdb925be22949232e4e55 --install all fi # Ensure we use a builder that can leverage it (the default on linux will not) docker buildx rm knp-builder || true docker buildx create --use --name=knp-builder ================================================ FILE: hack/lint.sh ================================================ #!/bin/bash set -o errexit set -o nounset set -o pipefail REPO_ROOT=$(dirname "${BASH_SOURCE[0]}")/.. cd $REPO_ROOT docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint:v2.7.2 golangci-lint run -v ================================================ FILE: internal/routeadder/main.go ================================================ // main.go package main import ( "fmt" "net" "os" "github.com/vishvananda/netlink" ) // usage: ./route-adder func main() { if len(os.Args) != 3 { fmt.Fprintf(os.Stderr, "Usage: %s \n", os.Args[0]) os.Exit(1) } cidr := os.Args[1] gatewayIP := os.Args[2] // Parse the destination CIDR. This gives us the destination network. _, dstNet, err := net.ParseCIDR(cidr) if err != nil { fmt.Fprintf(os.Stderr, "Error parsing destination CIDR %s: %v\n", cidr, err) os.Exit(1) } // Parse the gateway IP address. gw := net.ParseIP(gatewayIP) if gw == nil { fmt.Fprintf(os.Stderr, "Error parsing gateway IP %s\n", gatewayIP) os.Exit(1) } // Create the route object route := &netlink.Route{ Dst: dstNet, Gw: gw, } // Use RouteReplace, which is equivalent to `ip route replace`. // It will add the route if it doesn't exist or update it if it does. if err := netlink.RouteReplace(route); err != nil { fmt.Fprintf(os.Stderr, "Error replacing route: %v\n", err) os.Exit(1) } fmt.Printf("Successfully replaced route: %s via %s\n", cidr, gatewayIP) } ================================================ FILE: kind.yaml ================================================ kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 kubeadmConfigPatches: - | kind: ClusterConfiguration apiServer: extraArgs: v: "5" controllerManager: extraArgs: cloud-provider: "external" v: "5" --- kind: InitConfiguration nodeRegistration: kubeletExtraArgs: cloud-provider: "external" --- kind: JoinConfiguration nodeRegistration: kubeletExtraArgs: cloud-provider: "external" nodes: - role: control-plane - role: worker - role: worker ================================================ FILE: main.go ================================================ package main import ( "sigs.k8s.io/cloud-provider-kind/cmd" ) func main() { cmd.Main() } ================================================ FILE: pkg/config/config.go ================================================ package config import ( "os" "sigs.k8s.io/cloud-provider-kind/pkg/constants" ) // DefaultConfig is a global variable that is initialized at startup. // Note: Its fields are modified by command-line flags in cmd/app.go. var DefaultConfig = newDefaultConfig() func newDefaultConfig() *Config { c := &Config{ GatewayReleaseChannel: Standard, IngressDefault: true, ProxyImage: constants.DefaultProxyImageRegistry + "/" + constants.DefaultProxyImageName, } if registry := os.Getenv("CLOUD_PROVIDER_KIND_REGISTRY_URL"); registry != "" { c.ProxyImage = registry + "/" + constants.DefaultProxyImageName } return c } type Config struct { EnableLogDump bool LogDir string // Platforms like Mac or Windows can not access the containers directly // so we do a double hop, enable container portmapping for the LoadBalancer containter // and do userspace proxying from the original port to the portmaps. // If the cloud-provider-kind runs in a container on these platforms only enables portmapping. LoadBalancerConnectivity Connectivity // Type of connectivity between the cloud-provider-kind and the clusters ControlPlaneConnectivity Connectivity // Gateway API Release channel (default stable) // https://gateway-api.sigs.k8s.io/concepts/versioning/ GatewayReleaseChannel GatewayReleaseChannel IngressDefault bool ProxyImage string } type Connectivity int const ( Unknown Connectivity = iota Direct Portmap Tunnel ) type GatewayReleaseChannel string const ( Standard GatewayReleaseChannel = "standard" Experimental GatewayReleaseChannel = "experimental" Disabled GatewayReleaseChannel = "disabled" ) ================================================ FILE: pkg/constants/constants.go ================================================ package constants const ( ProviderName = "kind" // cloud-provider-kind ContainerPrefix = "kindccm" // DefaultProxyImageRegistry is the default registry for the Envoy proxy image DefaultProxyImageRegistry = "docker.io" // DefaultProxyImageName is the default Envoy proxy image DefaultProxyImageName = "envoyproxy/envoy:v1.33.2" // KIND constants FixedNetworkName = "kind" // NodeCCMLabelKey NodeCCMLabelKey = "io.x-k8s.cloud-provider-kind.cluster" // LoadBalancerNameLabelKey clustername/serviceNamespace/serviceName LoadBalancerNameLabelKey = "io.x-k8s.cloud-provider-kind.loadbalancer.name" // GatewayNameLabelKey clustername/gatewayNamespace/gatewayName GatewayNameLabelKey = "io.x-k8s.cloud-provider-kind.gateway.name" ) ================================================ FILE: pkg/container/container.go ================================================ package container import ( "encoding/json" "fmt" "io" "os" "os/exec" "strings" "k8s.io/klog/v2" kindexec "sigs.k8s.io/kind/pkg/exec" ) // TODO we can do it as in KIND var containerRuntime = "docker" // dockerIsAvailable checks if docker is available in the system func dockerIsAvailable() bool { cmd := kindexec.Command("docker", "-v") lines, err := kindexec.OutputLines(cmd) if err != nil || len(lines) != 1 { return false } return strings.HasPrefix(lines[0], "Docker version") } func podmanIsAvailable() bool { cmd := kindexec.Command("podman", "-v") lines, err := kindexec.OutputLines(cmd) if err != nil || len(lines) != 1 { return false } return strings.HasPrefix(lines[0], "podman version") } func nerdctlIsAvailable() bool { cmd := kindexec.Command("nerdctl", "-v") lines, err := kindexec.OutputLines(cmd) if err != nil || len(lines) != 1 { // check finch cmd = kindexec.Command("finch", "-v") lines, err = kindexec.OutputLines(cmd) if err != nil || len(lines) != 1 { return false } return strings.HasPrefix(lines[0], "finch version") } return strings.HasPrefix(lines[0], "nerdctl version") } // Runtime returns the detected container runtime name. func Runtime() string { return containerRuntime } func init() { // allow to override the container provider as we do in KIND if p := os.Getenv("KIND_EXPERIMENTAL_PROVIDER"); p != "" { containerRuntime = p return } if dockerIsAvailable() { return } if podmanIsAvailable() { containerRuntime = "podman" return } if nerdctlIsAvailable() { containerRuntime = "nerdctl" if _, err := exec.LookPath("nerdctl"); err != nil { if _, err := exec.LookPath("finch"); err == nil { containerRuntime = "finch" } } } } func Logs(name string, w io.Writer) error { cmd := exec.Command(containerRuntime, []string{"logs", name}...) cmd.Stderr = w cmd.Stdout = w err := cmd.Run() if err != nil { return fmt.Errorf("failed to get container logs: %w", err) } return nil } func LogDump(containerName string, fileName string) error { f, err := os.Create(fileName) if err != nil { return err } defer f.Close() err = Logs(containerName, f) if err != nil { return err } return nil } func Create(name string, args []string) error { if err := exec.Command(containerRuntime, append([]string{"run", "--name", name}, args...)...).Run(); err != nil { return err } return nil } func Restart(name string) error { if err := exec.Command(containerRuntime, []string{"restart", name}...).Run(); err != nil { return err } return nil } func Delete(name string) error { if err := exec.Command(containerRuntime, []string{"rm", "-f", name}...).Run(); err != nil { return err } return nil } func IsRunning(name string) bool { cmd := exec.Command(containerRuntime, []string{"ps", "-q", "-f", "name=" + name}...) output, err := cmd.Output() if err != nil || len(output) == 0 { return false } return true } func Exist(name string) bool { err := exec.Command(containerRuntime, []string{"inspect", name}...).Run() return err == nil } func Signal(name string, signal string) error { err := exec.Command(containerRuntime, []string{"kill", "-s", signal, name}...).Run() return err } func Exec(name string, command []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { args := []string{"exec", "--privileged"} if stdin != nil { args = append(args, "-i") } args = append(args, name) args = append(args, command...) cmd := exec.Command(containerRuntime, args...) if stdin != nil { cmd.Stdin = stdin } if stdout != nil { cmd.Stdout = stdout } if stderr != nil { cmd.Stderr = stderr } return cmd.Run() } func IPs(name string) (ipv4 string, ipv6 string, err error) { // retrieve the IP address of the node using docker inspect cmd := kindexec.Command(containerRuntime, "inspect", "-f", "{{range .NetworkSettings.Networks}}{{.IPAddress}},{{.GlobalIPv6Address}}{{end}}", name, // ... against the "node" container ) lines, err := kindexec.OutputLines(cmd) if err != nil { return "", "", fmt.Errorf("failed to get container details: %w", err) } if len(lines) != 1 { return "", "", fmt.Errorf("file should only be one line, got %d lines: %w", len(lines), err) } ips := strings.Split(lines[0], ",") if len(ips) != 2 { return "", "", fmt.Errorf("container addresses should have 2 values, got %d values", len(ips)) } return ips[0], ips[1], nil } // return a list with the map of the internal port to the external port func PortMaps(name string) (map[string]string, error) { // retrieve the IP address of the node using docker inspect cmd := kindexec.Command(containerRuntime, "inspect", "-f", "{{ json .NetworkSettings.Ports }}", name, // ... against the "node" container ) lines, err := kindexec.OutputLines(cmd) if err != nil { return nil, fmt.Errorf("failed to get container details: %w", err) } if len(lines) != 1 { return nil, fmt.Errorf("file should only be one line, got %d lines: %w", len(lines), err) } type portMapping struct { HostPort string `json:"HostPort"` HostIP string `json:"HostIp"` } portMappings := make(map[string][]portMapping) err = json.Unmarshal([]byte(lines[0]), &portMappings) if err != nil { return nil, err } result := map[string]string{} for k, v := range portMappings { protocol := "tcp" parts := strings.Split(k, "/") if len(parts) == 2 { protocol = strings.ToLower(parts[1]) } if protocol != "tcp" && protocol != "udp" { klog.Infof("skipping protocol %s not supported, only UDP and TCP", protocol) continue } // TODO we just can get the first entry or look for ip families for _, pm := range v { if pm.HostPort != "" { result[parts[0]+"/"+protocol] = pm.HostPort break } } } return result, nil } func ListByLabel(label string) ([]string, error) { cmd := kindexec.Command(containerRuntime, "ps", "-a", // show stopped nodes // filter for nodes with the cluster label "--filter", "label="+label, // format to include the cluster name "--format", `{{.ID }}`, ) lines, err := kindexec.OutputLines(cmd) return lines, err } // GetLabelValue return the value of the associated label // It returns an error if the label value does not exist func GetLabelValue(name string, label string) (string, error) { cmd := kindexec.Command(containerRuntime, "inspect", "--format", fmt.Sprintf(`{{ index .Config.Labels "%s"}}`, label), name, ) lines, err := kindexec.OutputLines(cmd) if err != nil { return "", err } if len(lines) != 1 { return "", fmt.Errorf("expected 1 line, got %d", len(lines)) } return lines[0], nil } ================================================ FILE: pkg/controller/controller.go ================================================ package controller import ( "context" "fmt" "net/http" "sync" "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/wait" utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" cloudprovider "k8s.io/cloud-provider" cloudproviderapi "k8s.io/cloud-provider/api" nodecontroller "k8s.io/cloud-provider/controllers/node" servicecontroller "k8s.io/cloud-provider/controllers/service" controllersmetrics "k8s.io/component-base/metrics/prometheus/controllers" ccmfeatures "k8s.io/controller-manager/pkg/features" "k8s.io/klog/v2" gatewayclient "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned" gatewayinformers "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions" cpkconfig "sigs.k8s.io/cloud-provider-kind/pkg/config" "sigs.k8s.io/cloud-provider-kind/pkg/constants" "sigs.k8s.io/cloud-provider-kind/pkg/container" "sigs.k8s.io/cloud-provider-kind/pkg/gateway" "sigs.k8s.io/cloud-provider-kind/pkg/ingress" "sigs.k8s.io/cloud-provider-kind/pkg/loadbalancer" "sigs.k8s.io/cloud-provider-kind/pkg/provider" "sigs.k8s.io/kind/pkg/cluster" ) var once sync.Once type Controller struct { kind *cluster.Provider clusters map[string]*ccm } type ccm struct { factory informers.SharedInformerFactory serviceController *servicecontroller.Controller nodeController *nodecontroller.CloudNodeController gatewayController *gateway.Controller cancelFn context.CancelFunc } func New(provider *cluster.Provider) *Controller { controllersmetrics.Register() return &Controller{ kind: provider, clusters: make(map[string]*ccm), } } func (c *Controller) Run(ctx context.Context) { logger := klog.FromContext(ctx) defer c.cleanup() for { // get existing kind clusters clusters, err := c.kind.List() if err != nil { logger.Error(err, "Error listing clusters, retrying...") } // add new ones for _, cluster := range clusters { select { case <-ctx.Done(): return default: } logger := logger.WithValues("cluster", cluster) logger.V(3).Info("Processing cluster") _, ok := c.clusters[cluster] if ok { logger.V(3).Info("Cluster already exist") continue } restConfig, err := c.getRestConfig(ctx, cluster) if err != nil { logger.Error(err, "Failed to create kubeClient") continue } logger.V(2).Info("Creating new cloud provider") cloud := provider.New(cluster, c.kind) ccm, err := startCloudControllerManager(ctx, cluster, restConfig, cloud) if err != nil { logger.Error(err, "Failed to start cloud controller") continue } logger.Info("Starting cloud controller") c.clusters[cluster] = ccm } // remove expired ones clusterSet := sets.New(clusters...) for cluster, ccm := range c.clusters { _, ok := clusterSet[cluster] if !ok { logger.Info("Deleting resources", "cluster", cluster) ccm.cancelFn() delete(c.clusters, cluster) } } select { case <-ctx.Done(): return case <-time.After(30 * time.Second): } } } func (c *Controller) getKubeConfig(cluster string, internal bool) (*rest.Config, error) { kconfig, err := c.kind.KubeConfig(cluster, internal) if err != nil { klog.Errorf("Failed to get kubeconfig for cluster %s: %v", cluster, err) return nil, err } config, err := clientcmd.RESTConfigFromKubeConfig([]byte(kconfig)) if err != nil { klog.Errorf("Failed to convert kubeconfig for cluster %s: %v", cluster, err) return nil, err } return config, nil } // getRestConfig returns a valid rest.Config for the cluster passed as argument // It tries first to connect to the internal endpoint. func (c *Controller) getRestConfig(ctx context.Context, cluster string) (*rest.Config, error) { logger := klog.FromContext(ctx).WithValues("cluster", cluster) addresses := []string{} internalConfig, err := c.getKubeConfig(cluster, true) if err != nil { logger.Error(err, "Failed to get internal kubeconfig") } else { addresses = append(addresses, internalConfig.Host) } externalConfig, err := c.getKubeConfig(cluster, false) if err != nil { logger.Error(err, "Failed to get external kubeconfig") } else { addresses = append(addresses, externalConfig.Host) } if len(addresses) == 0 { return nil, fmt.Errorf("could not find kubeconfig") } var host string for i := 0; i < 5; i++ { host, err = firstSuccessfulProbe(ctx, addresses) if err != nil { logger.Error(err, "Failed to connect to any address", "addresses", addresses) time.Sleep(time.Second * time.Duration(i)) } else { logger.Info("Connected succesfully", "host", host) break } } var config *rest.Config switch host { case internalConfig.Host: config = internalConfig // the first cluster will give us the type of connectivity between // cloud-provider-kind and the clusters and load balancer containers. // In Linux or containerized cloud-provider-kind this will be direct. once.Do(func() { cpkconfig.DefaultConfig.ControlPlaneConnectivity = cpkconfig.Direct }) case externalConfig.Host: config = externalConfig default: return nil, fmt.Errorf("restConfig for host %s not avaliable", host) } return config, nil } // TODO: implement leader election to not have problems with multiple providers // ref: https://github.com/kubernetes/kubernetes/blob/d97ea0f705847f90740cac3bc3dd8f6a4026d0b5/cmd/kube-scheduler/app/server.go#L211 func startCloudControllerManager(ctx context.Context, clusterName string, config *rest.Config, cloud cloudprovider.Interface) (*ccm, error) { logger := klog.FromContext(ctx).WithValues("cluster", clusterName) // TODO: we need to set up the ccm specific feature gates // but try to avoid to expose this to users featureGates := utilfeature.DefaultMutableFeatureGate err := ccmfeatures.SetupCurrentKubernetesSpecificFeatureGates(featureGates) if err != nil { return nil, err } kubeClient, err := kubernetes.NewForConfig(config) if err != nil { logger.Error(err, "Failed to create kubeClient") return nil, err } client := kubeClient.Discovery().RESTClient() // wait for health err = wait.PollUntilContextTimeout(ctx, 1*time.Second, 30*time.Second, true, func(ctx context.Context) (bool, error) { healthStatus := 0 client.Get().AbsPath("/healthz").Do(ctx).StatusCode(&healthStatus) if healthStatus != http.StatusOK { return false, nil } return true, nil }) if err != nil { logger.Error(err, "Failed waiting for apiserver to be ready") return nil, err } sharedInformers := informers.NewSharedInformerFactory(kubeClient, 60*time.Second) servicesInformer := sharedInformers.Core().V1().Services() nodesInformer := sharedInformers.Core().V1().Nodes() namespacesInformer := sharedInformers.Core().V1().Namespaces() secretsInformer := sharedInformers.Core().V1().Secrets() ingressInformer := sharedInformers.Networking().V1().Ingresses() ingressClassInformer := sharedInformers.Networking().V1().IngressClasses() ccmMetrics := controllersmetrics.NewControllerManagerMetrics(clusterName) // Start the service controller serviceController, err := servicecontroller.New( cloud, kubeClient, servicesInformer, nodesInformer, clusterName, featureGates, ) if err != nil { // This error shouldn't fail. It lives like this as a legacy. logger.Error(err, "Failed to start service controller") return nil, err } ctx, cancel := context.WithCancel(ctx) go serviceController.Run(ctx, 5, ccmMetrics) nodeController := &nodecontroller.CloudNodeController{} hasCloudProviderTaint, err := getCloudProviderTaint(ctx, clusterName, kubeClient) if err != nil { logger.Error(err, "Failed get cluster nodes") cancel() return nil, err } if hasCloudProviderTaint { // Start the node controller nodeController, err = nodecontroller.NewCloudNodeController( nodesInformer, kubeClient, cloud, 30*time.Second, 5, // workers ) if err != nil { // This error shouldn't fail. It lives like this as a legacy. logger.Error(err, "Failed to start node controller") cancel() return nil, err } go nodeController.Run(ctx.Done(), ccmMetrics) } // Gateway setup crdManager, err := gateway.NewCRDManager(config) if err != nil { logger.Error(err, "Failed to create Gateway API CRD manager") cancel() return nil, err } // Only install CRDs if gateway channel is not disabled if cpkconfig.DefaultConfig.GatewayReleaseChannel != cpkconfig.Disabled { err = crdManager.InstallCRDs(ctx, cpkconfig.DefaultConfig.GatewayReleaseChannel) if err != nil { logger.Error(err, "Failed to install Gateway API CRDs") cancel() return nil, err } } else { logger.Info("Gateway API CRDs installation skipped (disabled)") } var gatewayController *gateway.Controller // Only set up Gateway API controllers if not disabled if cpkconfig.DefaultConfig.GatewayReleaseChannel != cpkconfig.Disabled { gwClient, err := gatewayclient.NewForConfig(config) if err != nil { // This error shouldn't fail. It lives like this as a legacy. logger.Error(err, "Failed to create Gateway API client") cancel() return nil, err } sharedGwInformers := gatewayinformers.NewSharedInformerFactory(gwClient, 60*time.Second) gwClassInformer := sharedGwInformers.Gateway().V1().GatewayClasses() gwInformer := sharedGwInformers.Gateway().V1().Gateways() httpRouteInformer := sharedGwInformers.Gateway().V1().HTTPRoutes() grpcRouteInformer := sharedGwInformers.Gateway().V1().GRPCRoutes() referenceGrantInformer := sharedGwInformers.Gateway().V1().ReferenceGrants() gatewayController, err = gateway.New( clusterName, kubeClient, gwClient, namespacesInformer, servicesInformer, secretsInformer, gwClassInformer, gwInformer, httpRouteInformer, grpcRouteInformer, referenceGrantInformer, ) if err != nil { logger.Error(err, "Failed to start gateway controller") cancel() return nil, err } // Ingress to Gateway controller migration ingressController, err := ingress.NewController( kubeClient, gwClient, gateway.GWClassName, ingressInformer, ingressClassInformer, servicesInformer, secretsInformer, httpRouteInformer, gwInformer, ) if err != nil { logger.Error(err, "Failed to create Ingress controller") cancel() return nil, err } sharedInformers.Start(ctx.Done()) sharedGwInformers.Start(ctx.Done()) err = gatewayController.Init(ctx) if err != nil { logger.Error(err, "Failed to initialize gateway controller") cancel() return nil, err } go func() { _ = gatewayController.Run(ctx) }() err = ingressController.Init(ctx) if err != nil { logger.Error(err, "Failed to initialize ingress controller") cancel() return nil, err } go ingressController.Run(ctx, 5) } else { logger.Info("Gateway API controllers skipped (disabled)") // Start only the core informers, not gateway informers sharedInformers.Start(ctx.Done()) } // This has to cleanup all the resources allocated by the cloud provider in this cluster // - containers as loadbalancers // - in windows and darwin ip addresses on the loopback interface // Find all the containers associated to the cluster and then use the cloud provider methods to delete // the loadbalancer, we can extract the service name from the container labels. cancelFn := func() { cancel() containers, err := container.ListByLabel(fmt.Sprintf("%s=%s", constants.NodeCCMLabelKey, clusterName)) if err != nil { logger.Error(err, "Failed to list containers") return } lbController, ok := cloud.LoadBalancer() // this can not happen if !ok { return } for _, name := range containers { logger.V(2).Info("Cleaning up container", "container", name) cleanupLoadBalancer(lbController, name) cleanupGateway(name) } } return &ccm{ factory: sharedInformers, serviceController: serviceController, nodeController: nodeController, gatewayController: gatewayController, cancelFn: cancelFn}, nil } // TODO cleanup alias ip on mac func (c *Controller) cleanup() { for cluster, ccm := range c.clusters { klog.Infof("Cleaning resources for cluster %s", cluster) ccm.cancelFn() delete(c.clusters, cluster) } // final cleanup for containers that be left behind containers, err := container.ListByLabel(constants.NodeCCMLabelKey) if err != nil { klog.Errorf("can't list containers: %v", err) return } for _, name := range containers { klog.V(2).Infof("cleaning up container %s", name) err := container.Delete(name) if err != nil { klog.Errorf("error deleting container %s : %v", name, err) } } } func getCloudProviderTaint(ctx context.Context, clusterName string, kubeClient kubernetes.Interface) (bool, error) { nodes, err := kubeClient.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) if err != nil { return false, fmt.Errorf("failed to list nodes for cluster %s: %w", clusterName, err) } for _, node := range nodes.Items { for _, taint := range node.Spec.Taints { if taint.Key == cloudproviderapi.TaintExternalCloudProvider { return true, nil } } } return false, nil } func cleanupLoadBalancer(lbController cloudprovider.LoadBalancer, name string) { // create fake service to pass to the cloud provider method v, err := container.GetLabelValue(name, constants.LoadBalancerNameLabelKey) if err != nil || v == "" { klog.Infof("could not get the label for the loadbalancer on container %s : %v", name, err) return } clusterName, service := loadbalancer.ServiceFromLoadBalancerSimpleName(v) if service == nil { klog.Infof("invalid format for loadbalancer on cluster %s: %s", clusterName, v) return } err = lbController.EnsureLoadBalancerDeleted(context.Background(), clusterName, service) if err != nil { klog.Infof("error deleting loadbalancer %s/%s on cluster %s : %v", service.Namespace, service.Name, clusterName, err) return } } func cleanupGateway(name string) { // create fake service to pass to the cloud provider method v, err := container.GetLabelValue(name, constants.GatewayNameLabelKey) if err != nil || v == "" { klog.Infof("could not get the label for the loadbalancer on container %s : %v", name, err) return } err = container.Delete(name) if err != nil { klog.Infof("error deleting container %s gateway %s : %v", name, v, err) return } } ================================================ FILE: pkg/controller/http.go ================================================ package controller import ( "context" "crypto/tls" "fmt" "io" "net/http" "sync" "time" "k8s.io/klog/v2" ) func probeHTTP(ctx context.Context, address string) bool { klog.Infof("probe HTTP address %s", address) httpClient := &http.Client{ Timeout: 2 * time.Second, Transport: &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, }, } req, err := http.NewRequest("GET", address, nil) if err != nil { return false } req = req.WithContext(ctx) resp, err := httpClient.Do(req) if err != nil { klog.Infof("Failed to connect to HTTP address %s: %v", address, err) return false } defer resp.Body.Close() // drain the body io.ReadAll(resp.Body) // nolint:errcheck // we only want to verify connectivity so don't need to check the http status code // as the apiserver may not be ready return true } // firstSuccessfulProbe probes the given addresses in parallel and returns the first address to succeed, cancelling the other probes. func firstSuccessfulProbe(ctx context.Context, addresses []string) (string, error) { var wg sync.WaitGroup resultChan := make(chan string, 1) ctx, cancel := context.WithCancel(ctx) defer cancel() for _, addr := range addresses { wg.Add(1) go func(address string) { defer wg.Done() if probeHTTP(ctx, address) { select { case resultChan <- address: default: } cancel() } }(addr) } go func() { wg.Wait() close(resultChan) }() select { case result := <-resultChan: return result, nil case <-ctx.Done(): return "", fmt.Errorf("no address succeeded") } } ================================================ FILE: pkg/controller/http_test.go ================================================ package controller import ( "context" "net/http" "net/http/httptest" "testing" "time" ) func Test_firstSuccessfulProbe(t *testing.T) { reqCh := make(chan struct{}) ts := httptest.NewUnstartedServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { t.Logf("received connection ") close(reqCh) })) ts.EnableHTTP2 = true ts.StartTLS() defer ts.Close() // use an address that is not likely to exist to avoid flakes addresses := []string{"https://127.0.1.201:12349", ts.URL} got, err := firstSuccessfulProbe(context.Background(), addresses) if err != nil { t.Errorf("firstSuccessfulProbe() error = %v", err) return } if got != ts.URL { t.Errorf("firstSuccessfulProbe() = %v, want %v", got, ts.URL) } select { case <-reqCh: case <-time.After(10 * time.Second): t.Fatalf("test timed out, no request received") } } ================================================ FILE: pkg/gateway/backendref.go ================================================ package gateway import ( "fmt" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" ) // backendRefToClusterName generates a unique Envoy cluster name from a Gateway API BackendRef. // It returns a structured ControllerError with the appropriate Reason on failure. func backendRefToClusterName(defaultNamespace string, backendRef gatewayv1.BackendRef) (string, error) { // 1. Validate that the Kind is a supported type (Service). if backendRef.Kind != nil && *backendRef.Kind != "Service" { return "", &ControllerError{ Reason: string(gatewayv1.RouteReasonInvalidKind), Message: fmt.Sprintf("unsupported backend kind: %s", *backendRef.Kind), } } // 2. Validate that the Port is specified. if backendRef.Port == nil { return "", &ControllerError{ // Note: There isn't a specific reason for a missing port, // so InvalidParameters is a reasonable choice. Reason: string(gatewayv1.RouteReasonUnsupportedProtocol), Message: "backend port must be specified", } } namespace := defaultNamespace if backendRef.Namespace != nil { namespace = string(*backendRef.Namespace) } port := int32(*backendRef.Port) group := "core" if backendRef.Group != nil && *backendRef.Group != "" { group = string(*backendRef.Group) } kind := "Service" if backendRef.Kind != nil && *backendRef.Kind != "" { kind = string(*backendRef.Kind) } // Format: ____ clusterName := fmt.Sprintf("%s_%s_%s_%s_%d", namespace, backendRef.Name, group, kind, port) return clusterName, nil } ================================================ FILE: pkg/gateway/backendref_test.go ================================================ package gateway import ( "errors" "testing" "k8s.io/utils/ptr" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" ) func TestBackendRefToClusterName(t *testing.T) { tests := []struct { name string defaultNamespace string backendRef gatewayv1.BackendRef wantName string wantErr bool wantReason string }{ { name: "valid backendRef with all fields", defaultNamespace: "default", backendRef: gatewayv1.BackendRef{ BackendObjectReference: gatewayv1.BackendObjectReference{ Name: "my-service", Namespace: ptr.To(gatewayv1.Namespace("my-ns")), Group: ptr.To(gatewayv1.Group("my-group")), Kind: ptr.To(gatewayv1.Kind("Service")), Port: ptr.To(gatewayv1.PortNumber(8080)), }, }, wantName: "my-ns_my-service_my-group_Service_8080", wantErr: false, wantReason: "", }, { name: "valid backendRef with default fields", defaultNamespace: "default", backendRef: gatewayv1.BackendRef{ BackendObjectReference: gatewayv1.BackendObjectReference{ Name: "my-service", Port: ptr.To(gatewayv1.PortNumber(80)), }, }, wantName: "default_my-service_core_Service_80", wantErr: false, wantReason: "", }, { name: "unsupported kind", defaultNamespace: "default", backendRef: gatewayv1.BackendRef{ BackendObjectReference: gatewayv1.BackendObjectReference{ Name: "my-service", Kind: ptr.To(gatewayv1.Kind("UnsupportedKind")), Port: ptr.To(gatewayv1.PortNumber(80)), }, }, wantName: "", wantErr: true, wantReason: string(gatewayv1.RouteReasonInvalidKind), }, { name: "missing port", defaultNamespace: "default", backendRef: gatewayv1.BackendRef{ BackendObjectReference: gatewayv1.BackendObjectReference{ Name: "my-service", Port: nil, }, }, wantName: "", wantErr: true, wantReason: string(gatewayv1.RouteReasonUnsupportedProtocol), }, { name: "specified namespace", defaultNamespace: "default", backendRef: gatewayv1.BackendRef{ BackendObjectReference: gatewayv1.BackendObjectReference{ Name: "my-service", Namespace: ptr.To(gatewayv1.Namespace("other-ns")), Port: ptr.To(gatewayv1.PortNumber(80)), }, }, wantName: "other-ns_my-service_core_Service_80", wantErr: false, wantReason: "", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotName, err := backendRefToClusterName(tt.defaultNamespace, tt.backendRef) if (err != nil) != tt.wantErr { t.Errorf("backendRefToClusterName() error = %v, wantErr %v", err, tt.wantErr) return } if tt.wantErr { var controllerErr *ControllerError if errors.As(err, &controllerErr) { if controllerErr.Reason != tt.wantReason { t.Errorf("backendRefToClusterName() error reason = %s, wantReason %s", controllerErr.Reason, tt.wantReason) } } else { t.Errorf("backendRefToClusterName() expected a ControllerError, but got %T", err) } } if gotName != tt.wantName { t.Errorf("backendRefToClusterName() gotName = %v, want %v", gotName, tt.wantName) } }) } } ================================================ FILE: pkg/gateway/controller.go ================================================ /* Copyright 2024 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package gateway import ( "context" "fmt" "net" "sort" "sync/atomic" "time" "google.golang.org/grpc" "google.golang.org/grpc/keepalive" corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" clusterv3service "github.com/envoyproxy/go-control-plane/envoy/service/cluster/v3" discoveryv3 "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" endpointv3 "github.com/envoyproxy/go-control-plane/envoy/service/endpoint/v3" listenerv3service "github.com/envoyproxy/go-control-plane/envoy/service/listener/v3" routev3service "github.com/envoyproxy/go-control-plane/envoy/service/route/v3" runtimev3 "github.com/envoyproxy/go-control-plane/envoy/service/runtime/v3" secretv3 "github.com/envoyproxy/go-control-plane/envoy/service/secret/v3" envoyproxytypes "github.com/envoyproxy/go-control-plane/pkg/cache/types" cachev3 "github.com/envoyproxy/go-control-plane/pkg/cache/v3" resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" serverv3 "github.com/envoyproxy/go-control-plane/pkg/server/v3" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" corev1informers "k8s.io/client-go/informers/core/v1" "k8s.io/client-go/kubernetes" corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" "k8s.io/klog/v2" "k8s.io/utils/ptr" "sigs.k8s.io/cloud-provider-kind/pkg/config" "sigs.k8s.io/cloud-provider-kind/pkg/tunnels" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" gatewayclient "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned" gatewayinformers "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions/apis/v1" gatewaylisters "sigs.k8s.io/gateway-api/pkg/client/listers/apis/v1" ) const ( controllerName = "kind.sigs.k8s.io/gateway-controller" GWClassName = "cloud-provider-kind" maxRetries = 5 workers = 5 ) const ( grpcKeepaliveTime = 30 * time.Second grpcKeepaliveTimeout = 5 * time.Second grpcKeepaliveMinTime = 30 * time.Second grpcMaxConcurrentStreams = 1000000 ) type ControllerError struct { Reason string Message string } // Error implements the error interface. func (e *ControllerError) Error() string { return e.Message } type Controller struct { clusterName string clusterNameserver string client kubernetes.Interface gwClient gatewayclient.Interface namespaceLister corev1listers.NamespaceLister namespaceListerSynced cache.InformerSynced serviceLister corev1listers.ServiceLister serviceListerSynced cache.InformerSynced secretLister corev1listers.SecretLister secretListerSynced cache.InformerSynced gatewayClassLister gatewaylisters.GatewayClassLister gatewayClassListerSynced cache.InformerSynced gatewayLister gatewaylisters.GatewayLister gatewayListerSynced cache.InformerSynced gatewayqueue workqueue.TypedRateLimitingInterface[string] httprouteLister gatewaylisters.HTTPRouteLister httprouteListerSynced cache.InformerSynced grpcrouteLister gatewaylisters.GRPCRouteLister grpcrouteListerSynced cache.InformerSynced referenceGrantLister gatewaylisters.ReferenceGrantLister referenceGrantListerSynced cache.InformerSynced xdscache cachev3.SnapshotCache xdsserver serverv3.Server xdsLocalAddress string xdsLocalPort int xdsVersion atomic.Uint64 tunnelManager *tunnels.TunnelManager } func New( clusterName string, client *kubernetes.Clientset, gwClient *gatewayclient.Clientset, namespaceInformer corev1informers.NamespaceInformer, serviceInformer corev1informers.ServiceInformer, secretInformer corev1informers.SecretInformer, gatewayClassInformer gatewayinformers.GatewayClassInformer, gatewayInformer gatewayinformers.GatewayInformer, httprouteInformer gatewayinformers.HTTPRouteInformer, grpcrouteInformer gatewayinformers.GRPCRouteInformer, referenceGrantInformer gatewayinformers.ReferenceGrantInformer, ) (*Controller, error) { c := &Controller{ clusterName: clusterName, client: client, namespaceLister: namespaceInformer.Lister(), namespaceListerSynced: namespaceInformer.Informer().HasSynced, serviceLister: serviceInformer.Lister(), serviceListerSynced: serviceInformer.Informer().HasSynced, secretLister: secretInformer.Lister(), secretListerSynced: secretInformer.Informer().HasSynced, gwClient: gwClient, gatewayClassLister: gatewayClassInformer.Lister(), gatewayClassListerSynced: gatewayClassInformer.Informer().HasSynced, gatewayLister: gatewayInformer.Lister(), gatewayListerSynced: gatewayInformer.Informer().HasSynced, gatewayqueue: workqueue.NewTypedRateLimitingQueueWithConfig( workqueue.DefaultTypedControllerRateLimiter[string](), workqueue.TypedRateLimitingQueueConfig[string]{Name: "gateway"}, ), httprouteLister: httprouteInformer.Lister(), httprouteListerSynced: httprouteInformer.Informer().HasSynced, grpcrouteLister: grpcrouteInformer.Lister(), grpcrouteListerSynced: grpcrouteInformer.Informer().HasSynced, referenceGrantLister: referenceGrantInformer.Lister(), referenceGrantListerSynced: referenceGrantInformer.Informer().HasSynced, } _, err := gatewayClassInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) if err == nil { c.syncGatewayClass(key) } }, UpdateFunc: func(oldObj, newObj interface{}) { key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(newObj) if err == nil { c.syncGatewayClass(key) } }, DeleteFunc: func(obj interface{}) { key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) if err == nil { c.syncGatewayClass(key) } }, }) if err != nil { return nil, err } _, err = gatewayInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { gw := obj.(*gatewayv1.Gateway) if gw.Spec.GatewayClassName != GWClassName { return } key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) if err == nil { c.gatewayqueue.Add(key) } }, UpdateFunc: func(oldObj, newObj interface{}) { gw := newObj.(*gatewayv1.Gateway) if gw.Spec.GatewayClassName != GWClassName { return } key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(newObj) if err == nil { c.gatewayqueue.Add(key) } }, DeleteFunc: func(obj interface{}) { gw := obj.(*gatewayv1.Gateway) if gw.Spec.GatewayClassName != GWClassName { return } key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) if err == nil { c.gatewayqueue.Add(key) } }, }) if err != nil { return nil, err } _, err = httprouteInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { httproute := obj.(*gatewayv1.HTTPRoute) c.processGateways(httproute.Spec.ParentRefs, httproute.Namespace) }, UpdateFunc: func(oldObj, newObj interface{}) { oldHTTPRoute := oldObj.(*gatewayv1.HTTPRoute) newHTTPRoute := newObj.(*gatewayv1.HTTPRoute) c.processGateways(append(oldHTTPRoute.Spec.ParentRefs, newHTTPRoute.Spec.ParentRefs...), newHTTPRoute.Namespace) }, DeleteFunc: func(obj interface{}) { httproute, ok := obj.(*gatewayv1.HTTPRoute) if !ok { tombstone, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { runtime.HandleError(fmt.Errorf("couldn't get object from tombstone %#v", obj)) return } httproute, ok = tombstone.Obj.(*gatewayv1.HTTPRoute) if !ok { runtime.HandleError(fmt.Errorf("tombstone contained object that is not a HTTPRoute: %#v", obj)) return } } c.processGateways(httproute.Spec.ParentRefs, httproute.Namespace) }, }) if err != nil { return nil, err } _, err = grpcrouteInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { grpcroute := obj.(*gatewayv1.GRPCRoute) c.processGateways(grpcroute.Spec.ParentRefs, grpcroute.Namespace) }, UpdateFunc: func(oldObj, newObj interface{}) { oldGRPCRoute := oldObj.(*gatewayv1.GRPCRoute) newGRPCRoute := newObj.(*gatewayv1.GRPCRoute) c.processGateways(append(oldGRPCRoute.Spec.ParentRefs, newGRPCRoute.Spec.ParentRefs...), newGRPCRoute.Namespace) }, DeleteFunc: func(obj interface{}) { grpcroute, ok := obj.(*gatewayv1.GRPCRoute) if !ok { tombstone, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { runtime.HandleError(fmt.Errorf("couldn't get object from tombstone %#v", obj)) return } grpcroute, ok = tombstone.Obj.(*gatewayv1.GRPCRoute) if !ok { runtime.HandleError(fmt.Errorf("tombstone contained object that is not a GRPCRoute: %#v", obj)) return } } c.processGateways(grpcroute.Spec.ParentRefs, grpcroute.Namespace) }, }) if err != nil { return nil, err } _, err = referenceGrantInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: c.processReferenceGrant, UpdateFunc: func(old, new interface{}) { c.processReferenceGrant(new) }, DeleteFunc: c.processReferenceGrant, }) if err != nil { return nil, err } if config.DefaultConfig.LoadBalancerConnectivity == config.Tunnel { c.tunnelManager = tunnels.NewTunnelManager() } return c, nil } func (c *Controller) Init(ctx context.Context) error { defer runtime.HandleCrashWithContext(ctx) klog.Info("Waiting for gateway informer caches to sync") if !cache.WaitForNamedCacheSync(controllerName, ctx.Done(), c.gatewayClassListerSynced, c.gatewayListerSynced, c.httprouteListerSynced, c.grpcrouteListerSynced, c.namespaceListerSynced, c.serviceListerSynced, c.secretListerSynced, c.referenceGrantListerSynced, ) { return fmt.Errorf("timed out waiting for caches to sync") } kindGwClass := gatewayv1.GatewayClass{ ObjectMeta: metav1.ObjectMeta{ Name: GWClassName, }, Spec: gatewayv1.GatewayClassSpec{ ControllerName: controllerName, Description: ptr.To("cloud-provider-kind gateway API"), }, } _, err := c.gwClient.GatewayV1().GatewayClasses().Get(ctx, GWClassName, metav1.GetOptions{}) if apierrors.IsNotFound(err) { _, err = c.gwClient.GatewayV1().GatewayClasses().Create(ctx, &kindGwClass, metav1.CreateOptions{}) if err != nil { return fmt.Errorf("failed to create cloud-provider-kind GatewayClass: %w", err) } } else if err != nil { return fmt.Errorf("failed to get cloud-provider-kind GatewayClass: %w", err) } // This is kind/kubeadm specific as it uses kube-system/kube-dns as the nameserver err = wait.PollUntilContextTimeout(ctx, 100*time.Millisecond, 10*time.Second, true, func(ctx context.Context) (bool, error) { svc, err := c.serviceLister.Services(metav1.NamespaceSystem).Get("kube-dns") if err != nil { return false, nil } if svc.Spec.ClusterIP == "" { return false, nil } c.clusterNameserver = svc.Spec.ClusterIP return true, nil }) if err != nil { return fmt.Errorf("failed to get kube-dns service: %w", err) } return nil } func (c *Controller) syncGatewayClass(key string) { startTime := time.Now() klog.V(2).Infof("Started syncing gatewayclass %q (%v)", key, time.Since(startTime)) defer func() { klog.V(2).Infof("Finished syncing gatewayclass %q (%v)", key, time.Since(startTime)) }() gwc, err := c.gatewayClassLister.Get(key) if err != nil { if apierrors.IsNotFound(err) { klog.V(2).Infof("GatewayClass %q has been deleted", key) } return } // We only care about the GatewayClass that matches our controller name. if gwc.Spec.ControllerName != controllerName { return } newGwc := gwc.DeepCopy() // Set the "Accepted" condition to True and update the observedGeneration. meta.SetStatusCondition(&newGwc.Status.Conditions, metav1.Condition{ Type: string(gatewayv1.GatewayClassConditionStatusAccepted), Status: metav1.ConditionTrue, Reason: string(gatewayv1.GatewayClassReasonAccepted), Message: "GatewayClass is accepted by this controller.", ObservedGeneration: gwc.Generation, }) // Update the status on the API server. if _, err := c.gwClient.GatewayV1().GatewayClasses().UpdateStatus(context.Background(), newGwc, metav1.UpdateOptions{}); err != nil { klog.Errorf("failed to update gatewayclass status: %v", err) } } func (c *Controller) Run(ctx context.Context) error { logger := klog.FromContext(ctx).WithName("gateway") ctx = klog.NewContext(ctx, logger) defer runtime.HandleCrashWithContext(ctx) logger.Info("Starting Envoy proxy controller") c.xdscache = cachev3.NewSnapshotCache(false, cachev3.IDHash{}, nil) c.xdsserver = serverv3.NewServer(ctx, c.xdscache, &xdsCallbacks{}) var grpcOptions []grpc.ServerOption grpcOptions = append(grpcOptions, grpc.MaxConcurrentStreams(grpcMaxConcurrentStreams), grpc.KeepaliveParams(keepalive.ServerParameters{ Time: grpcKeepaliveTime, Timeout: grpcKeepaliveTimeout, }), grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{ MinTime: grpcKeepaliveMinTime, PermitWithoutStream: true, }), ) grpcServer := grpc.NewServer(grpcOptions...) discoveryv3.RegisterAggregatedDiscoveryServiceServer(grpcServer, c.xdsserver) endpointv3.RegisterEndpointDiscoveryServiceServer(grpcServer, c.xdsserver) clusterv3service.RegisterClusterDiscoveryServiceServer(grpcServer, c.xdsserver) routev3service.RegisterRouteDiscoveryServiceServer(grpcServer, c.xdsserver) listenerv3service.RegisterListenerDiscoveryServiceServer(grpcServer, c.xdsserver) secretv3.RegisterSecretDiscoveryServiceServer(grpcServer, c.xdsserver) runtimev3.RegisterRuntimeDiscoveryServiceServer(grpcServer, c.xdsserver) address, err := GetControlPlaneAddress() if err != nil { return err } listener, err := net.Listen("tcp", fmt.Sprintf("%s:0", address)) if err != nil { return err } defer listener.Close() addr := listener.Addr() tcpAddr, ok := addr.(*net.TCPAddr) if !ok { return fmt.Errorf("could not assert listener address to TCPAddr: %s", addr.String()) } c.xdsLocalAddress = address c.xdsLocalPort = tcpAddr.Port go func() { logger.Info( "XDS management server listening", "address", c.xdsLocalAddress, "port", c.xdsLocalPort) if err = grpcServer.Serve(listener); err != nil { logger.Error(err, "gRPC server error:") } grpcServer.Stop() }() defer c.gatewayqueue.ShutDown() logger.Info("Starting Gateway API controller") for i := 0; i < workers; i++ { go wait.UntilWithContext(ctx, c.runGatewayWorker, time.Second) } <-ctx.Done() logger.Info("Stopping Gateway API controller") return nil } func (c *Controller) processGateways(references []gatewayv1.ParentReference, localNamespace string) { gatewaysToEnqueue := make(map[string]struct{}) for _, ref := range references { if (ref.Group != nil && string(*ref.Group) != gatewayv1.GroupName) || (ref.Kind != nil && string(*ref.Kind) != "Gateway") { continue } namespace := localNamespace if ref.Namespace != nil { namespace = string(*ref.Namespace) } key := namespace + "/" + string(ref.Name) gatewaysToEnqueue[key] = struct{}{} } for key := range gatewaysToEnqueue { c.gatewayqueue.Add(key) } } // processReferenceGrant finds all Gateways that may be affected by a change to a // ReferenceGrant and enqueues them for reconciliation. This function handles grants // for both cross-namespace BackendRefs (from Routes) and cross-namespace // SecretRefs (from Gateways). func (c *Controller) processReferenceGrant(obj interface{}) { grant, ok := obj.(*gatewayv1.ReferenceGrant) if !ok { tombstone, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { klog.Errorf("error decoding object, invalid type") return } grant, ok = tombstone.Obj.(*gatewayv1.ReferenceGrant) if !ok { klog.Errorf("error decoding object tombstone, invalid type") return } } gatewaysToEnqueue := make(map[string]struct{}) // The ReferenceGrant lives in the namespace of the resource being referenced (the "To" side). targetNamespace := grant.Namespace // Check for Grants allowing Routes to reference Services for _, from := range grant.Spec.From { // As the controller supports more route types, they should be added here. if !CloudProviderSupportedKinds.Has(from.Kind) { continue } // Check if the grant allows references TO a Service. isServiceGrant := false for _, to := range grant.Spec.To { if to.Kind == "Service" { isServiceGrant = true break } } if !isServiceGrant { continue } // Find all routes in the "From" namespace that could be affected. httpRoutes, err := c.httprouteLister.HTTPRoutes(string(from.Namespace)).List(labels.Everything()) if err != nil { klog.Errorf("Failed to list HTTPRoutes in namespace %s: %v", from.Namespace, err) continue } for _, route := range httpRoutes { if routeReferencesBackendInNamespace(route, targetNamespace) { // This route is affected. Find its parent Gateways and add them to the queue. for _, parentRef := range route.Spec.ParentRefs { if (parentRef.Group != nil && string(*parentRef.Group) != gatewayv1.GroupName) || (parentRef.Kind != nil && string(*parentRef.Kind) != "Gateway") { continue } gwNamespace := route.Namespace if parentRef.Namespace != nil { gwNamespace = string(*parentRef.Namespace) } key := gwNamespace + "/" + string(parentRef.Name) gatewaysToEnqueue[key] = struct{}{} } } } } // Check for Grants allowing Gateways to reference Secrets for _, from := range grant.Spec.From { // We are looking for grants FROM Gateways. if from.Group != gatewayv1.GroupName || from.Kind != "Gateway" { continue } // Check if the grant allows references TO a Secret. isSecretGrant := false for _, to := range grant.Spec.To { if to.Kind == "Secret" { isSecretGrant = true break } } if !isSecretGrant { continue } // Find all Gateways in the "From" namespace that could be affected. gateways, err := c.gatewayLister.Gateways(string(from.Namespace)).List(labels.Everything()) if err != nil { klog.Errorf("Failed to list Gateways in namespace %s: %v", from.Namespace, err) continue } for _, gw := range gateways { if gatewayReferencesSecretInNamespace(gw, targetNamespace) { // This Gateway is affected. Add it to the queue. key := gw.Namespace + "/" + gw.Name gatewaysToEnqueue[key] = struct{}{} } } } // Enqueue all unique Gateways that were found to be affected. for key := range gatewaysToEnqueue { c.gatewayqueue.Add(key) } } // routeReferencesBackendInNamespace is a helper to check if an HTTPRoute has a backendRef // pointing to the specified namespace. func routeReferencesBackendInNamespace(route *gatewayv1.HTTPRoute, namespace string) bool { for _, rule := range route.Spec.Rules { for _, backendRef := range rule.BackendRefs { backendNamespace := route.Namespace if backendRef.Namespace != nil { backendNamespace = string(*backendRef.Namespace) } if backendNamespace == namespace { return true } } } return false } // gatewayReferencesSecretInNamespace is a helper to check if a Gateway has a certificateRef // pointing to a Secret in the specified namespace. func gatewayReferencesSecretInNamespace(gateway *gatewayv1.Gateway, namespace string) bool { for _, listener := range gateway.Spec.Listeners { if listener.TLS == nil { continue } for _, certRef := range listener.TLS.CertificateRefs { // We only care about references to Secrets. if (certRef.Group != nil && *certRef.Group != "") || (certRef.Kind != nil && *certRef.Kind != "Secret") { continue } secretNamespace := gateway.Namespace if certRef.Namespace != nil { secretNamespace = string(*certRef.Namespace) } if secretNamespace == namespace { return true } } } return false } func (c *Controller) runGatewayWorker(ctx context.Context) { for c.processNextGatewayItem(ctx) { } } func (c *Controller) processNextGatewayItem(ctx context.Context) bool { key, quit := c.gatewayqueue.Get() if quit { return false } defer c.gatewayqueue.Done(key) err := c.syncGateway(ctx, key) c.handleGatewayErr(err, key) return true } func (c *Controller) handleGatewayErr(err error, key string) { if err == nil { c.gatewayqueue.Forget(key) return } if c.gatewayqueue.NumRequeues(key) < maxRetries { klog.Infof("Error syncing Gateway %v: %v", key, err) c.gatewayqueue.AddRateLimited(key) return } c.gatewayqueue.Forget(key) runtime.HandleError(err) klog.Infof("Dropping Gateway %q out of the queue: %v", key, err) } func GetControlPlaneAddress() (string, error) { interfaces, err := net.Interfaces() if err != nil { return "", err } sort.Slice(interfaces, func(i, j int) bool { nameI := interfaces[i].Name nameJ := interfaces[j].Name if nameI == "docker0" { return true } if nameJ == "docker0" { return false } if nameI == "eth0" { return nameJ != "docker0" } if nameJ == "eth0" { return false } return nameI < nameJ }) for _, iface := range interfaces { if iface.Flags&net.FlagUp == 0 || iface.Flags&net.FlagLoopback != 0 { continue } addrs, err := iface.Addrs() if err != nil { continue } for _, addr := range addrs { ipNet, ok := addr.(*net.IPNet) if ok && ipNet.IP.To4() != nil && !ipNet.IP.IsLinkLocalUnicast() && !ipNet.IP.IsLoopback() { return ipNet.IP.String(), nil } } } return "", fmt.Errorf("no suitable global unicast IPv4 address found on any active non-loopback interface") } func (c *Controller) UpdateXDSServer(ctx context.Context, nodeid string, resources map[resourcev3.Type][]envoyproxytypes.Resource) error { c.xdsVersion.Add(1) snapshot, err := cachev3.NewSnapshot(fmt.Sprintf("%d", c.xdsVersion.Load()), resources) if err != nil { return fmt.Errorf("failed to create new snapshot cache: %v", err) } if err := c.xdscache.SetSnapshot(ctx, nodeid, snapshot); err != nil { return fmt.Errorf("failed to update resource snapshot in management server: %v", err) } logger := klog.FromContext(ctx).WithValues("nodeID", nodeid) logger.V(4).Info("Updated snapshot cache with resource snapshot...") return nil } var _ serverv3.Callbacks = &xdsCallbacks{} type xdsCallbacks struct{} func (cb *xdsCallbacks) OnStreamOpen(ctx context.Context, id int64, typ string) error { klog.V(2).Infof("xDS stream %d opened for type %s", id, typ) return nil } func (cb *xdsCallbacks) OnStreamClosed(id int64, node *corev3.Node) { nodeID := "unknown" if node != nil { nodeID = node.GetId() } klog.V(2).Infof("xDS stream %d closed for node %s", id, nodeID) } func (cb *xdsCallbacks) OnStreamRequest(id int64, req *discoveryv3.DiscoveryRequest) error { klog.V(5).Infof("xDS stream %d received request for type %s from node %s", id, req.TypeUrl, req.Node.GetId()) return nil } func (cb *xdsCallbacks) OnStreamResponse(ctx context.Context, id int64, req *discoveryv3.DiscoveryRequest, resp *discoveryv3.DiscoveryResponse) { klog.V(5).Infof("xDS stream %d sent response for type %s to node %s", id, resp.TypeUrl, req.Node.GetId()) } func (cb *xdsCallbacks) OnFetchRequest(ctx context.Context, req *discoveryv3.DiscoveryRequest) error { klog.V(5).Infof("xDS fetch request received for type %s from node %s", req.TypeUrl, req.Node.GetId()) return nil } func (cb *xdsCallbacks) OnFetchResponse(req *discoveryv3.DiscoveryRequest, resp *discoveryv3.DiscoveryResponse) { klog.V(5).Infof("xDS fetch response sent for type %s to node %s", resp.TypeUrl, req.Node.GetId()) } func (cb *xdsCallbacks) OnStreamDeltaRequest(id int64, req *discoveryv3.DeltaDiscoveryRequest) error { return nil } func (cb *xdsCallbacks) OnStreamDeltaResponse(id int64, req *discoveryv3.DeltaDiscoveryRequest, resp *discoveryv3.DeltaDiscoveryResponse) { } func (cb *xdsCallbacks) OnDeltaStreamClosed(int64, *corev3.Node) {} func (cb *xdsCallbacks) OnDeltaStreamOpen(context.Context, int64, string) error { return nil } ================================================ FILE: pkg/gateway/controller_test.go ================================================ package gateway ================================================ FILE: pkg/gateway/crd_manager.go ================================================ package gateway import ( "context" "embed" "fmt" "io" "io/fs" "path" "strings" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/util/yaml" "k8s.io/client-go/dynamic" "k8s.io/client-go/rest" "k8s.io/klog/v2" "sigs.k8s.io/cloud-provider-kind/pkg/config" ) //go:embed crds/standard/*.yaml crds/experimental/*.yaml var crdFS embed.FS const ( crdsDir = "crds" // Base directory within the embedded FS // use constants to avoid the dependency on apiextensions crdKind = "CustomResourceDefinition" crdResource = "customresourcedefinitions" crdGroup = "apiextensions.k8s.io" crdVersion = "v1" crdAPIVersion = "apiextensions.k8s.io/v1" ) // CRDManager handles the installation of Gateway API CRDs. type CRDManager struct { dynamicClient dynamic.Interface } // NewCRDManager creates a new CRDManager instance. // It attempts to load in-cluster config first, then falls back to kubeconfig. func NewCRDManager(config *rest.Config) (*CRDManager, error) { dynamicClient, err := dynamic.NewForConfig(config) if err != nil { return nil, fmt.Errorf("failed to create dynamic client: %w", err) } return &CRDManager{ dynamicClient: dynamicClient, }, nil } // InstallCRDs reads CRDs from the embedded filesystem and applies them to the cluster. func (m *CRDManager) InstallCRDs(ctx context.Context, channelDir config.GatewayReleaseChannel) error { crdGVR := schema.GroupVersionResource{ Group: crdGroup, Version: crdVersion, Resource: crdResource, } // embed.FS always uses Unix path names targetDir := path.Join(crdsDir, string(channelDir)) klog.Infof("Walking embedded directory for channel %q: %s", channelDir, targetDir) err := fs.WalkDir(crdFS, targetDir, func(path string, d fs.DirEntry, err error) error { if err != nil { return fmt.Errorf("error walking embedded fs at %q: %w", path, err) } // Skip directories if d.IsDir() { // Skip the root directory itself, only process files within it if path == targetDir { klog.V(4).Infof("Entering directory: %s", path) return nil } klog.V(4).Infof("Skipping directory: %s", path) return nil // Continue walking } // Skip the kustomize files if strings.HasSuffix(path, "kustomization.yaml") { return nil } // Process only YAML files if !strings.HasSuffix(path, ".yaml") && !strings.HasSuffix(path, ".yml") { klog.V(4).Infof("Skipping non-yaml file: %s", path) return nil } klog.Infof("Processing embedded CRD file: %s", path) file, err := crdFS.Open(path) if err != nil { return fmt.Errorf("failed to open embedded file %q: %w", path, err) } defer file.Close() // Use a YAML decoder to handle multi-document files decoder := yaml.NewYAMLOrJSONDecoder(file, 4096) for { obj := &unstructured.Unstructured{} if err := decoder.Decode(obj); err != nil { if err == io.EOF { break // End of file } return fmt.Errorf("failed to decode YAML document from %q: %w", path, err) } // Basic validation if obj.GetKind() != crdKind || obj.GetAPIVersion() != crdAPIVersion { klog.Warningf("Skipping object in %q with unexpected kind/apiVersion: %s/%s", path, obj.GetAPIVersion(), obj.GetKind()) continue } crdName := obj.GetName() klog.Infof("Attempting to create CRD: %s", crdName) _, createErr := m.dynamicClient.Resource(crdGVR).Create(ctx, obj, metav1.CreateOptions{}) if createErr != nil { if errors.IsAlreadyExists(createErr) { klog.Infof("CRD %q already exists, skipping creation.", crdName) // TODO: Consider updating/patching if needed, but for CRDs, create-if-not-exists is often sufficient. } else { return fmt.Errorf("failed to create CRD %q from file %q: %w", crdName, path, createErr) } } else { klog.Infof("Successfully created CRD: %s", crdName) } } return nil }) if err != nil { return fmt.Errorf("error processing embedded CRDs from %s: %w", targetDir, err) } klog.Info("Finished processing embedded CRDs.") return nil } ================================================ FILE: pkg/gateway/crd_manager_test.go ================================================ package gateway import ( "testing" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/dynamic/fake" "sigs.k8s.io/cloud-provider-kind/pkg/config" ) func TestYAMLFilesApply(t *testing.T) { tests := []struct { name string channel config.GatewayReleaseChannel }{ { name: "Stable release channel", channel: config.Standard, }, { name: "Experimental release channel", channel: config.Experimental, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { manager := CRDManager { dynamicClient: fake.NewSimpleDynamicClient(runtime.NewScheme()), } if err := manager.InstallCRDs(t.Context(), tt.channel); err != nil { t.Fatalf("couldn't install CRDs: %v", err) } }) } } ================================================ FILE: pkg/gateway/crds/README.md ================================================ # Gateway CRDs This folder is updated with `hack/download-gateway-crds.sh` script to obtain the lastest CRDs from https://github.com/kubernetes-sigs/gateway-api ================================================ FILE: pkg/gateway/crds/experimental/gateway.networking.k8s.io_backendtlspolicies.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: experimental labels: gateway.networking.k8s.io/policy: Direct name: backendtlspolicies.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: BackendTLSPolicy listKind: BackendTLSPolicyList plural: backendtlspolicies shortNames: - btlspolicy singular: backendtlspolicy scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1 schema: openAPIV3Schema: description: |- BackendTLSPolicy provides a way to configure how a Gateway connects to a Backend via TLS. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of BackendTLSPolicy. properties: options: additionalProperties: description: |- AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. maxLength: 4096 minLength: 0 type: string description: |- Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. Support: Implementation-specific maxProperties: 16 type: object targetRefs: description: |- TargetRefs identifies an API object to apply the policy to. Note that this config applies to the entire referenced resource by default, but this default may change in the future to provide a more granular application of the policy. TargetRefs must be _distinct_. This means either that: * They select different targets. If this is the case, then targetRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, and `name` must be unique across all targetRef entries in the BackendTLSPolicy. * They select different sectionNames in the same target. When more than one BackendTLSPolicy selects the same target and sectionName, implementations MUST determine precedence using the following criteria, continuing on ties: * The older policy by creation timestamp takes precedence. For example, a policy with a creation timestamp of "2021-07-15 01:02:03" MUST be given precedence over a policy with a creation timestamp of "2021-07-15 01:02:04". * The policy appearing first in alphabetical order by {namespace}/{name}. For example, a policy named `foo/bar` is given precedence over a policy named `foo/baz`. For any BackendTLSPolicy that does not take precedence, the implementation MUST ensure the `Accepted` Condition is set to `status: False`, with Reason `Conflicted`. Implementations SHOULD NOT support more than one targetRef at this time. Although the API technically allows for this, the current guidance for conflict resolution and status handling is lacking. Until that can be clarified in a future release, the safest approach is to support a single targetRef. Support Levels: * Extended: Kubernetes Service referenced by HTTPRoute backendRefs. * Implementation-Specific: Services not connected via HTTPRoute, and any other kind of backend. Implementations MAY use BackendTLSPolicy for: - Services not referenced by any Route (e.g., infrastructure services) - Gateway feature backends (e.g., ExternalAuth, rate-limiting services) - Service mesh workload-to-service communication - Other resource types beyond Service Implementations SHOULD aim to ensure that BackendTLSPolicy behavior is consistent, even outside of the extended HTTPRoute -(backendRef) -> Service path. They SHOULD clearly document how BackendTLSPolicy is interpreted in these scenarios, including: - Which resources beyond Service are supported - How the policy is discovered and applied - Any implementation-specific semantics or restrictions Note that this config applies to the entire referenced resource by default, but this default may change in the future to provide a more granular application of the policy. items: description: |- LocalPolicyTargetReferenceWithSectionName identifies an API object to apply a direct policy to. This should be used as part of Policy resources that can target single resources. For more information on how this policy attachment mode works, and a sample Policy resource, refer to the policy attachment documentation for Gateway API. Note: This should only be used for direct policy attachment when references to SectionName are actually needed. In all other cases, LocalPolicyTargetReference should be used. properties: group: description: Group is the group of the target resource. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the target resource. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the target resource. maxLength: 253 minLength: 1 type: string sectionName: description: |- SectionName is the name of a section within the target resource. When unspecified, this targetRef targets the entire resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name * HTTPRoute: HTTPRouteRule name * Service: Port name If a SectionName is specified, but does not exist on the targeted object, the Policy must fail to attach, and the policy implementation should record a `ResolvedRefs` or similar Condition in the Policy's status. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - group - kind - name type: object maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName must be specified when targetRefs includes 2 or more references to the same target rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''')) : true))' - message: sectionName must be unique when targetRefs includes 2 or more references to the same target rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)))) validation: description: Validation contains backend TLS validation configuration. properties: caCertificateRefs: description: |- CACertificateRefs contains one or more references to Kubernetes objects that contain a PEM-encoded TLS CA certificate bundle, which is used to validate a TLS handshake between the Gateway and backend Pod. If CACertificateRefs is empty or unspecified, then WellKnownCACertificates must be specified. Only one of CACertificateRefs or WellKnownCACertificates may be specified, not both. If CACertificateRefs is empty or unspecified, the configuration for WellKnownCACertificates MUST be honored instead if supported by the implementation. A CACertificateRef is invalid if: * It refers to a resource that cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured (e.g., a ConfigMap does not contain a key named `ca.crt`). In this case, the Reason must be set to `InvalidCACertificateRef` and the Message of the Condition must indicate which reference is invalid and why. * It refers to an unknown or unsupported kind of resource. In this case, the Reason must be set to `InvalidKind` and the Message of the Condition must explain which kind of resource is unknown or unsupported. * It refers to a resource in another namespace. This may change in future spec updates. Implementations MAY choose to perform further validation of the certificate content (e.g., checking expiry or enforcing specific formats). In such cases, an implementation-specific Reason and Message must be set for the invalid reference. In all cases, the implementation MUST ensure the `ResolvedRefs` Condition on the BackendTLSPolicy is set to `status: False`, with a Reason and Message that indicate the cause of the error. Connections using an invalid CACertificateRef MUST fail, and the client MUST receive an HTTP 5xx error response. If ALL CACertificateRefs are invalid, the implementation MUST also ensure the `Accepted` Condition on the BackendTLSPolicy is set to `status: False`, with a Reason `NoValidCACertificate`. A single CACertificateRef to a Kubernetes ConfigMap kind has "Core" support. Implementations MAY choose to support attaching multiple certificates to a backend, but this behavior is implementation-specific. Support: Core - An optional single reference to a Kubernetes ConfigMap, with the CA certificate in a key named `ca.crt`. Support: Implementation-specific - More than one reference, other kinds of resources, or a single reference that includes multiple certificates. items: description: |- LocalObjectReference identifies an API object within the namespace of the referrer. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "HTTPRoute" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object maxItems: 8 type: array x-kubernetes-list-type: atomic hostname: description: |- Hostname is used for two purposes in the connection between Gateways and backends: 1. Hostname MUST be used as the SNI to connect to the backend (RFC 6066). 2. Hostname MUST be used for authentication and MUST match the certificate served by the matching backend, unless SubjectAltNames is specified. 3. If SubjectAltNames are specified, Hostname can be used for certificate selection but MUST NOT be used for authentication. If you want to use the value of the Hostname field for authentication, you MUST add it to the SubjectAltNames list. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string subjectAltNames: description: |- SubjectAltNames contains one or more Subject Alternative Names. When specified the certificate served from the backend MUST have at least one Subject Alternate Name matching one of the specified SubjectAltNames. Support: Extended items: description: SubjectAltName represents Subject Alternative Name. properties: hostname: description: |- Hostname contains Subject Alternative Name specified in DNS name format. Required when Type is set to Hostname, ignored otherwise. Support: Core maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string type: description: |- Type determines the format of the Subject Alternative Name. Always required. Support: Core enum: - Hostname - URI type: string uri: description: |- URI contains Subject Alternative Name specified in a full URI format. It MUST include both a scheme (e.g., "http" or "ftp") and a scheme-specific-part. Common values include SPIFFE IDs like "spiffe://mycluster.example.com/ns/myns/sa/svc1sa". Required when Type is set to URI, ignored otherwise. Support: Core maxLength: 253 minLength: 1 pattern: ^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))? type: string required: - type type: object x-kubernetes-validations: - message: SubjectAltName element must contain Hostname, if Type is set to Hostname rule: '!(self.type == "Hostname" && (!has(self.hostname) || self.hostname == ""))' - message: SubjectAltName element must not contain Hostname, if Type is not set to Hostname rule: '!(self.type != "Hostname" && has(self.hostname) && self.hostname != "")' - message: SubjectAltName element must contain URI, if Type is set to URI rule: '!(self.type == "URI" && (!has(self.uri) || self.uri == ""))' - message: SubjectAltName element must not contain URI, if Type is not set to URI rule: '!(self.type != "URI" && has(self.uri) && self.uri != "")' maxItems: 5 type: array x-kubernetes-list-type: atomic wellKnownCACertificates: description: |- WellKnownCACertificates specifies whether a well-known set of CA certificates may be used in the TLS handshake between the gateway and backend pod. If WellKnownCACertificates is unspecified or empty (""), then CACertificateRefs must be specified with at least one entry for a valid configuration. Only one of CACertificateRefs or WellKnownCACertificates may be specified, not both. If an implementation does not support the WellKnownCACertificates field, or the supplied value is not recognized, the implementation MUST ensure the `Accepted` Condition on the BackendTLSPolicy is set to `status: False`, with a Reason `Invalid`. Valid values include: * "System" - indicates that well-known system CA certificates should be used. Implementations MAY define their own sets of CA certificates. Such definitions MUST use an implementation-specific, prefixed name, such as `mycompany.com/my-custom-ca-certifcates`. Support: Implementation-specific maxLength: 253 minLength: 1 pattern: ^(System|([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9]))$ type: string required: - hostname type: object x-kubernetes-validations: - message: must not contain both CACertificateRefs and WellKnownCACertificates rule: '!(has(self.caCertificateRefs) && size(self.caCertificateRefs) > 0 && has(self.wellKnownCACertificates) && self.wellKnownCACertificates != "")' - message: must specify either CACertificateRefs or WellKnownCACertificates rule: (has(self.caCertificateRefs) && size(self.caCertificateRefs) > 0 || has(self.wellKnownCACertificates) && self.wellKnownCACertificates != "") required: - targetRefs - validation type: object status: description: Status defines the current state of BackendTLSPolicy. properties: ancestors: description: |- Ancestors is a list of ancestor resources (usually Gateways) that are associated with the policy, and the status of the policy with respect to each ancestor. When this policy attaches to a parent, the controller that manages the parent and the ancestors MUST add an entry to this list when the controller first sees the policy and SHOULD update the entry as appropriate when the relevant ancestor is modified. Note that choosing the relevant ancestor is left to the Policy designers; an important part of Policy design is designing the right object level at which to namespace this status. Note also that implementations MUST ONLY populate ancestor status for the Ancestor resources they are responsible for. Implementations MUST use the ControllerName field to uniquely identify the entries in this list that they are responsible for. Note that to achieve this, the list of PolicyAncestorStatus structs MUST be treated as a map with a composite key, made up of the AncestorRef and ControllerName fields combined. A maximum of 16 ancestors will be represented in this list. An empty list means the Policy is not relevant for any ancestors. If this slice is full, implementations MUST NOT add further entries. Instead they MUST consider the policy unimplementable and signal that on any related resources such as the ancestor that would be referenced here. For example, if this list was full on BackendTLSPolicy, no additional Gateways would be able to reference the Service targeted by the BackendTLSPolicy. items: description: |- PolicyAncestorStatus describes the status of a route with respect to an associated Ancestor. Ancestors refer to objects that are either the Target of a policy or above it in terms of object hierarchy. For example, if a policy targets a Service, the Policy's Ancestors are, in order, the Service, the HTTPRoute, the Gateway, and the GatewayClass. Almost always, in this hierarchy, the Gateway will be the most useful object to place Policy status on, so we recommend that implementations SHOULD use Gateway as the PolicyAncestorStatus object unless the designers have a _very_ good reason otherwise. In the context of policy attachment, the Ancestor is used to distinguish which resource results in a distinct application of this policy. For example, if a policy targets a Service, it may have a distinct result per attached Gateway. Policies targeting the same resource may have different effects depending on the ancestors of those resources. For example, different Gateways targeting the same Service may have different capabilities, especially if they have different underlying implementations. For example, in BackendTLSPolicy, the Policy attaches to a Service that is used as a backend in a HTTPRoute that is itself attached to a Gateway. In this case, the relevant object for status is the Gateway, and that is the ancestor object referred to in this status. Note that a parent is also an ancestor, so for objects where the parent is the relevant object for status, this struct SHOULD still be used. This struct is intended to be used in a slice that's effectively a map, with a composite key made up of the AncestorRef and the ControllerName. properties: ancestorRef: description: |- AncestorRef corresponds with a ParentRef in the spec that this PolicyAncestorStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object conditions: description: Conditions describes the status of the Policy with respect to the given Ancestor. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string required: - ancestorRef - conditions - controllerName type: object maxItems: 16 type: array x-kubernetes-list-type: atomic required: - ancestors type: object required: - spec type: object served: true storage: true subresources: status: {} - deprecated: true deprecationWarning: The v1alpha3 version of BackendTLSPolicy has been deprecated and will be removed in a future release of the API. Please upgrade to v1. name: v1alpha3 schema: openAPIV3Schema: description: |- BackendTLSPolicy provides a way to configure how a Gateway connects to a Backend via TLS. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of BackendTLSPolicy. properties: options: additionalProperties: description: |- AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. maxLength: 4096 minLength: 0 type: string description: |- Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. Support: Implementation-specific maxProperties: 16 type: object targetRefs: description: |- TargetRefs identifies an API object to apply the policy to. Note that this config applies to the entire referenced resource by default, but this default may change in the future to provide a more granular application of the policy. TargetRefs must be _distinct_. This means either that: * They select different targets. If this is the case, then targetRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, and `name` must be unique across all targetRef entries in the BackendTLSPolicy. * They select different sectionNames in the same target. When more than one BackendTLSPolicy selects the same target and sectionName, implementations MUST determine precedence using the following criteria, continuing on ties: * The older policy by creation timestamp takes precedence. For example, a policy with a creation timestamp of "2021-07-15 01:02:03" MUST be given precedence over a policy with a creation timestamp of "2021-07-15 01:02:04". * The policy appearing first in alphabetical order by {namespace}/{name}. For example, a policy named `foo/bar` is given precedence over a policy named `foo/baz`. For any BackendTLSPolicy that does not take precedence, the implementation MUST ensure the `Accepted` Condition is set to `status: False`, with Reason `Conflicted`. Implementations SHOULD NOT support more than one targetRef at this time. Although the API technically allows for this, the current guidance for conflict resolution and status handling is lacking. Until that can be clarified in a future release, the safest approach is to support a single targetRef. Support Levels: * Extended: Kubernetes Service referenced by HTTPRoute backendRefs. * Implementation-Specific: Services not connected via HTTPRoute, and any other kind of backend. Implementations MAY use BackendTLSPolicy for: - Services not referenced by any Route (e.g., infrastructure services) - Gateway feature backends (e.g., ExternalAuth, rate-limiting services) - Service mesh workload-to-service communication - Other resource types beyond Service Implementations SHOULD aim to ensure that BackendTLSPolicy behavior is consistent, even outside of the extended HTTPRoute -(backendRef) -> Service path. They SHOULD clearly document how BackendTLSPolicy is interpreted in these scenarios, including: - Which resources beyond Service are supported - How the policy is discovered and applied - Any implementation-specific semantics or restrictions Note that this config applies to the entire referenced resource by default, but this default may change in the future to provide a more granular application of the policy. items: description: |- LocalPolicyTargetReferenceWithSectionName identifies an API object to apply a direct policy to. This should be used as part of Policy resources that can target single resources. For more information on how this policy attachment mode works, and a sample Policy resource, refer to the policy attachment documentation for Gateway API. Note: This should only be used for direct policy attachment when references to SectionName are actually needed. In all other cases, LocalPolicyTargetReference should be used. properties: group: description: Group is the group of the target resource. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the target resource. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the target resource. maxLength: 253 minLength: 1 type: string sectionName: description: |- SectionName is the name of a section within the target resource. When unspecified, this targetRef targets the entire resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name * HTTPRoute: HTTPRouteRule name * Service: Port name If a SectionName is specified, but does not exist on the targeted object, the Policy must fail to attach, and the policy implementation should record a `ResolvedRefs` or similar Condition in the Policy's status. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - group - kind - name type: object maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName must be specified when targetRefs includes 2 or more references to the same target rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''')) : true))' - message: sectionName must be unique when targetRefs includes 2 or more references to the same target rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)))) validation: description: Validation contains backend TLS validation configuration. properties: caCertificateRefs: description: |- CACertificateRefs contains one or more references to Kubernetes objects that contain a PEM-encoded TLS CA certificate bundle, which is used to validate a TLS handshake between the Gateway and backend Pod. If CACertificateRefs is empty or unspecified, then WellKnownCACertificates must be specified. Only one of CACertificateRefs or WellKnownCACertificates may be specified, not both. If CACertificateRefs is empty or unspecified, the configuration for WellKnownCACertificates MUST be honored instead if supported by the implementation. A CACertificateRef is invalid if: * It refers to a resource that cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured (e.g., a ConfigMap does not contain a key named `ca.crt`). In this case, the Reason must be set to `InvalidCACertificateRef` and the Message of the Condition must indicate which reference is invalid and why. * It refers to an unknown or unsupported kind of resource. In this case, the Reason must be set to `InvalidKind` and the Message of the Condition must explain which kind of resource is unknown or unsupported. * It refers to a resource in another namespace. This may change in future spec updates. Implementations MAY choose to perform further validation of the certificate content (e.g., checking expiry or enforcing specific formats). In such cases, an implementation-specific Reason and Message must be set for the invalid reference. In all cases, the implementation MUST ensure the `ResolvedRefs` Condition on the BackendTLSPolicy is set to `status: False`, with a Reason and Message that indicate the cause of the error. Connections using an invalid CACertificateRef MUST fail, and the client MUST receive an HTTP 5xx error response. If ALL CACertificateRefs are invalid, the implementation MUST also ensure the `Accepted` Condition on the BackendTLSPolicy is set to `status: False`, with a Reason `NoValidCACertificate`. A single CACertificateRef to a Kubernetes ConfigMap kind has "Core" support. Implementations MAY choose to support attaching multiple certificates to a backend, but this behavior is implementation-specific. Support: Core - An optional single reference to a Kubernetes ConfigMap, with the CA certificate in a key named `ca.crt`. Support: Implementation-specific - More than one reference, other kinds of resources, or a single reference that includes multiple certificates. items: description: |- LocalObjectReference identifies an API object within the namespace of the referrer. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "HTTPRoute" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object maxItems: 8 type: array x-kubernetes-list-type: atomic hostname: description: |- Hostname is used for two purposes in the connection between Gateways and backends: 1. Hostname MUST be used as the SNI to connect to the backend (RFC 6066). 2. Hostname MUST be used for authentication and MUST match the certificate served by the matching backend, unless SubjectAltNames is specified. 3. If SubjectAltNames are specified, Hostname can be used for certificate selection but MUST NOT be used for authentication. If you want to use the value of the Hostname field for authentication, you MUST add it to the SubjectAltNames list. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string subjectAltNames: description: |- SubjectAltNames contains one or more Subject Alternative Names. When specified the certificate served from the backend MUST have at least one Subject Alternate Name matching one of the specified SubjectAltNames. Support: Extended items: description: SubjectAltName represents Subject Alternative Name. properties: hostname: description: |- Hostname contains Subject Alternative Name specified in DNS name format. Required when Type is set to Hostname, ignored otherwise. Support: Core maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string type: description: |- Type determines the format of the Subject Alternative Name. Always required. Support: Core enum: - Hostname - URI type: string uri: description: |- URI contains Subject Alternative Name specified in a full URI format. It MUST include both a scheme (e.g., "http" or "ftp") and a scheme-specific-part. Common values include SPIFFE IDs like "spiffe://mycluster.example.com/ns/myns/sa/svc1sa". Required when Type is set to URI, ignored otherwise. Support: Core maxLength: 253 minLength: 1 pattern: ^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))? type: string required: - type type: object x-kubernetes-validations: - message: SubjectAltName element must contain Hostname, if Type is set to Hostname rule: '!(self.type == "Hostname" && (!has(self.hostname) || self.hostname == ""))' - message: SubjectAltName element must not contain Hostname, if Type is not set to Hostname rule: '!(self.type != "Hostname" && has(self.hostname) && self.hostname != "")' - message: SubjectAltName element must contain URI, if Type is set to URI rule: '!(self.type == "URI" && (!has(self.uri) || self.uri == ""))' - message: SubjectAltName element must not contain URI, if Type is not set to URI rule: '!(self.type != "URI" && has(self.uri) && self.uri != "")' maxItems: 5 type: array x-kubernetes-list-type: atomic wellKnownCACertificates: description: |- WellKnownCACertificates specifies whether a well-known set of CA certificates may be used in the TLS handshake between the gateway and backend pod. If WellKnownCACertificates is unspecified or empty (""), then CACertificateRefs must be specified with at least one entry for a valid configuration. Only one of CACertificateRefs or WellKnownCACertificates may be specified, not both. If an implementation does not support the WellKnownCACertificates field, or the supplied value is not recognized, the implementation MUST ensure the `Accepted` Condition on the BackendTLSPolicy is set to `status: False`, with a Reason `Invalid`. Valid values include: * "System" - indicates that well-known system CA certificates should be used. Implementations MAY define their own sets of CA certificates. Such definitions MUST use an implementation-specific, prefixed name, such as `mycompany.com/my-custom-ca-certifcates`. Support: Implementation-specific maxLength: 253 minLength: 1 pattern: ^(System|([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9]))$ type: string required: - hostname type: object x-kubernetes-validations: - message: must not contain both CACertificateRefs and WellKnownCACertificates rule: '!(has(self.caCertificateRefs) && size(self.caCertificateRefs) > 0 && has(self.wellKnownCACertificates) && self.wellKnownCACertificates != "")' - message: must specify either CACertificateRefs or WellKnownCACertificates rule: (has(self.caCertificateRefs) && size(self.caCertificateRefs) > 0 || has(self.wellKnownCACertificates) && self.wellKnownCACertificates != "") required: - targetRefs - validation type: object status: description: Status defines the current state of BackendTLSPolicy. properties: ancestors: description: |- Ancestors is a list of ancestor resources (usually Gateways) that are associated with the policy, and the status of the policy with respect to each ancestor. When this policy attaches to a parent, the controller that manages the parent and the ancestors MUST add an entry to this list when the controller first sees the policy and SHOULD update the entry as appropriate when the relevant ancestor is modified. Note that choosing the relevant ancestor is left to the Policy designers; an important part of Policy design is designing the right object level at which to namespace this status. Note also that implementations MUST ONLY populate ancestor status for the Ancestor resources they are responsible for. Implementations MUST use the ControllerName field to uniquely identify the entries in this list that they are responsible for. Note that to achieve this, the list of PolicyAncestorStatus structs MUST be treated as a map with a composite key, made up of the AncestorRef and ControllerName fields combined. A maximum of 16 ancestors will be represented in this list. An empty list means the Policy is not relevant for any ancestors. If this slice is full, implementations MUST NOT add further entries. Instead they MUST consider the policy unimplementable and signal that on any related resources such as the ancestor that would be referenced here. For example, if this list was full on BackendTLSPolicy, no additional Gateways would be able to reference the Service targeted by the BackendTLSPolicy. items: description: |- PolicyAncestorStatus describes the status of a route with respect to an associated Ancestor. Ancestors refer to objects that are either the Target of a policy or above it in terms of object hierarchy. For example, if a policy targets a Service, the Policy's Ancestors are, in order, the Service, the HTTPRoute, the Gateway, and the GatewayClass. Almost always, in this hierarchy, the Gateway will be the most useful object to place Policy status on, so we recommend that implementations SHOULD use Gateway as the PolicyAncestorStatus object unless the designers have a _very_ good reason otherwise. In the context of policy attachment, the Ancestor is used to distinguish which resource results in a distinct application of this policy. For example, if a policy targets a Service, it may have a distinct result per attached Gateway. Policies targeting the same resource may have different effects depending on the ancestors of those resources. For example, different Gateways targeting the same Service may have different capabilities, especially if they have different underlying implementations. For example, in BackendTLSPolicy, the Policy attaches to a Service that is used as a backend in a HTTPRoute that is itself attached to a Gateway. In this case, the relevant object for status is the Gateway, and that is the ancestor object referred to in this status. Note that a parent is also an ancestor, so for objects where the parent is the relevant object for status, this struct SHOULD still be used. This struct is intended to be used in a slice that's effectively a map, with a composite key made up of the AncestorRef and the ControllerName. properties: ancestorRef: description: |- AncestorRef corresponds with a ParentRef in the spec that this PolicyAncestorStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object conditions: description: Conditions describes the status of the Policy with respect to the given Ancestor. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string required: - ancestorRef - conditions - controllerName type: object maxItems: 16 type: array x-kubernetes-list-type: atomic required: - ancestors type: object required: - spec type: object served: true storage: false subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/experimental/gateway.networking.k8s.io_gatewayclasses.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: experimental name: gatewayclasses.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: GatewayClass listKind: GatewayClassList plural: gatewayclasses shortNames: - gc singular: gatewayclass scope: Cluster versions: - additionalPrinterColumns: - jsonPath: .spec.controllerName name: Controller type: string - jsonPath: .status.conditions[?(@.type=="Accepted")].status name: Accepted type: string - jsonPath: .metadata.creationTimestamp name: Age type: date - jsonPath: .spec.description name: Description priority: 1 type: string name: v1 schema: openAPIV3Schema: description: |- GatewayClass describes a class of Gateways available to the user for creating Gateway resources. It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are not propagated down to existing Gateways. This recommendation is intended to limit the blast radius of changes to GatewayClass or associated parameters. If implementations choose to propagate GatewayClass changes to existing Gateways, that MUST be clearly documented by the implementation. Whenever one or more Gateways are using a GatewayClass, implementations SHOULD add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the associated GatewayClass. This ensures that a GatewayClass associated with a Gateway is not deleted while in use. GatewayClass is a Cluster level resource. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of GatewayClass. properties: controllerName: description: |- ControllerName is the name of the controller that is managing Gateways of this class. The value of this field MUST be a domain prefixed path. Example: "example.net/gateway-controller". This field is not mutable and cannot be empty. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string x-kubernetes-validations: - message: Value is immutable rule: self == oldSelf description: description: Description helps describe a GatewayClass with more details. maxLength: 64 type: string parametersRef: description: |- ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the GatewayClass. This is optional if the controller does not require any additional configuration. ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, or an implementation-specific custom resource. The resource can be cluster-scoped or namespace-scoped. If the referent cannot be found, refers to an unsupported kind, or when the data within that resource is malformed, the GatewayClass SHOULD be rejected with the "Accepted" status condition set to "False" and an "InvalidParameters" reason. A Gateway for this GatewayClass may provide its own `parametersRef`. When both are specified, the merging behavior is implementation specific. It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. Support: Implementation-specific properties: group: description: Group is the group of the referent. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. This field is required when referring to a Namespace-scoped resource and MUST be unset when referring to a Cluster-scoped resource. maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - group - kind - name type: object required: - controllerName type: object status: default: conditions: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted description: |- Status defines the current state of GatewayClass. Implementations MUST populate status on all GatewayClass resources which specify their controller name. properties: conditions: default: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted description: |- Conditions is the current status from the controller for this GatewayClass. Controllers should prefer to publish conditions using values of GatewayClassConditionType for the type of each Condition. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map supportedFeatures: description: |- SupportedFeatures is the set of features the GatewayClass support. It MUST be sorted in ascending alphabetical order by the Name key. items: properties: name: description: |- FeatureName is used to describe distinct features that are covered by conformance tests. type: string required: - name type: object maxItems: 64 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object required: - spec type: object served: true storage: true subresources: status: {} - additionalPrinterColumns: - jsonPath: .spec.controllerName name: Controller type: string - jsonPath: .status.conditions[?(@.type=="Accepted")].status name: Accepted type: string - jsonPath: .metadata.creationTimestamp name: Age type: date - jsonPath: .spec.description name: Description priority: 1 type: string name: v1beta1 schema: openAPIV3Schema: description: |- GatewayClass describes a class of Gateways available to the user for creating Gateway resources. It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are not propagated down to existing Gateways. This recommendation is intended to limit the blast radius of changes to GatewayClass or associated parameters. If implementations choose to propagate GatewayClass changes to existing Gateways, that MUST be clearly documented by the implementation. Whenever one or more Gateways are using a GatewayClass, implementations SHOULD add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the associated GatewayClass. This ensures that a GatewayClass associated with a Gateway is not deleted while in use. GatewayClass is a Cluster level resource. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of GatewayClass. properties: controllerName: description: |- ControllerName is the name of the controller that is managing Gateways of this class. The value of this field MUST be a domain prefixed path. Example: "example.net/gateway-controller". This field is not mutable and cannot be empty. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string x-kubernetes-validations: - message: Value is immutable rule: self == oldSelf description: description: Description helps describe a GatewayClass with more details. maxLength: 64 type: string parametersRef: description: |- ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the GatewayClass. This is optional if the controller does not require any additional configuration. ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, or an implementation-specific custom resource. The resource can be cluster-scoped or namespace-scoped. If the referent cannot be found, refers to an unsupported kind, or when the data within that resource is malformed, the GatewayClass SHOULD be rejected with the "Accepted" status condition set to "False" and an "InvalidParameters" reason. A Gateway for this GatewayClass may provide its own `parametersRef`. When both are specified, the merging behavior is implementation specific. It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. Support: Implementation-specific properties: group: description: Group is the group of the referent. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. This field is required when referring to a Namespace-scoped resource and MUST be unset when referring to a Cluster-scoped resource. maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - group - kind - name type: object required: - controllerName type: object status: default: conditions: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted description: |- Status defines the current state of GatewayClass. Implementations MUST populate status on all GatewayClass resources which specify their controller name. properties: conditions: default: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted description: |- Conditions is the current status from the controller for this GatewayClass. Controllers should prefer to publish conditions using values of GatewayClassConditionType for the type of each Condition. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map supportedFeatures: description: |- SupportedFeatures is the set of features the GatewayClass support. It MUST be sorted in ascending alphabetical order by the Name key. items: properties: name: description: |- FeatureName is used to describe distinct features that are covered by conformance tests. type: string required: - name type: object maxItems: 64 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object required: - spec type: object served: true storage: false subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/experimental/gateway.networking.k8s.io_gateways.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: experimental name: gateways.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: Gateway listKind: GatewayList plural: gateways shortNames: - gtw singular: gateway scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .spec.gatewayClassName name: Class type: string - jsonPath: .status.addresses[*].value name: Address type: string - jsonPath: .status.conditions[?(@.type=="Programmed")].status name: Programmed type: string - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1 schema: openAPIV3Schema: description: |- Gateway represents an instance of a service-traffic handling infrastructure by binding Listeners to a set of IP addresses. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of Gateway. properties: addresses: description: |- Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST indicate this in an associated entry in GatewayStatus.Conditions. The Addresses field represents a request for the address(es) on the "outside of the Gateway", that traffic bound for this Gateway will use. This could be the IP address or hostname of an external load balancer or other networking infrastructure, or some other address that traffic will be sent to. If no Addresses are specified, the implementation MAY schedule the Gateway in an implementation-specific manner, assigning an appropriate set of Addresses. The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. Support: Extended items: description: GatewaySpecAddress describes an address that can be bound to a Gateway. oneOf: - properties: type: enum: - IPAddress value: anyOf: - format: ipv4 - format: ipv6 - properties: type: not: enum: - IPAddress properties: type: default: IPAddress description: Type of the address. maxLength: 253 minLength: 1 pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string value: description: |- When a value is unspecified, an implementation SHOULD automatically assign an address matching the requested type if possible. If an implementation does not support an empty value, they MUST set the "Programmed" condition in status to False with a reason of "AddressNotAssigned". Examples: `1.2.3.4`, `128::1`, `my-ip-address`. maxLength: 253 type: string type: object x-kubernetes-validations: - message: Hostname value must be empty or contain only valid characters (matching ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) rule: 'self.type == ''Hostname'' ? (!has(self.value) || self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$""")): true' maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: IPAddress values must be unique rule: 'self.all(a1, a1.type == ''IPAddress'' && has(a1.value) ? self.exists_one(a2, a2.type == a1.type && has(a2.value) && a2.value == a1.value) : true )' - message: Hostname values must be unique rule: 'self.all(a1, a1.type == ''Hostname'' && has(a1.value) ? self.exists_one(a2, a2.type == a1.type && has(a2.value) && a2.value == a1.value) : true )' allowedListeners: description: |- AllowedListeners defines which ListenerSets can be attached to this Gateway. The default value is to allow no ListenerSets. properties: namespaces: default: from: None description: |- Namespaces defines which namespaces ListenerSets can be attached to this Gateway. The default value is to allow no ListenerSets. properties: from: default: None description: |- From indicates where ListenerSets can attach to this Gateway. Possible values are: * Same: Only ListenerSets in the same namespace may be attached to this Gateway. * Selector: ListenerSets in namespaces selected by the selector may be attached to this Gateway. * All: ListenerSets in all namespaces may be attached to this Gateway. * None: Only listeners defined in the Gateway's spec are allowed The default value None enum: - All - Selector - Same - None type: string selector: description: |- Selector must be specified when From is set to "Selector". In that case, only ListenerSets in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of "From". properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic type: object type: object defaultScope: description: |- DefaultScope, when set, configures the Gateway as a default Gateway, meaning it will dynamically and implicitly have Routes (e.g. HTTPRoute) attached to it, according to the scope configured here. If unset (the default) or set to None, the Gateway will not act as a default Gateway; if set, the Gateway will claim any Route with a matching scope set in its UseDefaultGateway field, subject to the usual rules about which routes the Gateway can attach to. Think carefully before using this functionality! While the normal rules about which Route can apply are still enforced, it is simply easier for the wrong Route to be accidentally attached to this Gateway in this configuration. If the Gateway operator is not also the operator in control of the scope (e.g. namespace) with tight controls and checks on what kind of workloads and Routes get added in that scope, we strongly recommend not using this just because it seems convenient, and instead stick to direct Route attachment. enum: - All - None type: string gatewayClassName: description: |- GatewayClassName used for this Gateway. This is the name of a GatewayClass resource. maxLength: 253 minLength: 1 type: string infrastructure: description: |- Infrastructure defines infrastructure level attributes about this Gateway instance. Support: Extended properties: annotations: additionalProperties: description: |- AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. maxLength: 4096 minLength: 0 type: string description: |- Annotations that SHOULD be applied to any resources created in response to this Gateway. For implementations creating other Kubernetes objects, this should be the `metadata.annotations` field on resources. For other implementations, this refers to any relevant (implementation specific) "annotations" concepts. An implementation may chose to add additional implementation-specific annotations as they see fit. Support: Extended maxProperties: 8 type: object x-kubernetes-validations: - message: Annotation keys must be in the form of an optional DNS subdomain prefix followed by a required name segment of up to 63 characters. rule: self.all(key, key.matches(r"""^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9]$""")) - message: If specified, the annotation key's prefix must be a DNS subdomain not longer than 253 characters in total. rule: self.all(key, key.split("/")[0].size() < 253) labels: additionalProperties: description: |- LabelValue is the value of a label in the Gateway API. This is used for validation of maps such as Gateway infrastructure labels. This matches the Kubernetes label validation rules: * must be 63 characters or less (can be empty), * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. Valid values include: * MyValue * my.name * 123-my-value maxLength: 63 minLength: 0 pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels that SHOULD be applied to any resources created in response to this Gateway. For implementations creating other Kubernetes objects, this should be the `metadata.labels` field on resources. For other implementations, this refers to any relevant (implementation specific) "labels" concepts. An implementation may chose to add additional implementation-specific labels as they see fit. If an implementation maps these labels to Pods, or any other resource that would need to be recreated when labels change, it SHOULD clearly warn about this behavior in documentation. Support: Extended maxProperties: 8 type: object x-kubernetes-validations: - message: Label keys must be in the form of an optional DNS subdomain prefix followed by a required name segment of up to 63 characters. rule: self.all(key, key.matches(r"""^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9]$""")) - message: If specified, the label key's prefix must be a DNS subdomain not longer than 253 characters in total. rule: self.all(key, key.split("/")[0].size() < 253) parametersRef: description: |- ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the Gateway. This is optional if the controller does not require any additional configuration. This follows the same semantics as GatewayClass's `parametersRef`, but on a per-Gateway basis The Gateway's GatewayClass may provide its own `parametersRef`. When both are specified, the merging behavior is implementation specific. It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. If the referent cannot be found, refers to an unsupported kind, or when the data within that resource is malformed, the Gateway SHOULD be rejected with the "Accepted" status condition set to "False" and an "InvalidParameters" reason. Support: Implementation-specific properties: group: description: Group is the group of the referent. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object type: object listeners: description: |- Listeners associated with this Gateway. Listeners define logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. ## Distinct Listeners Each Listener in a set of Listeners (for example, in a single Gateway) MUST be _distinct_, in that a traffic flow MUST be able to be assigned to exactly one listener. (This section uses "set of Listeners" rather than "Listeners in a single Gateway" because implementations MAY merge configuration from multiple Gateways onto a single data plane, and these rules _also_ apply in that case). Practically, this means that each listener in a set MUST have a unique combination of Port, Protocol, and, if supported by the protocol, Hostname. Some combinations of port, protocol, and TLS settings are considered Core support and MUST be supported by implementations based on the objects they support: HTTPRoute 1. HTTPRoute, Port: 80, Protocol: HTTP 2. HTTPRoute, Port: 443, Protocol: HTTPS, TLS Mode: Terminate, TLS keypair provided TLSRoute 1. TLSRoute, Port: 443, Protocol: TLS, TLS Mode: Passthrough "Distinct" Listeners have the following property: **The implementation can match inbound requests to a single distinct Listener**. When multiple Listeners share values for fields (for example, two Listeners with the same Port value), the implementation can match requests to only one of the Listeners using other Listener fields. When multiple listeners have the same value for the Protocol field, then each of the Listeners with matching Protocol values MUST have different values for other fields. The set of fields that MUST be different for a Listener differs per protocol. The following rules define the rules for what fields MUST be considered for Listeners to be distinct with each protocol currently defined in the Gateway API spec. The set of listeners that all share a protocol value MUST have _different_ values for _at least one_ of these fields to be distinct: * **HTTP, HTTPS, TLS**: Port, Hostname * **TCP, UDP**: Port One **very** important rule to call out involves what happens when an implementation: * Supports TCP protocol Listeners, as well as HTTP, HTTPS, or TLS protocol Listeners, and * sees HTTP, HTTPS, or TLS protocols with the same `port` as one with TCP Protocol. In this case all the Listeners that share a port with the TCP Listener are not distinct and so MUST NOT be accepted. If an implementation does not support TCP Protocol Listeners, then the previous rule does not apply, and the TCP Listeners SHOULD NOT be accepted. Note that the `tls` field is not used for determining if a listener is distinct, because Listeners that _only_ differ on TLS config will still conflict in all cases. ### Listeners that are distinct only by Hostname When the Listeners are distinct based only on Hostname, inbound request hostnames MUST match from the most specific to least specific Hostname values to choose the correct Listener and its associated set of Routes. Exact matches MUST be processed before wildcard matches, and wildcard matches MUST be processed before fallback (empty Hostname value) matches. For example, `"foo.example.com"` takes precedence over `"*.example.com"`, and `"*.example.com"` takes precedence over `""`. Additionally, if there are multiple wildcard entries, more specific wildcard entries must be processed before less specific wildcard entries. For example, `"*.foo.example.com"` takes precedence over `"*.example.com"`. The precise definition here is that the higher the number of dots in the hostname to the right of the wildcard character, the higher the precedence. The wildcard character will match any number of characters _and dots_ to the left, however, so `"*.example.com"` will match both `"foo.bar.example.com"` _and_ `"bar.example.com"`. ## Handling indistinct Listeners If a set of Listeners contains Listeners that are not distinct, then those Listeners are _Conflicted_, and the implementation MUST set the "Conflicted" condition in the Listener Status to "True". The words "indistinct" and "conflicted" are considered equivalent for the purpose of this documentation. Implementations MAY choose to accept a Gateway with some Conflicted Listeners only if they only accept the partial Listener set that contains no Conflicted Listeners. Specifically, an implementation MAY accept a partial Listener set subject to the following rules: * The implementation MUST NOT pick one conflicting Listener as the winner. ALL indistinct Listeners must not be accepted for processing. * At least one distinct Listener MUST be present, or else the Gateway effectively contains _no_ Listeners, and must be rejected from processing as a whole. The implementation MUST set a "ListenersNotValid" condition on the Gateway Status when the Gateway contains Conflicted Listeners whether or not they accept the Gateway. That Condition SHOULD clearly indicate in the Message which Listeners are conflicted, and which are Accepted. Additionally, the Listener status for those listeners SHOULD indicate which Listeners are conflicted and not Accepted. ## General Listener behavior Note that, for all distinct Listeners, requests SHOULD match at most one Listener. For example, if Listeners are defined for "foo.example.com" and "*.example.com", a request to "foo.example.com" SHOULD only be routed using routes attached to the "foo.example.com" Listener (and not the "*.example.com" Listener). This concept is known as "Listener Isolation", and it is an Extended feature of Gateway API. Implementations that do not support Listener Isolation MUST clearly document this, and MUST NOT claim support for the `GatewayHTTPListenerIsolation` feature. Implementations that _do_ support Listener Isolation SHOULD claim support for the Extended `GatewayHTTPListenerIsolation` feature and pass the associated conformance tests. ## Compatible Listeners A Gateway's Listeners are considered _compatible_ if: 1. They are distinct. 2. The implementation can serve them in compliance with the Addresses requirement that all Listeners are available on all assigned addresses. Compatible combinations in Extended support are expected to vary across implementations. A combination that is compatible for one implementation may not be compatible for another. For example, an implementation that cannot serve both TCP and UDP listeners on the same address, or cannot mix HTTPS and generic TLS listens on the same port would not consider those cases compatible, even though they are distinct. Implementations MAY merge separate Gateways onto a single set of Addresses if all Listeners across all Gateways are compatible. In a future release the MinItems=1 requirement MAY be dropped. Support: Core items: description: |- Listener embodies the concept of a logical endpoint where a Gateway accepts network connections. properties: allowedRoutes: default: namespaces: from: Same description: |- AllowedRoutes defines the types of routes that MAY be attached to a Listener and the trusted namespaces where those Route resources MAY be present. Although a client request may match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: * The most specific match as defined by the Route type. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of "2020-09-08 01:02:03" is given precedence over a Route with a creation timestamp of "2020-09-08 01:02:04". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. All valid rules within a Route attached to this Listener should be implemented. Invalid Route rules can be ignored (sometimes that will mean the full Route). If a Route rule transitions from valid to invalid, support for that Route rule should be dropped to ensure consistency. For example, even if a filter specified by a Route rule is invalid, the rest of the rules within that Route should still be supported. Support: Core properties: kinds: description: |- Kinds specifies the groups and kinds of Routes that are allowed to bind to this Gateway Listener. When unspecified or empty, the kinds of Routes selected are determined using the Listener protocol. A RouteGroupKind MUST correspond to kinds of Routes that are compatible with the application protocol specified in the Listener's Protocol field. If an implementation does not support or recognize this resource type, it MUST set the "ResolvedRefs" condition to False for this Listener with the "InvalidRouteKinds" reason. Support: Core items: description: RouteGroupKind indicates the group and kind of a Route resource. properties: group: default: gateway.networking.k8s.io description: Group is the group of the Route. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is the kind of the Route. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string required: - kind type: object maxItems: 8 type: array x-kubernetes-list-type: atomic namespaces: default: from: Same description: |- Namespaces indicates namespaces from which Routes may be attached to this Listener. This is restricted to the namespace of this Gateway by default. Support: Core properties: from: default: Same description: |- From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. Support: Core enum: - All - Selector - Same type: string selector: description: |- Selector must be specified when From is set to "Selector". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of "From". Support: Core properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic type: object type: object hostname: description: |- Hostname specifies the virtual hostname to match for protocol types that define this concept. When unspecified, all hostnames are matched. This field is ignored for protocols that don't require hostname based matching. Implementations MUST apply Hostname matching appropriately for each of the following protocols: * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. * HTTPS: The Listener Hostname SHOULD match both the SNI and Host header. Note that this does not require the SNI and Host header to be the same. The semantics of this are described in more detail below. To ensure security, Section 11.1 of RFC-6066 emphasizes that server implementations that rely on SNI hostname matching MUST also verify hostnames within the application protocol. Section 9.1.2 of RFC-7540 provides a mechanism for servers to reject the reuse of a connection by responding with the HTTP 421 Misdirected Request status code. This indicates that the origin server has rejected the request because it appears to have been misdirected. To detect misdirected requests, Gateways SHOULD match the authority of the requests with all the SNI hostname(s) configured across all the Gateway Listeners on the same port and protocol: * If another Listener has an exact match or more specific wildcard entry, the Gateway SHOULD return a 421. * If the current Listener (selected by SNI matching during ClientHello) does not match the Host: * If another Listener does match the Host, the Gateway SHOULD return a 421. * If no other Listener matches the Host, the Gateway MUST return a 404. For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, there MUST be an intersection between the values for a Route to be accepted. For more information, refer to the Route specific Hostnames documentation. Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. Support: Core maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string name: description: |- Name is the name of the Listener. This name MUST be unique within a Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string port: description: |- Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. Support: Core format: int32 maximum: 65535 minimum: 1 type: integer protocol: description: |- Protocol specifies the network protocol this listener expects to receive. Support: Core maxLength: 255 minLength: 1 pattern: ^[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ type: string tls: description: |- TLS is the TLS configuration for the Listener. This field is required if the Protocol field is "HTTPS" or "TLS". It is invalid to set this field if the Protocol field is "HTTP", "TCP", or "UDP". The association of SNIs to Certificate defined in ListenerTLSConfig is defined based on the Hostname field for this listener. The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. Support: Core properties: certificateRefs: description: |- CertificateRefs contains a series of references to Kubernetes objects that contains TLS certificates and private keys. These certificates are used to establish a TLS handshake for requests that match the hostname of the associated listener. A single CertificateRef to a Kubernetes Secret has "Core" support. Implementations MAY choose to support attaching multiple certificates to a Listener, but this behavior is implementation-specific. References to a resource in different namespace are invalid UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the "ResolvedRefs" condition MUST be set to False for this listener with the "RefNotPermitted" reason. This field is required to have at least one element when the mode is set to "Terminate" (default) and is optional otherwise. CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls Support: Implementation-specific (More than one reference or other resource types) items: description: |- SecretObjectReference identifies an API object including its namespace, defaulting to Secret. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Secret description: Kind is kind of the referent. For example "Secret". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - name type: object maxItems: 64 type: array x-kubernetes-list-type: atomic mode: default: Terminate description: |- Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificates to be specified in some way, such as populating the certificateRefs field. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. The certificateRefs field is ignored in this mode. Support: Core enum: - Terminate - Passthrough type: string options: additionalProperties: description: |- AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. maxLength: 4096 minLength: 0 type: string description: |- Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. Support: Implementation-specific maxProperties: 16 type: object type: object x-kubernetes-validations: - message: certificateRefs or options must be specified when mode is Terminate rule: 'self.mode == ''Terminate'' ? size(self.certificateRefs) > 0 || size(self.options) > 0 : true' required: - name - port - protocol type: object maxItems: 64 minItems: 1 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map x-kubernetes-validations: - message: tls must not be specified for protocols ['HTTP', 'TCP', 'UDP'] rule: 'self.all(l, l.protocol in [''HTTP'', ''TCP'', ''UDP''] ? !has(l.tls) : true)' - message: tls mode must be Terminate for protocol HTTPS rule: 'self.all(l, (l.protocol == ''HTTPS'' && has(l.tls)) ? (l.tls.mode == '''' || l.tls.mode == ''Terminate'') : true)' - message: tls mode must be set for protocol TLS rule: 'self.all(l, (l.protocol == ''TLS'' ? has(l.tls) && has(l.tls.mode) && l.tls.mode != '''' : true))' - message: hostname must not be specified for protocols ['TCP', 'UDP'] rule: 'self.all(l, l.protocol in [''TCP'', ''UDP''] ? (!has(l.hostname) || l.hostname == '''') : true)' - message: Listener name must be unique within the Gateway rule: self.all(l1, self.exists_one(l2, l1.name == l2.name)) - message: Combination of port, protocol and hostname must be unique for each listener rule: 'self.all(l1, self.exists_one(l2, l1.port == l2.port && l1.protocol == l2.protocol && (has(l1.hostname) && has(l2.hostname) ? l1.hostname == l2.hostname : !has(l1.hostname) && !has(l2.hostname))))' tls: description: |- TLS specifies frontend and backend tls configuration for entire gateway. Support: Extended properties: backend: description: |- Backend describes TLS configuration for gateway when connecting to backends. Note that this contains only details for the Gateway as a TLS client, and does _not_ imply behavior about how to choose which backend should get a TLS connection. That is determined by the presence of a BackendTLSPolicy. Support: Core properties: clientCertificateRef: description: |- ClientCertificateRef references an object that contains a client certificate and its associated private key. It can reference standard Kubernetes resources, i.e., Secret, or implementation-specific custom resources. A ClientCertificateRef is considered invalid if: * It refers to a resource that cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured (e.g., a Secret does not contain the keys named `tls.crt` and `tls.key`). In this case, the `ResolvedRefs` condition on the Gateway MUST be set to False with the Reason `InvalidClientCertificateRef` and the Message of the Condition MUST indicate why the reference is invalid. * It refers to a resource in another namespace UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the `ResolvedRefs` condition on the Gateway MUST be set to False with the Reason `RefNotPermitted`. Implementations MAY choose to perform further validation of the certificate content (e.g., checking expiry or enforcing specific formats). In such cases, an implementation-specific Reason and Message MUST be set. Support: Core - Reference to a Kubernetes TLS Secret (with the type `kubernetes.io/tls`). Support: Implementation-specific - Other resource kinds or Secrets with a different type (e.g., `Opaque`). properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Secret description: Kind is kind of the referent. For example "Secret". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - name type: object type: object frontend: description: |- Frontend describes TLS config when client connects to Gateway. Support: Core properties: default: description: |- Default specifies the default client certificate validation configuration for all Listeners handling HTTPS traffic, unless a per-port configuration is defined. support: Core properties: validation: description: |- Validation holds configuration information for validating the frontend (client). Setting this field will result in mutual authentication when connecting to the gateway. In browsers this may result in a dialog appearing that requests a user to specify the client certificate. The maximum depth of a certificate chain accepted in verification is Implementation specific. Support: Core properties: caCertificateRefs: description: |- CACertificateRefs contains one or more references to Kubernetes objects that contain a PEM-encoded TLS CA certificate bundle, which is used as a trust anchor to validate the certificates presented by the client. A CACertificateRef is invalid if: * It refers to a resource that cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured (e.g., a ConfigMap does not contain a key named `ca.crt`). In this case, the Reason on all matching HTTPS listeners must be set to `InvalidCACertificateRef` and the Message of the Condition must indicate which reference is invalid and why. * It refers to an unknown or unsupported kind of resource. In this case, the Reason on all matching HTTPS listeners must be set to `InvalidCACertificateKind` and the Message of the Condition must explain which kind of resource is unknown or unsupported. * It refers to a resource in another namespace UNLESS there is a ReferenceGrant in the target namespace that allows the CA certificate to be attached. If a ReferenceGrant does not allow this reference, the `ResolvedRefs` on all matching HTTPS listeners condition MUST be set with the Reason `RefNotPermitted`. Implementations MAY choose to perform further validation of the certificate content (e.g., checking expiry or enforcing specific formats). In such cases, an implementation-specific Reason and Message MUST be set. In all cases, the implementation MUST ensure that the `ResolvedRefs` condition is set to `status: False` on all targeted listeners (i.e., listeners serving HTTPS on a matching port). The condition MUST include a Reason and Message that indicate the cause of the error. If ALL CACertificateRefs are invalid, the implementation MUST also ensure the `Accepted` condition on the listener is set to `status: False`, with the Reason `NoValidCACertificate`. Implementations MAY choose to support attaching multiple CA certificates to a listener, but this behavior is implementation-specific. Support: Core - A single reference to a Kubernetes ConfigMap, with the CA certificate in a key named `ca.crt`. Support: Implementation-specific - More than one reference, other kinds of resources, or a single reference that includes multiple certificates. items: description: |- ObjectReference identifies an API object including its namespace. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When set to the empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "ConfigMap" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - group - kind - name type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-type: atomic mode: default: AllowValidOnly description: |- FrontendValidationMode defines the mode for validating the client certificate. There are two possible modes: - AllowValidOnly: In this mode, the gateway will accept connections only if the client presents a valid certificate. This certificate must successfully pass validation against the CA certificates specified in `CACertificateRefs`. - AllowInsecureFallback: In this mode, the gateway will accept connections even if the client certificate is not presented or fails verification. This approach delegates client authorization to the backend and introduce a significant security risk. It should be used in testing environments or on a temporary basis in non-testing environments. Defaults to AllowValidOnly. Support: Core enum: - AllowValidOnly - AllowInsecureFallback type: string required: - caCertificateRefs type: object type: object perPort: description: |- PerPort specifies tls configuration assigned per port. Per port configuration is optional. Once set this configuration overrides the default configuration for all Listeners handling HTTPS traffic that match this port. Each override port requires a unique TLS configuration. support: Core items: properties: port: description: |- The Port indicates the Port Number to which the TLS configuration will be applied. This configuration will be applied to all Listeners handling HTTPS traffic that match this port. Support: Core format: int32 maximum: 65535 minimum: 1 type: integer tls: description: |- TLS store the configuration that will be applied to all Listeners handling HTTPS traffic and matching given port. Support: Core properties: validation: description: |- Validation holds configuration information for validating the frontend (client). Setting this field will result in mutual authentication when connecting to the gateway. In browsers this may result in a dialog appearing that requests a user to specify the client certificate. The maximum depth of a certificate chain accepted in verification is Implementation specific. Support: Core properties: caCertificateRefs: description: |- CACertificateRefs contains one or more references to Kubernetes objects that contain a PEM-encoded TLS CA certificate bundle, which is used as a trust anchor to validate the certificates presented by the client. A CACertificateRef is invalid if: * It refers to a resource that cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured (e.g., a ConfigMap does not contain a key named `ca.crt`). In this case, the Reason on all matching HTTPS listeners must be set to `InvalidCACertificateRef` and the Message of the Condition must indicate which reference is invalid and why. * It refers to an unknown or unsupported kind of resource. In this case, the Reason on all matching HTTPS listeners must be set to `InvalidCACertificateKind` and the Message of the Condition must explain which kind of resource is unknown or unsupported. * It refers to a resource in another namespace UNLESS there is a ReferenceGrant in the target namespace that allows the CA certificate to be attached. If a ReferenceGrant does not allow this reference, the `ResolvedRefs` on all matching HTTPS listeners condition MUST be set with the Reason `RefNotPermitted`. Implementations MAY choose to perform further validation of the certificate content (e.g., checking expiry or enforcing specific formats). In such cases, an implementation-specific Reason and Message MUST be set. In all cases, the implementation MUST ensure that the `ResolvedRefs` condition is set to `status: False` on all targeted listeners (i.e., listeners serving HTTPS on a matching port). The condition MUST include a Reason and Message that indicate the cause of the error. If ALL CACertificateRefs are invalid, the implementation MUST also ensure the `Accepted` condition on the listener is set to `status: False`, with the Reason `NoValidCACertificate`. Implementations MAY choose to support attaching multiple CA certificates to a listener, but this behavior is implementation-specific. Support: Core - A single reference to a Kubernetes ConfigMap, with the CA certificate in a key named `ca.crt`. Support: Implementation-specific - More than one reference, other kinds of resources, or a single reference that includes multiple certificates. items: description: |- ObjectReference identifies an API object including its namespace. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When set to the empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "ConfigMap" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - group - kind - name type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-type: atomic mode: default: AllowValidOnly description: |- FrontendValidationMode defines the mode for validating the client certificate. There are two possible modes: - AllowValidOnly: In this mode, the gateway will accept connections only if the client presents a valid certificate. This certificate must successfully pass validation against the CA certificates specified in `CACertificateRefs`. - AllowInsecureFallback: In this mode, the gateway will accept connections even if the client certificate is not presented or fails verification. This approach delegates client authorization to the backend and introduce a significant security risk. It should be used in testing environments or on a temporary basis in non-testing environments. Defaults to AllowValidOnly. Support: Core enum: - AllowValidOnly - AllowInsecureFallback type: string required: - caCertificateRefs type: object type: object required: - port - tls type: object maxItems: 64 type: array x-kubernetes-list-map-keys: - port x-kubernetes-list-type: map x-kubernetes-validations: - message: Port for TLS configuration must be unique within the Gateway rule: self.all(t1, self.exists_one(t2, t1.port == t2.port)) required: - default type: object type: object required: - gatewayClassName - listeners type: object status: default: conditions: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Programmed description: Status defines the current state of Gateway. properties: addresses: description: |- Addresses lists the network addresses that have been bound to the Gateway. This list may differ from the addresses provided in the spec under some conditions: * no addresses are specified, all addresses are dynamically assigned * a combination of specified and dynamic addresses are assigned * a specified address was unusable (e.g. already in use) items: description: GatewayStatusAddress describes a network address that is bound to a Gateway. oneOf: - properties: type: enum: - IPAddress value: anyOf: - format: ipv4 - format: ipv6 - properties: type: not: enum: - IPAddress properties: type: default: IPAddress description: Type of the address. maxLength: 253 minLength: 1 pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string value: description: |- Value of the address. The validity of the values will depend on the type and support by the controller. Examples: `1.2.3.4`, `128::1`, `my-ip-address`. maxLength: 253 minLength: 1 type: string required: - value type: object x-kubernetes-validations: - message: Hostname value must only contain valid characters (matching ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) rule: 'self.type == ''Hostname'' ? self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"""): true' maxItems: 16 type: array x-kubernetes-list-type: atomic attachedListenerSets: description: |- AttachedListenerSets represents the total number of ListenerSets that have been successfully attached to this Gateway. A ListenerSet is successfully attached to a Gateway when all the following conditions are met: - The ListenerSet is selected by the Gateway's AllowedListeners field - The ListenerSet has a valid ParentRef selecting the Gateway - The ListenerSet's status has the condition "Accepted: true" Uses for this field include troubleshooting AttachedListenerSets attachment and measuring blast radius/impact of changes to a Gateway. format: int32 type: integer conditions: default: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Programmed description: |- Conditions describe the current conditions of the Gateway. Implementations should prefer to express Gateway conditions using the `GatewayConditionType` and `GatewayConditionReason` constants so that operators and tools can converge on a common vocabulary to describe Gateway state. Known condition types are: * "Accepted" * "Programmed" * "Ready" items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map listeners: description: Listeners provide status for each unique listener port defined in the Spec. items: description: ListenerStatus is the status associated with a Listener. properties: attachedRoutes: description: |- AttachedRoutes represents the total number of Routes that have been successfully attached to this Listener. Successful attachment of a Route to a Listener is based solely on the combination of the AllowedRoutes field on the corresponding Listener and the Route's ParentRefs field. A Route is successfully attached to a Listener when it is selected by the Listener's AllowedRoutes field AND the Route has a valid ParentRef selecting the whole Gateway resource or a specific Listener as a parent resource (more detail on attachment semantics can be found in the documentation on the various Route kinds ParentRefs fields). Listener or Route status does not impact successful attachment, i.e. the AttachedRoutes field count MUST be set for Listeners, even if the Accepted condition of an individual Listener is set to "False". The AttachedRoutes number represents the number of Routes with the Accepted condition set to "True" that have been attached to this Listener. Routes with any other value for the Accepted condition MUST NOT be included in this count. Uses for this field include troubleshooting Route attachment and measuring blast radius/impact of changes to a Listener. format: int32 type: integer conditions: description: Conditions describe the current condition of this listener. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map name: description: Name is the name of the Listener that this status corresponds to. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string supportedKinds: description: |- SupportedKinds is the list indicating the Kinds supported by this listener. This MUST represent the kinds supported by an implementation for that Listener configuration. If kinds are specified in Spec that are not supported, they MUST NOT appear in this list and an implementation MUST set the "ResolvedRefs" condition to "False" with the "InvalidRouteKinds" reason. If both valid and invalid Route kinds are specified, the implementation MUST reference the valid Route kinds that have been specified. items: description: RouteGroupKind indicates the group and kind of a Route resource. properties: group: default: gateway.networking.k8s.io description: Group is the group of the Route. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is the kind of the Route. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string required: - kind type: object maxItems: 8 type: array x-kubernetes-list-type: atomic required: - attachedRoutes - conditions - name type: object maxItems: 64 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object required: - spec type: object served: true storage: true subresources: status: {} - additionalPrinterColumns: - jsonPath: .spec.gatewayClassName name: Class type: string - jsonPath: .status.addresses[*].value name: Address type: string - jsonPath: .status.conditions[?(@.type=="Programmed")].status name: Programmed type: string - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1beta1 schema: openAPIV3Schema: description: |- Gateway represents an instance of a service-traffic handling infrastructure by binding Listeners to a set of IP addresses. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of Gateway. properties: addresses: description: |- Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST indicate this in an associated entry in GatewayStatus.Conditions. The Addresses field represents a request for the address(es) on the "outside of the Gateway", that traffic bound for this Gateway will use. This could be the IP address or hostname of an external load balancer or other networking infrastructure, or some other address that traffic will be sent to. If no Addresses are specified, the implementation MAY schedule the Gateway in an implementation-specific manner, assigning an appropriate set of Addresses. The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. Support: Extended items: description: GatewaySpecAddress describes an address that can be bound to a Gateway. oneOf: - properties: type: enum: - IPAddress value: anyOf: - format: ipv4 - format: ipv6 - properties: type: not: enum: - IPAddress properties: type: default: IPAddress description: Type of the address. maxLength: 253 minLength: 1 pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string value: description: |- When a value is unspecified, an implementation SHOULD automatically assign an address matching the requested type if possible. If an implementation does not support an empty value, they MUST set the "Programmed" condition in status to False with a reason of "AddressNotAssigned". Examples: `1.2.3.4`, `128::1`, `my-ip-address`. maxLength: 253 type: string type: object x-kubernetes-validations: - message: Hostname value must be empty or contain only valid characters (matching ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) rule: 'self.type == ''Hostname'' ? (!has(self.value) || self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$""")): true' maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: IPAddress values must be unique rule: 'self.all(a1, a1.type == ''IPAddress'' && has(a1.value) ? self.exists_one(a2, a2.type == a1.type && has(a2.value) && a2.value == a1.value) : true )' - message: Hostname values must be unique rule: 'self.all(a1, a1.type == ''Hostname'' && has(a1.value) ? self.exists_one(a2, a2.type == a1.type && has(a2.value) && a2.value == a1.value) : true )' allowedListeners: description: |- AllowedListeners defines which ListenerSets can be attached to this Gateway. The default value is to allow no ListenerSets. properties: namespaces: default: from: None description: |- Namespaces defines which namespaces ListenerSets can be attached to this Gateway. The default value is to allow no ListenerSets. properties: from: default: None description: |- From indicates where ListenerSets can attach to this Gateway. Possible values are: * Same: Only ListenerSets in the same namespace may be attached to this Gateway. * Selector: ListenerSets in namespaces selected by the selector may be attached to this Gateway. * All: ListenerSets in all namespaces may be attached to this Gateway. * None: Only listeners defined in the Gateway's spec are allowed The default value None enum: - All - Selector - Same - None type: string selector: description: |- Selector must be specified when From is set to "Selector". In that case, only ListenerSets in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of "From". properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic type: object type: object defaultScope: description: |- DefaultScope, when set, configures the Gateway as a default Gateway, meaning it will dynamically and implicitly have Routes (e.g. HTTPRoute) attached to it, according to the scope configured here. If unset (the default) or set to None, the Gateway will not act as a default Gateway; if set, the Gateway will claim any Route with a matching scope set in its UseDefaultGateway field, subject to the usual rules about which routes the Gateway can attach to. Think carefully before using this functionality! While the normal rules about which Route can apply are still enforced, it is simply easier for the wrong Route to be accidentally attached to this Gateway in this configuration. If the Gateway operator is not also the operator in control of the scope (e.g. namespace) with tight controls and checks on what kind of workloads and Routes get added in that scope, we strongly recommend not using this just because it seems convenient, and instead stick to direct Route attachment. enum: - All - None type: string gatewayClassName: description: |- GatewayClassName used for this Gateway. This is the name of a GatewayClass resource. maxLength: 253 minLength: 1 type: string infrastructure: description: |- Infrastructure defines infrastructure level attributes about this Gateway instance. Support: Extended properties: annotations: additionalProperties: description: |- AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. maxLength: 4096 minLength: 0 type: string description: |- Annotations that SHOULD be applied to any resources created in response to this Gateway. For implementations creating other Kubernetes objects, this should be the `metadata.annotations` field on resources. For other implementations, this refers to any relevant (implementation specific) "annotations" concepts. An implementation may chose to add additional implementation-specific annotations as they see fit. Support: Extended maxProperties: 8 type: object x-kubernetes-validations: - message: Annotation keys must be in the form of an optional DNS subdomain prefix followed by a required name segment of up to 63 characters. rule: self.all(key, key.matches(r"""^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9]$""")) - message: If specified, the annotation key's prefix must be a DNS subdomain not longer than 253 characters in total. rule: self.all(key, key.split("/")[0].size() < 253) labels: additionalProperties: description: |- LabelValue is the value of a label in the Gateway API. This is used for validation of maps such as Gateway infrastructure labels. This matches the Kubernetes label validation rules: * must be 63 characters or less (can be empty), * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. Valid values include: * MyValue * my.name * 123-my-value maxLength: 63 minLength: 0 pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels that SHOULD be applied to any resources created in response to this Gateway. For implementations creating other Kubernetes objects, this should be the `metadata.labels` field on resources. For other implementations, this refers to any relevant (implementation specific) "labels" concepts. An implementation may chose to add additional implementation-specific labels as they see fit. If an implementation maps these labels to Pods, or any other resource that would need to be recreated when labels change, it SHOULD clearly warn about this behavior in documentation. Support: Extended maxProperties: 8 type: object x-kubernetes-validations: - message: Label keys must be in the form of an optional DNS subdomain prefix followed by a required name segment of up to 63 characters. rule: self.all(key, key.matches(r"""^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9]$""")) - message: If specified, the label key's prefix must be a DNS subdomain not longer than 253 characters in total. rule: self.all(key, key.split("/")[0].size() < 253) parametersRef: description: |- ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the Gateway. This is optional if the controller does not require any additional configuration. This follows the same semantics as GatewayClass's `parametersRef`, but on a per-Gateway basis The Gateway's GatewayClass may provide its own `parametersRef`. When both are specified, the merging behavior is implementation specific. It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. If the referent cannot be found, refers to an unsupported kind, or when the data within that resource is malformed, the Gateway SHOULD be rejected with the "Accepted" status condition set to "False" and an "InvalidParameters" reason. Support: Implementation-specific properties: group: description: Group is the group of the referent. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object type: object listeners: description: |- Listeners associated with this Gateway. Listeners define logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. ## Distinct Listeners Each Listener in a set of Listeners (for example, in a single Gateway) MUST be _distinct_, in that a traffic flow MUST be able to be assigned to exactly one listener. (This section uses "set of Listeners" rather than "Listeners in a single Gateway" because implementations MAY merge configuration from multiple Gateways onto a single data plane, and these rules _also_ apply in that case). Practically, this means that each listener in a set MUST have a unique combination of Port, Protocol, and, if supported by the protocol, Hostname. Some combinations of port, protocol, and TLS settings are considered Core support and MUST be supported by implementations based on the objects they support: HTTPRoute 1. HTTPRoute, Port: 80, Protocol: HTTP 2. HTTPRoute, Port: 443, Protocol: HTTPS, TLS Mode: Terminate, TLS keypair provided TLSRoute 1. TLSRoute, Port: 443, Protocol: TLS, TLS Mode: Passthrough "Distinct" Listeners have the following property: **The implementation can match inbound requests to a single distinct Listener**. When multiple Listeners share values for fields (for example, two Listeners with the same Port value), the implementation can match requests to only one of the Listeners using other Listener fields. When multiple listeners have the same value for the Protocol field, then each of the Listeners with matching Protocol values MUST have different values for other fields. The set of fields that MUST be different for a Listener differs per protocol. The following rules define the rules for what fields MUST be considered for Listeners to be distinct with each protocol currently defined in the Gateway API spec. The set of listeners that all share a protocol value MUST have _different_ values for _at least one_ of these fields to be distinct: * **HTTP, HTTPS, TLS**: Port, Hostname * **TCP, UDP**: Port One **very** important rule to call out involves what happens when an implementation: * Supports TCP protocol Listeners, as well as HTTP, HTTPS, or TLS protocol Listeners, and * sees HTTP, HTTPS, or TLS protocols with the same `port` as one with TCP Protocol. In this case all the Listeners that share a port with the TCP Listener are not distinct and so MUST NOT be accepted. If an implementation does not support TCP Protocol Listeners, then the previous rule does not apply, and the TCP Listeners SHOULD NOT be accepted. Note that the `tls` field is not used for determining if a listener is distinct, because Listeners that _only_ differ on TLS config will still conflict in all cases. ### Listeners that are distinct only by Hostname When the Listeners are distinct based only on Hostname, inbound request hostnames MUST match from the most specific to least specific Hostname values to choose the correct Listener and its associated set of Routes. Exact matches MUST be processed before wildcard matches, and wildcard matches MUST be processed before fallback (empty Hostname value) matches. For example, `"foo.example.com"` takes precedence over `"*.example.com"`, and `"*.example.com"` takes precedence over `""`. Additionally, if there are multiple wildcard entries, more specific wildcard entries must be processed before less specific wildcard entries. For example, `"*.foo.example.com"` takes precedence over `"*.example.com"`. The precise definition here is that the higher the number of dots in the hostname to the right of the wildcard character, the higher the precedence. The wildcard character will match any number of characters _and dots_ to the left, however, so `"*.example.com"` will match both `"foo.bar.example.com"` _and_ `"bar.example.com"`. ## Handling indistinct Listeners If a set of Listeners contains Listeners that are not distinct, then those Listeners are _Conflicted_, and the implementation MUST set the "Conflicted" condition in the Listener Status to "True". The words "indistinct" and "conflicted" are considered equivalent for the purpose of this documentation. Implementations MAY choose to accept a Gateway with some Conflicted Listeners only if they only accept the partial Listener set that contains no Conflicted Listeners. Specifically, an implementation MAY accept a partial Listener set subject to the following rules: * The implementation MUST NOT pick one conflicting Listener as the winner. ALL indistinct Listeners must not be accepted for processing. * At least one distinct Listener MUST be present, or else the Gateway effectively contains _no_ Listeners, and must be rejected from processing as a whole. The implementation MUST set a "ListenersNotValid" condition on the Gateway Status when the Gateway contains Conflicted Listeners whether or not they accept the Gateway. That Condition SHOULD clearly indicate in the Message which Listeners are conflicted, and which are Accepted. Additionally, the Listener status for those listeners SHOULD indicate which Listeners are conflicted and not Accepted. ## General Listener behavior Note that, for all distinct Listeners, requests SHOULD match at most one Listener. For example, if Listeners are defined for "foo.example.com" and "*.example.com", a request to "foo.example.com" SHOULD only be routed using routes attached to the "foo.example.com" Listener (and not the "*.example.com" Listener). This concept is known as "Listener Isolation", and it is an Extended feature of Gateway API. Implementations that do not support Listener Isolation MUST clearly document this, and MUST NOT claim support for the `GatewayHTTPListenerIsolation` feature. Implementations that _do_ support Listener Isolation SHOULD claim support for the Extended `GatewayHTTPListenerIsolation` feature and pass the associated conformance tests. ## Compatible Listeners A Gateway's Listeners are considered _compatible_ if: 1. They are distinct. 2. The implementation can serve them in compliance with the Addresses requirement that all Listeners are available on all assigned addresses. Compatible combinations in Extended support are expected to vary across implementations. A combination that is compatible for one implementation may not be compatible for another. For example, an implementation that cannot serve both TCP and UDP listeners on the same address, or cannot mix HTTPS and generic TLS listens on the same port would not consider those cases compatible, even though they are distinct. Implementations MAY merge separate Gateways onto a single set of Addresses if all Listeners across all Gateways are compatible. In a future release the MinItems=1 requirement MAY be dropped. Support: Core items: description: |- Listener embodies the concept of a logical endpoint where a Gateway accepts network connections. properties: allowedRoutes: default: namespaces: from: Same description: |- AllowedRoutes defines the types of routes that MAY be attached to a Listener and the trusted namespaces where those Route resources MAY be present. Although a client request may match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: * The most specific match as defined by the Route type. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of "2020-09-08 01:02:03" is given precedence over a Route with a creation timestamp of "2020-09-08 01:02:04". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. All valid rules within a Route attached to this Listener should be implemented. Invalid Route rules can be ignored (sometimes that will mean the full Route). If a Route rule transitions from valid to invalid, support for that Route rule should be dropped to ensure consistency. For example, even if a filter specified by a Route rule is invalid, the rest of the rules within that Route should still be supported. Support: Core properties: kinds: description: |- Kinds specifies the groups and kinds of Routes that are allowed to bind to this Gateway Listener. When unspecified or empty, the kinds of Routes selected are determined using the Listener protocol. A RouteGroupKind MUST correspond to kinds of Routes that are compatible with the application protocol specified in the Listener's Protocol field. If an implementation does not support or recognize this resource type, it MUST set the "ResolvedRefs" condition to False for this Listener with the "InvalidRouteKinds" reason. Support: Core items: description: RouteGroupKind indicates the group and kind of a Route resource. properties: group: default: gateway.networking.k8s.io description: Group is the group of the Route. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is the kind of the Route. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string required: - kind type: object maxItems: 8 type: array x-kubernetes-list-type: atomic namespaces: default: from: Same description: |- Namespaces indicates namespaces from which Routes may be attached to this Listener. This is restricted to the namespace of this Gateway by default. Support: Core properties: from: default: Same description: |- From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. Support: Core enum: - All - Selector - Same type: string selector: description: |- Selector must be specified when From is set to "Selector". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of "From". Support: Core properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic type: object type: object hostname: description: |- Hostname specifies the virtual hostname to match for protocol types that define this concept. When unspecified, all hostnames are matched. This field is ignored for protocols that don't require hostname based matching. Implementations MUST apply Hostname matching appropriately for each of the following protocols: * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. * HTTPS: The Listener Hostname SHOULD match both the SNI and Host header. Note that this does not require the SNI and Host header to be the same. The semantics of this are described in more detail below. To ensure security, Section 11.1 of RFC-6066 emphasizes that server implementations that rely on SNI hostname matching MUST also verify hostnames within the application protocol. Section 9.1.2 of RFC-7540 provides a mechanism for servers to reject the reuse of a connection by responding with the HTTP 421 Misdirected Request status code. This indicates that the origin server has rejected the request because it appears to have been misdirected. To detect misdirected requests, Gateways SHOULD match the authority of the requests with all the SNI hostname(s) configured across all the Gateway Listeners on the same port and protocol: * If another Listener has an exact match or more specific wildcard entry, the Gateway SHOULD return a 421. * If the current Listener (selected by SNI matching during ClientHello) does not match the Host: * If another Listener does match the Host, the Gateway SHOULD return a 421. * If no other Listener matches the Host, the Gateway MUST return a 404. For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, there MUST be an intersection between the values for a Route to be accepted. For more information, refer to the Route specific Hostnames documentation. Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. Support: Core maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string name: description: |- Name is the name of the Listener. This name MUST be unique within a Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string port: description: |- Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. Support: Core format: int32 maximum: 65535 minimum: 1 type: integer protocol: description: |- Protocol specifies the network protocol this listener expects to receive. Support: Core maxLength: 255 minLength: 1 pattern: ^[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ type: string tls: description: |- TLS is the TLS configuration for the Listener. This field is required if the Protocol field is "HTTPS" or "TLS". It is invalid to set this field if the Protocol field is "HTTP", "TCP", or "UDP". The association of SNIs to Certificate defined in ListenerTLSConfig is defined based on the Hostname field for this listener. The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. Support: Core properties: certificateRefs: description: |- CertificateRefs contains a series of references to Kubernetes objects that contains TLS certificates and private keys. These certificates are used to establish a TLS handshake for requests that match the hostname of the associated listener. A single CertificateRef to a Kubernetes Secret has "Core" support. Implementations MAY choose to support attaching multiple certificates to a Listener, but this behavior is implementation-specific. References to a resource in different namespace are invalid UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the "ResolvedRefs" condition MUST be set to False for this listener with the "RefNotPermitted" reason. This field is required to have at least one element when the mode is set to "Terminate" (default) and is optional otherwise. CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls Support: Implementation-specific (More than one reference or other resource types) items: description: |- SecretObjectReference identifies an API object including its namespace, defaulting to Secret. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Secret description: Kind is kind of the referent. For example "Secret". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - name type: object maxItems: 64 type: array x-kubernetes-list-type: atomic mode: default: Terminate description: |- Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificates to be specified in some way, such as populating the certificateRefs field. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. The certificateRefs field is ignored in this mode. Support: Core enum: - Terminate - Passthrough type: string options: additionalProperties: description: |- AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. maxLength: 4096 minLength: 0 type: string description: |- Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. Support: Implementation-specific maxProperties: 16 type: object type: object x-kubernetes-validations: - message: certificateRefs or options must be specified when mode is Terminate rule: 'self.mode == ''Terminate'' ? size(self.certificateRefs) > 0 || size(self.options) > 0 : true' required: - name - port - protocol type: object maxItems: 64 minItems: 1 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map x-kubernetes-validations: - message: tls must not be specified for protocols ['HTTP', 'TCP', 'UDP'] rule: 'self.all(l, l.protocol in [''HTTP'', ''TCP'', ''UDP''] ? !has(l.tls) : true)' - message: tls mode must be Terminate for protocol HTTPS rule: 'self.all(l, (l.protocol == ''HTTPS'' && has(l.tls)) ? (l.tls.mode == '''' || l.tls.mode == ''Terminate'') : true)' - message: tls mode must be set for protocol TLS rule: 'self.all(l, (l.protocol == ''TLS'' ? has(l.tls) && has(l.tls.mode) && l.tls.mode != '''' : true))' - message: hostname must not be specified for protocols ['TCP', 'UDP'] rule: 'self.all(l, l.protocol in [''TCP'', ''UDP''] ? (!has(l.hostname) || l.hostname == '''') : true)' - message: Listener name must be unique within the Gateway rule: self.all(l1, self.exists_one(l2, l1.name == l2.name)) - message: Combination of port, protocol and hostname must be unique for each listener rule: 'self.all(l1, self.exists_one(l2, l1.port == l2.port && l1.protocol == l2.protocol && (has(l1.hostname) && has(l2.hostname) ? l1.hostname == l2.hostname : !has(l1.hostname) && !has(l2.hostname))))' tls: description: |- TLS specifies frontend and backend tls configuration for entire gateway. Support: Extended properties: backend: description: |- Backend describes TLS configuration for gateway when connecting to backends. Note that this contains only details for the Gateway as a TLS client, and does _not_ imply behavior about how to choose which backend should get a TLS connection. That is determined by the presence of a BackendTLSPolicy. Support: Core properties: clientCertificateRef: description: |- ClientCertificateRef references an object that contains a client certificate and its associated private key. It can reference standard Kubernetes resources, i.e., Secret, or implementation-specific custom resources. A ClientCertificateRef is considered invalid if: * It refers to a resource that cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured (e.g., a Secret does not contain the keys named `tls.crt` and `tls.key`). In this case, the `ResolvedRefs` condition on the Gateway MUST be set to False with the Reason `InvalidClientCertificateRef` and the Message of the Condition MUST indicate why the reference is invalid. * It refers to a resource in another namespace UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the `ResolvedRefs` condition on the Gateway MUST be set to False with the Reason `RefNotPermitted`. Implementations MAY choose to perform further validation of the certificate content (e.g., checking expiry or enforcing specific formats). In such cases, an implementation-specific Reason and Message MUST be set. Support: Core - Reference to a Kubernetes TLS Secret (with the type `kubernetes.io/tls`). Support: Implementation-specific - Other resource kinds or Secrets with a different type (e.g., `Opaque`). properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Secret description: Kind is kind of the referent. For example "Secret". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - name type: object type: object frontend: description: |- Frontend describes TLS config when client connects to Gateway. Support: Core properties: default: description: |- Default specifies the default client certificate validation configuration for all Listeners handling HTTPS traffic, unless a per-port configuration is defined. support: Core properties: validation: description: |- Validation holds configuration information for validating the frontend (client). Setting this field will result in mutual authentication when connecting to the gateway. In browsers this may result in a dialog appearing that requests a user to specify the client certificate. The maximum depth of a certificate chain accepted in verification is Implementation specific. Support: Core properties: caCertificateRefs: description: |- CACertificateRefs contains one or more references to Kubernetes objects that contain a PEM-encoded TLS CA certificate bundle, which is used as a trust anchor to validate the certificates presented by the client. A CACertificateRef is invalid if: * It refers to a resource that cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured (e.g., a ConfigMap does not contain a key named `ca.crt`). In this case, the Reason on all matching HTTPS listeners must be set to `InvalidCACertificateRef` and the Message of the Condition must indicate which reference is invalid and why. * It refers to an unknown or unsupported kind of resource. In this case, the Reason on all matching HTTPS listeners must be set to `InvalidCACertificateKind` and the Message of the Condition must explain which kind of resource is unknown or unsupported. * It refers to a resource in another namespace UNLESS there is a ReferenceGrant in the target namespace that allows the CA certificate to be attached. If a ReferenceGrant does not allow this reference, the `ResolvedRefs` on all matching HTTPS listeners condition MUST be set with the Reason `RefNotPermitted`. Implementations MAY choose to perform further validation of the certificate content (e.g., checking expiry or enforcing specific formats). In such cases, an implementation-specific Reason and Message MUST be set. In all cases, the implementation MUST ensure that the `ResolvedRefs` condition is set to `status: False` on all targeted listeners (i.e., listeners serving HTTPS on a matching port). The condition MUST include a Reason and Message that indicate the cause of the error. If ALL CACertificateRefs are invalid, the implementation MUST also ensure the `Accepted` condition on the listener is set to `status: False`, with the Reason `NoValidCACertificate`. Implementations MAY choose to support attaching multiple CA certificates to a listener, but this behavior is implementation-specific. Support: Core - A single reference to a Kubernetes ConfigMap, with the CA certificate in a key named `ca.crt`. Support: Implementation-specific - More than one reference, other kinds of resources, or a single reference that includes multiple certificates. items: description: |- ObjectReference identifies an API object including its namespace. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When set to the empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "ConfigMap" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - group - kind - name type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-type: atomic mode: default: AllowValidOnly description: |- FrontendValidationMode defines the mode for validating the client certificate. There are two possible modes: - AllowValidOnly: In this mode, the gateway will accept connections only if the client presents a valid certificate. This certificate must successfully pass validation against the CA certificates specified in `CACertificateRefs`. - AllowInsecureFallback: In this mode, the gateway will accept connections even if the client certificate is not presented or fails verification. This approach delegates client authorization to the backend and introduce a significant security risk. It should be used in testing environments or on a temporary basis in non-testing environments. Defaults to AllowValidOnly. Support: Core enum: - AllowValidOnly - AllowInsecureFallback type: string required: - caCertificateRefs type: object type: object perPort: description: |- PerPort specifies tls configuration assigned per port. Per port configuration is optional. Once set this configuration overrides the default configuration for all Listeners handling HTTPS traffic that match this port. Each override port requires a unique TLS configuration. support: Core items: properties: port: description: |- The Port indicates the Port Number to which the TLS configuration will be applied. This configuration will be applied to all Listeners handling HTTPS traffic that match this port. Support: Core format: int32 maximum: 65535 minimum: 1 type: integer tls: description: |- TLS store the configuration that will be applied to all Listeners handling HTTPS traffic and matching given port. Support: Core properties: validation: description: |- Validation holds configuration information for validating the frontend (client). Setting this field will result in mutual authentication when connecting to the gateway. In browsers this may result in a dialog appearing that requests a user to specify the client certificate. The maximum depth of a certificate chain accepted in verification is Implementation specific. Support: Core properties: caCertificateRefs: description: |- CACertificateRefs contains one or more references to Kubernetes objects that contain a PEM-encoded TLS CA certificate bundle, which is used as a trust anchor to validate the certificates presented by the client. A CACertificateRef is invalid if: * It refers to a resource that cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured (e.g., a ConfigMap does not contain a key named `ca.crt`). In this case, the Reason on all matching HTTPS listeners must be set to `InvalidCACertificateRef` and the Message of the Condition must indicate which reference is invalid and why. * It refers to an unknown or unsupported kind of resource. In this case, the Reason on all matching HTTPS listeners must be set to `InvalidCACertificateKind` and the Message of the Condition must explain which kind of resource is unknown or unsupported. * It refers to a resource in another namespace UNLESS there is a ReferenceGrant in the target namespace that allows the CA certificate to be attached. If a ReferenceGrant does not allow this reference, the `ResolvedRefs` on all matching HTTPS listeners condition MUST be set with the Reason `RefNotPermitted`. Implementations MAY choose to perform further validation of the certificate content (e.g., checking expiry or enforcing specific formats). In such cases, an implementation-specific Reason and Message MUST be set. In all cases, the implementation MUST ensure that the `ResolvedRefs` condition is set to `status: False` on all targeted listeners (i.e., listeners serving HTTPS on a matching port). The condition MUST include a Reason and Message that indicate the cause of the error. If ALL CACertificateRefs are invalid, the implementation MUST also ensure the `Accepted` condition on the listener is set to `status: False`, with the Reason `NoValidCACertificate`. Implementations MAY choose to support attaching multiple CA certificates to a listener, but this behavior is implementation-specific. Support: Core - A single reference to a Kubernetes ConfigMap, with the CA certificate in a key named `ca.crt`. Support: Implementation-specific - More than one reference, other kinds of resources, or a single reference that includes multiple certificates. items: description: |- ObjectReference identifies an API object including its namespace. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When set to the empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "ConfigMap" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - group - kind - name type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-type: atomic mode: default: AllowValidOnly description: |- FrontendValidationMode defines the mode for validating the client certificate. There are two possible modes: - AllowValidOnly: In this mode, the gateway will accept connections only if the client presents a valid certificate. This certificate must successfully pass validation against the CA certificates specified in `CACertificateRefs`. - AllowInsecureFallback: In this mode, the gateway will accept connections even if the client certificate is not presented or fails verification. This approach delegates client authorization to the backend and introduce a significant security risk. It should be used in testing environments or on a temporary basis in non-testing environments. Defaults to AllowValidOnly. Support: Core enum: - AllowValidOnly - AllowInsecureFallback type: string required: - caCertificateRefs type: object type: object required: - port - tls type: object maxItems: 64 type: array x-kubernetes-list-map-keys: - port x-kubernetes-list-type: map x-kubernetes-validations: - message: Port for TLS configuration must be unique within the Gateway rule: self.all(t1, self.exists_one(t2, t1.port == t2.port)) required: - default type: object type: object required: - gatewayClassName - listeners type: object status: default: conditions: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Programmed description: Status defines the current state of Gateway. properties: addresses: description: |- Addresses lists the network addresses that have been bound to the Gateway. This list may differ from the addresses provided in the spec under some conditions: * no addresses are specified, all addresses are dynamically assigned * a combination of specified and dynamic addresses are assigned * a specified address was unusable (e.g. already in use) items: description: GatewayStatusAddress describes a network address that is bound to a Gateway. oneOf: - properties: type: enum: - IPAddress value: anyOf: - format: ipv4 - format: ipv6 - properties: type: not: enum: - IPAddress properties: type: default: IPAddress description: Type of the address. maxLength: 253 minLength: 1 pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string value: description: |- Value of the address. The validity of the values will depend on the type and support by the controller. Examples: `1.2.3.4`, `128::1`, `my-ip-address`. maxLength: 253 minLength: 1 type: string required: - value type: object x-kubernetes-validations: - message: Hostname value must only contain valid characters (matching ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) rule: 'self.type == ''Hostname'' ? self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"""): true' maxItems: 16 type: array x-kubernetes-list-type: atomic attachedListenerSets: description: |- AttachedListenerSets represents the total number of ListenerSets that have been successfully attached to this Gateway. A ListenerSet is successfully attached to a Gateway when all the following conditions are met: - The ListenerSet is selected by the Gateway's AllowedListeners field - The ListenerSet has a valid ParentRef selecting the Gateway - The ListenerSet's status has the condition "Accepted: true" Uses for this field include troubleshooting AttachedListenerSets attachment and measuring blast radius/impact of changes to a Gateway. format: int32 type: integer conditions: default: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Programmed description: |- Conditions describe the current conditions of the Gateway. Implementations should prefer to express Gateway conditions using the `GatewayConditionType` and `GatewayConditionReason` constants so that operators and tools can converge on a common vocabulary to describe Gateway state. Known condition types are: * "Accepted" * "Programmed" * "Ready" items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map listeners: description: Listeners provide status for each unique listener port defined in the Spec. items: description: ListenerStatus is the status associated with a Listener. properties: attachedRoutes: description: |- AttachedRoutes represents the total number of Routes that have been successfully attached to this Listener. Successful attachment of a Route to a Listener is based solely on the combination of the AllowedRoutes field on the corresponding Listener and the Route's ParentRefs field. A Route is successfully attached to a Listener when it is selected by the Listener's AllowedRoutes field AND the Route has a valid ParentRef selecting the whole Gateway resource or a specific Listener as a parent resource (more detail on attachment semantics can be found in the documentation on the various Route kinds ParentRefs fields). Listener or Route status does not impact successful attachment, i.e. the AttachedRoutes field count MUST be set for Listeners, even if the Accepted condition of an individual Listener is set to "False". The AttachedRoutes number represents the number of Routes with the Accepted condition set to "True" that have been attached to this Listener. Routes with any other value for the Accepted condition MUST NOT be included in this count. Uses for this field include troubleshooting Route attachment and measuring blast radius/impact of changes to a Listener. format: int32 type: integer conditions: description: Conditions describe the current condition of this listener. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map name: description: Name is the name of the Listener that this status corresponds to. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string supportedKinds: description: |- SupportedKinds is the list indicating the Kinds supported by this listener. This MUST represent the kinds supported by an implementation for that Listener configuration. If kinds are specified in Spec that are not supported, they MUST NOT appear in this list and an implementation MUST set the "ResolvedRefs" condition to "False" with the "InvalidRouteKinds" reason. If both valid and invalid Route kinds are specified, the implementation MUST reference the valid Route kinds that have been specified. items: description: RouteGroupKind indicates the group and kind of a Route resource. properties: group: default: gateway.networking.k8s.io description: Group is the group of the Route. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is the kind of the Route. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string required: - kind type: object maxItems: 8 type: array x-kubernetes-list-type: atomic required: - attachedRoutes - conditions - name type: object maxItems: 64 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object required: - spec type: object served: true storage: false subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/experimental/gateway.networking.k8s.io_grpcroutes.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: experimental name: grpcroutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: GRPCRoute listKind: GRPCRouteList plural: grpcroutes singular: grpcroute scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .spec.hostnames name: Hostnames type: string - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1 schema: openAPIV3Schema: description: |- GRPCRoute provides a way to route gRPC requests. This includes the capability to match requests by hostname, gRPC service, gRPC method, or HTTP/2 header. Filters can be used to specify additional processing steps. Backends specify where matching requests will be routed. GRPCRoute falls under extended support within the Gateway API. Within the following specification, the word "MUST" indicates that an implementation supporting GRPCRoute must conform to the indicated requirement, but an implementation not supporting this route type need not follow the requirement unless explicitly indicated. Implementations supporting `GRPCRoute` with the `HTTPS` `ProtocolType` MUST accept HTTP/2 connections without an initial upgrade from HTTP/1.1, i.e. via ALPN. If the implementation does not support this, then it MUST set the "Accepted" condition to "False" for the affected listener with a reason of "UnsupportedProtocol". Implementations MAY also accept HTTP/2 connections with an upgrade from HTTP/1. Implementations supporting `GRPCRoute` with the `HTTP` `ProtocolType` MUST support HTTP/2 over cleartext TCP (h2c, https://www.rfc-editor.org/rfc/rfc7540#section-3.1) without an initial upgrade from HTTP/1.1, i.e. with prior knowledge (https://www.rfc-editor.org/rfc/rfc7540#section-3.4). If the implementation does not support this, then it MUST set the "Accepted" condition to "False" for the affected listener with a reason of "UnsupportedProtocol". Implementations MAY also accept HTTP/2 connections with an upgrade from HTTP/1, i.e. without prior knowledge. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of GRPCRoute. properties: hostnames: description: |- Hostnames defines a set of hostnames to match against the GRPC Host header to select a GRPCRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label MUST appear by itself as the first label. If a hostname is specified by both the Listener and GRPCRoute, there MUST be at least one intersecting hostname for the GRPCRoute to be attached to the Listener. For example: * A Listener with `test.example.com` as the hostname matches GRPCRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches GRPCRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `test.example.com` and `*.example.com` would both match. On the other hand, `example.com` and `test.example.net` would not match. Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. If both the Listener and GRPCRoute have specified hostnames, any GRPCRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the GRPCRoute specified `test.example.com` and `test.example.net`, `test.example.net` MUST NOT be considered for a match. If both the Listener and GRPCRoute have specified hostnames, and none match with the criteria above, then the GRPCRoute MUST NOT be accepted by the implementation. The implementation MUST raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. If a Route (A) of type HTTPRoute or GRPCRoute is attached to a Listener and that listener already has another Route (B) of the other type attached and the intersection of the hostnames of A and B is non-empty, then the implementation MUST accept exactly one of these two routes, determined by the following criteria, in order: * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by "{namespace}/{name}". The rejected Route MUST raise an 'Accepted' condition with a status of 'False' in the corresponding RouteParentStatus. Support: Core items: description: |- Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. Hostname can be "precise" which is a domain name without the terminating dot of a network host (e.g. "foo.example.com") or "wildcard", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed. maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string maxItems: 16 type: array x-kubernetes-list-type: atomic parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. For Services, that means the Service must either be in the same namespace for a "producer" route, or the mesh implementation must support and allow "consumer" routes for the referenced Service. ReferenceGrant is not applicable for governing ParentRefs to Services - it is not possible to create a "producer" route for a Service in a different namespace from the Route. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. ParentRefs must be _distinct_. This means either that: * They select different objects. If this is the case, then parentRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, `namespace`, and `name` must be unique across all parentRef entries in the Route. * They do not select different objects, but for each optional field used, each ParentRef that selects the same object must set the same set of optional fields to different values. If one ParentRef sets a combination of optional fields, all must set the same combination. Some examples: * If one ParentRef sets `sectionName`, all ParentRefs referencing the same object must also set `sectionName`. * If one ParentRef sets `port`, all ParentRefs referencing the same object must also set `port`. * If one ParentRef sets `sectionName` and `port`, all ParentRefs referencing the same object must also set `sectionName` and `port`. It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable other kinds of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object maxItems: 32 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__)) ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''') && (!has(p1.port) || p1.port == 0) == (!has(p2.port) || p2.port == 0)): true))' - message: sectionName or port must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || ( has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)) && (((!has(p1.port) || p1.port == 0) && (!has(p2.port) || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port)))) rules: description: Rules are a list of GRPC matchers, filters and actions. items: description: |- GRPCRouteRule defines the semantics for matching a gRPC request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be sent. Failure behavior here depends on how many BackendRefs are specified and how many are invalid. If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive an `UNAVAILABLE` status. See the GRPCBackendRef definition for the rules about what makes a single GRPCBackendRef invalid. When a GRPCBackendRef is invalid, `UNAVAILABLE` statuses MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive an `UNAVAILABLE` status. For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic MUST receive an `UNAVAILABLE` status. Implementations may choose how that 50 percent is determined. Support: Core for Kubernetes Service Support: Implementation-specific for any other resource Support for weight: Core items: description: |- GRPCBackendRef defines how a GRPCRoute forwards a gRPC request. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. Implementations supporting appProtocol SHOULD recognize the Kubernetes Standard Application Protocols defined in KEP-3726. If a Service appProtocol isn't specified, an implementation MAY infer the backend protocol through its own means. Implementations MAY infer the protocol from the Route type referring to the backend Service. If a Route is not able to send traffic to the backend using the specified protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. properties: filters: description: |- Filters defined at this level MUST be executed if and only if the request is being forwarded to the backend defined here. Support: Implementation-specific (For broader support of filters, use the Filters field in GRPCRouteRule.) items: description: |- GRPCRouteFilter defines processing steps that must be completed during the request or response lifecycle. GRPCRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the "filter" behavior. For example, resource "myroutefilter" in group "networking.example.net"). ExtensionRef MUST NOT be used for core and extended filters. Support: Implementation-specific This filter can be used multiple times within the same rule. properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "HTTPRoute" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request headers. Support: Core properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object requestMirror: description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. This filter can be used multiple times within the same rule. Note that not all implementations will be able to support mirroring to multiple backends. Support: Extended properties: backendRef: description: |- BackendRef references a resource where mirrored requests are sent. Mirrored requests must be sent only to a single destination endpoint within this BackendRef, irrespective of how many endpoints are present within this BackendRef. If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the "ResolvedRefs" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the "ResolvedRefs" condition on the Route is set to `status: False`, with the "RefNotPermitted" reason and not configure this backend in the underlying implementation. In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. Support: Extended for Kubernetes Service Support: Implementation-specific for any other resource properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: description: |- Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. properties: denominator: default: 100 format: int32 minimum: 1 type: integer numerator: format: int32 minimum: 0 type: integer required: - numerator type: object x-kubernetes-validations: - message: numerator must be less than or equal to denominator rule: self.numerator <= self.denominator percent: description: |- Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. format: int32 maximum: 100 minimum: 0 type: integer required: - backendRef type: object x-kubernetes-validations: - message: Only one of percent or fraction may be specified in HTTPRequestMirrorFilter rule: '!(has(self.percent) && has(self.fraction))' responseHeaderModifier: description: |- ResponseHeaderModifier defines a schema for a filter that modifies response headers. Support: Extended properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object type: description: |- Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: - Core: Filter types and their corresponding configuration defined by "Support: Core" in this package, e.g. "RequestHeaderModifier". All implementations supporting GRPCRoute MUST support core filters. - Extended: Filter types and their corresponding configuration defined by "Support: Extended" in this package, e.g. "RequestMirror". Implementers are encouraged to support extended filters. - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` MUST be set to "ExtensionRef" for custom filters. Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. enum: - ResponseHeaderModifier - RequestHeaderModifier - RequestMirror - ExtensionRef type: string required: - type type: object x-kubernetes-validations: - message: filter.requestHeaderModifier must be nil if the filter.type is not RequestHeaderModifier rule: '!(has(self.requestHeaderModifier) && self.type != ''RequestHeaderModifier'')' - message: filter.requestHeaderModifier must be specified for RequestHeaderModifier filter.type rule: '!(!has(self.requestHeaderModifier) && self.type == ''RequestHeaderModifier'')' - message: filter.responseHeaderModifier must be nil if the filter.type is not ResponseHeaderModifier rule: '!(has(self.responseHeaderModifier) && self.type != ''ResponseHeaderModifier'')' - message: filter.responseHeaderModifier must be specified for ResponseHeaderModifier filter.type rule: '!(!has(self.responseHeaderModifier) && self.type == ''ResponseHeaderModifier'')' - message: filter.requestMirror must be nil if the filter.type is not RequestMirror rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' - message: filter.requestMirror must be specified for RequestMirror filter.type rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' - message: filter.extensionRef must be nil if the filter.type is not ExtensionRef rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: RequestHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1 - message: ResponseHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() <= 1 group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer weight: default: 1 description: |- Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. Support for this field varies based on the context where used. format: int32 maximum: 1000000 minimum: 0 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' maxItems: 16 type: array x-kubernetes-list-type: atomic filters: description: |- Filters define the filters that are applied to requests that match this rule. The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. Conformance-levels at this level are defined based on the type of filter: - ALL core filters MUST be supported by all implementations that support GRPCRoute. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. Specifying the same filter multiple times is not supported unless explicitly indicated in the filter. If an implementation cannot support a combination of filters, it must clearly document that limitation. In cases where incompatible or unsupported filters are specified and cause the `Accepted` condition to be set to status `False`, implementations may use the `IncompatibleFilters` reason to specify this configuration error. Support: Core items: description: |- GRPCRouteFilter defines processing steps that must be completed during the request or response lifecycle. GRPCRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the "filter" behavior. For example, resource "myroutefilter" in group "networking.example.net"). ExtensionRef MUST NOT be used for core and extended filters. Support: Implementation-specific This filter can be used multiple times within the same rule. properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "HTTPRoute" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request headers. Support: Core properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object requestMirror: description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. This filter can be used multiple times within the same rule. Note that not all implementations will be able to support mirroring to multiple backends. Support: Extended properties: backendRef: description: |- BackendRef references a resource where mirrored requests are sent. Mirrored requests must be sent only to a single destination endpoint within this BackendRef, irrespective of how many endpoints are present within this BackendRef. If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the "ResolvedRefs" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the "ResolvedRefs" condition on the Route is set to `status: False`, with the "RefNotPermitted" reason and not configure this backend in the underlying implementation. In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. Support: Extended for Kubernetes Service Support: Implementation-specific for any other resource properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: description: |- Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. properties: denominator: default: 100 format: int32 minimum: 1 type: integer numerator: format: int32 minimum: 0 type: integer required: - numerator type: object x-kubernetes-validations: - message: numerator must be less than or equal to denominator rule: self.numerator <= self.denominator percent: description: |- Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. format: int32 maximum: 100 minimum: 0 type: integer required: - backendRef type: object x-kubernetes-validations: - message: Only one of percent or fraction may be specified in HTTPRequestMirrorFilter rule: '!(has(self.percent) && has(self.fraction))' responseHeaderModifier: description: |- ResponseHeaderModifier defines a schema for a filter that modifies response headers. Support: Extended properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object type: description: |- Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: - Core: Filter types and their corresponding configuration defined by "Support: Core" in this package, e.g. "RequestHeaderModifier". All implementations supporting GRPCRoute MUST support core filters. - Extended: Filter types and their corresponding configuration defined by "Support: Extended" in this package, e.g. "RequestMirror". Implementers are encouraged to support extended filters. - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` MUST be set to "ExtensionRef" for custom filters. Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. enum: - ResponseHeaderModifier - RequestHeaderModifier - RequestMirror - ExtensionRef type: string required: - type type: object x-kubernetes-validations: - message: filter.requestHeaderModifier must be nil if the filter.type is not RequestHeaderModifier rule: '!(has(self.requestHeaderModifier) && self.type != ''RequestHeaderModifier'')' - message: filter.requestHeaderModifier must be specified for RequestHeaderModifier filter.type rule: '!(!has(self.requestHeaderModifier) && self.type == ''RequestHeaderModifier'')' - message: filter.responseHeaderModifier must be nil if the filter.type is not ResponseHeaderModifier rule: '!(has(self.responseHeaderModifier) && self.type != ''ResponseHeaderModifier'')' - message: filter.responseHeaderModifier must be specified for ResponseHeaderModifier filter.type rule: '!(!has(self.responseHeaderModifier) && self.type == ''ResponseHeaderModifier'')' - message: filter.requestMirror must be nil if the filter.type is not RequestMirror rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' - message: filter.requestMirror must be specified for RequestMirror filter.type rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' - message: filter.extensionRef must be nil if the filter.type is not ExtensionRef rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: RequestHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1 - message: ResponseHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() <= 1 matches: description: |- Matches define conditions used for matching the rule against incoming gRPC requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. For example, take the following matches configuration: ``` matches: - method: service: foo.bar headers: values: version: 2 - method: service: foo.bar.v2 ``` For a request to match against this rule, it MUST satisfy EITHER of the two conditions: - service of foo.bar AND contains the header `version: 2` - service of foo.bar.v2 See the documentation for GRPCRouteMatch on how to specify multiple match conditions to be ANDed together. If no matches are specified, the implementation MUST match every gRPC request. Proxy or Load Balancer routing configuration generated from GRPCRoutes MUST prioritize rules based on the following criteria, continuing on ties. Merging MUST not be done between GRPCRoutes and HTTPRoutes. Precedence MUST be given to the rule with the largest number of: * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. * Characters in a matching service. * Characters in a matching method. * Header matches. If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by "{namespace}/{name}". If ties still exist within the Route that has been given precedence, matching precedence MUST be granted to the first matching rule meeting the above criteria. items: description: |- GRPCRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. For example, the match below will match a gRPC request only if its service is `foo` AND it contains the `version: v1` header: ``` matches: - method: type: Exact service: "foo" - headers: name: "version" value "v1" ``` properties: headers: description: |- Headers specifies gRPC request header matchers. Multiple match values are ANDed together, meaning, a request MUST match all the specified headers to select the route. items: description: |- GRPCHeaderMatch describes how to select a gRPC route by matching gRPC request headers. properties: name: description: |- Name is the name of the gRPC Header to be matched. If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string type: default: Exact description: Type specifies how to match against the value of the header. enum: - Exact - RegularExpression type: string value: description: Value is the value of the gRPC Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map method: description: |- Method specifies a gRPC request service/method matcher. If this field is not specified, all services and methods will match. properties: method: description: |- Value of the method to match against. If left empty or omitted, will match all services. At least one of Service and Method MUST be a non-empty string. maxLength: 1024 type: string service: description: |- Value of the service to match against. If left empty or omitted, will match any service. At least one of Service and Method MUST be a non-empty string. maxLength: 1024 type: string type: default: Exact description: |- Type specifies how to match against the service and/or method. Support: Core (Exact with service and method specified) Support: Implementation-specific (Exact with method specified but no service specified) Support: Implementation-specific (RegularExpression) enum: - Exact - RegularExpression type: string type: object x-kubernetes-validations: - message: One or both of 'service' or 'method' must be specified rule: 'has(self.type) ? has(self.service) || has(self.method) : true' - message: service must only contain valid characters (matching ^(?i)\.?[a-z_][a-z_0-9]*(\.[a-z_][a-z_0-9]*)*$) rule: '(!has(self.type) || self.type == ''Exact'') && has(self.service) ? self.service.matches(r"""^(?i)\.?[a-z_][a-z_0-9]*(\.[a-z_][a-z_0-9]*)*$"""): true' - message: method must only contain valid characters (matching ^[A-Za-z_][A-Za-z_0-9]*$) rule: '(!has(self.type) || self.type == ''Exact'') && has(self.method) ? self.method.matches(r"""^[A-Za-z_][A-Za-z_0-9]*$"""): true' type: object maxItems: 64 type: array x-kubernetes-list-type: atomic name: description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. Support: Extended maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string sessionPersistence: description: |- SessionPersistence defines and configures session persistence for the route rule. Support: Extended properties: absoluteTimeout: description: |- AbsoluteTimeout defines the absolute timeout of the persistent session. Once the AbsoluteTimeout duration has elapsed, the session becomes invalid. Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string cookieConfig: description: |- CookieConfig provides configuration settings that are specific to cookie-based session persistence. Support: Core properties: lifetimeType: default: Session description: |- LifetimeType specifies whether the cookie has a permanent or session-based lifetime. A permanent cookie persists until its specified expiry time, defined by the Expires or Max-Age cookie attributes, while a session cookie is deleted when the current session ends. When set to "Permanent", AbsoluteTimeout indicates the cookie's lifetime via the Expires or Max-Age cookie attributes and is required. When set to "Session", AbsoluteTimeout indicates the absolute lifetime of the cookie tracked by the gateway and is optional. Defaults to "Session". Support: Core for "Session" type Support: Extended for "Permanent" type enum: - Permanent - Session type: string type: object idleTimeout: description: |- IdleTimeout defines the idle timeout of the persistent session. Once the session has been idle for more than the specified IdleTimeout duration, the session becomes invalid. Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string sessionName: description: |- SessionName defines the name of the persistent session token which may be reflected in the cookie or the header. Users should avoid reusing session names to prevent unintended consequences, such as rejection or unpredictable behavior. Support: Implementation-specific maxLength: 128 type: string type: default: Cookie description: |- Type defines the type of session persistence such as through the use of a header or cookie. Defaults to cookie based session persistence. Support: Core for "Cookie" type Support: Extended for "Header" type enum: - Cookie - Header type: string type: object x-kubernetes-validations: - message: AbsoluteTimeout must be specified when cookie lifetimeType is Permanent rule: '!has(self.cookieConfig) || !has(self.cookieConfig.lifetimeType) || self.cookieConfig.lifetimeType != ''Permanent'' || has(self.absoluteTimeout)' - message: cookieConfig can only be set with type Cookie rule: '!has(self.cookieConfig) || self.type == ''Cookie''' type: object maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: While 16 rules and 64 matches per rule are allowed, the total number of matches across all rules in a route must be less than 128 rule: '(self.size() > 0 ? (has(self[0].matches) ? self[0].matches.size() : 0) : 0) + (self.size() > 1 ? (has(self[1].matches) ? self[1].matches.size() : 0) : 0) + (self.size() > 2 ? (has(self[2].matches) ? self[2].matches.size() : 0) : 0) + (self.size() > 3 ? (has(self[3].matches) ? self[3].matches.size() : 0) : 0) + (self.size() > 4 ? (has(self[4].matches) ? self[4].matches.size() : 0) : 0) + (self.size() > 5 ? (has(self[5].matches) ? self[5].matches.size() : 0) : 0) + (self.size() > 6 ? (has(self[6].matches) ? self[6].matches.size() : 0) : 0) + (self.size() > 7 ? (has(self[7].matches) ? self[7].matches.size() : 0) : 0) + (self.size() > 8 ? (has(self[8].matches) ? self[8].matches.size() : 0) : 0) + (self.size() > 9 ? (has(self[9].matches) ? self[9].matches.size() : 0) : 0) + (self.size() > 10 ? (has(self[10].matches) ? self[10].matches.size() : 0) : 0) + (self.size() > 11 ? (has(self[11].matches) ? self[11].matches.size() : 0) : 0) + (self.size() > 12 ? (has(self[12].matches) ? self[12].matches.size() : 0) : 0) + (self.size() > 13 ? (has(self[13].matches) ? self[13].matches.size() : 0) : 0) + (self.size() > 14 ? (has(self[14].matches) ? self[14].matches.size() : 0) : 0) + (self.size() > 15 ? (has(self[15].matches) ? self[15].matches.size() : 0) : 0) <= 128' - message: Rule name must be unique within the route rule: self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) && l1.name == l2.name)) useDefaultGateways: description: |- UseDefaultGateways indicates the default Gateway scope to use for this Route. If unset (the default) or set to None, the Route will not be attached to any default Gateway; if set, it will be attached to any default Gateway supporting the named scope, subject to the usual rules about which Routes a Gateway is allowed to claim. Think carefully before using this functionality! The set of default Gateways supporting the requested scope can change over time without any notice to the Route author, and in many situations it will not be appropriate to request a default Gateway for a given Route -- for example, a Route with specific security requirements should almost certainly not use a default Gateway. enum: - All - None type: string type: object status: description: Status defines the current state of GRPCRoute. properties: parents: description: |- Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway. items: description: |- RouteParentStatus describes the status of a route with respect to an associated Parent. properties: conditions: description: |- Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the "Accepted" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. A Route MUST be considered "Accepted" if at least one of the Route's rules is implemented by the Gateway. There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: * The Route refers to a nonexistent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace to which the controller does not have access. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string parentRef: description: |- ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object required: - conditions - controllerName - parentRef type: object maxItems: 32 type: array x-kubernetes-list-type: atomic required: - parents type: object required: - spec type: object served: true storage: true subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/experimental/gateway.networking.k8s.io_httproutes.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: experimental name: httproutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: HTTPRoute listKind: HTTPRouteList plural: httproutes singular: httproute scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .spec.hostnames name: Hostnames type: string - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1 schema: openAPIV3Schema: description: |- HTTPRoute provides a way to route HTTP requests. This includes the capability to match requests by hostname, path, header, or query param. Filters can be used to specify additional processing steps. Backends specify where matching requests should be routed. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of HTTPRoute. properties: hostnames: description: |- Hostnames defines a set of hostnames that should match against the HTTP Host header to select a HTTPRoute used to process the request. Implementations MUST ignore any port value specified in the HTTP Host header while performing a match and (absent of any applicable header modification configuration) MUST forward this header unmodified to the backend. Valid values for Hostnames are determined by RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. If a hostname is specified by both the Listener and HTTPRoute, there must be at least one intersecting hostname for the HTTPRoute to be attached to the Listener. For example: * A Listener with `test.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `*.example.com`, `test.example.com`, and `foo.test.example.com` would all match. On the other hand, `example.com` and `test.example.net` would not match. Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. If both the Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the HTTPRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. If both the Listener and HTTPRoute have specified hostnames, and none match with the criteria above, then the HTTPRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. overlapping wildcard matching and exact matching hostnames), precedence must be given to rules from the HTTPRoute with the largest number of: * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. Support: Core items: description: |- Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. Hostname can be "precise" which is a domain name without the terminating dot of a network host (e.g. "foo.example.com") or "wildcard", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed. maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string maxItems: 16 type: array x-kubernetes-list-type: atomic parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. For Services, that means the Service must either be in the same namespace for a "producer" route, or the mesh implementation must support and allow "consumer" routes for the referenced Service. ReferenceGrant is not applicable for governing ParentRefs to Services - it is not possible to create a "producer" route for a Service in a different namespace from the Route. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. ParentRefs must be _distinct_. This means either that: * They select different objects. If this is the case, then parentRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, `namespace`, and `name` must be unique across all parentRef entries in the Route. * They do not select different objects, but for each optional field used, each ParentRef that selects the same object must set the same set of optional fields to different values. If one ParentRef sets a combination of optional fields, all must set the same combination. Some examples: * If one ParentRef sets `sectionName`, all ParentRefs referencing the same object must also set `sectionName`. * If one ParentRef sets `port`, all ParentRefs referencing the same object must also set `port`. * If one ParentRef sets `sectionName` and `port`, all ParentRefs referencing the same object must also set `sectionName` and `port`. It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable other kinds of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object maxItems: 32 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__)) ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''') && (!has(p1.port) || p1.port == 0) == (!has(p2.port) || p2.port == 0)): true))' - message: sectionName or port must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || ( has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)) && (((!has(p1.port) || p1.port == 0) && (!has(p2.port) || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port)))) rules: default: - matches: - path: type: PathPrefix value: / description: Rules are a list of HTTP matchers, filters and actions. items: description: |- HTTPRouteRule defines semantics for matching an HTTP request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be sent. Failure behavior here depends on how many BackendRefs are specified and how many are invalid. If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive a 500 status code. See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. When a HTTPBackendRef is invalid, 500 status codes MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive a 500 status code. For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic must receive a 500. Implementations may choose how that 50 percent is determined. When a HTTPBackendRef refers to a Service that has no ready endpoints, implementations SHOULD return a 503 for requests to that backend instead. If an implementation chooses to do this, all of the above rules for 500 responses MUST also apply for responses that return a 503. Support: Core for Kubernetes Service Support: Extended for Kubernetes ServiceImport Support: Implementation-specific for any other resource Support for weight: Core items: description: |- HTTPBackendRef defines how a HTTPRoute forwards a HTTP request. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. Implementations supporting appProtocol SHOULD recognize the Kubernetes Standard Application Protocols defined in KEP-3726. If a Service appProtocol isn't specified, an implementation MAY infer the backend protocol through its own means. Implementations MAY infer the protocol from the Route type referring to the backend Service. If a Route is not able to send traffic to the backend using the specified protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. properties: filters: description: |- Filters defined at this level should be executed if and only if the request is being forwarded to the backend defined here. Support: Implementation-specific (For broader support of filters, use the Filters field in HTTPRouteRule.) items: description: |- HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: cors: description: |- CORS defines a schema for a filter that responds to the cross-origin request based on HTTP response header. Support: Extended properties: allowCredentials: description: |- AllowCredentials indicates whether the actual cross-origin request allows to include credentials. When set to true, the gateway will include the `Access-Control-Allow-Credentials` response header with value true (case-sensitive). When set to false or omitted the gateway will omit the header `Access-Control-Allow-Credentials` entirely (this is the standard CORS behavior). Support: Extended type: boolean allowHeaders: description: |- AllowHeaders indicates which HTTP request headers are supported for accessing the requested resource. Header names are not case-sensitive. Multiple header names in the value of the `Access-Control-Allow-Headers` response header are separated by a comma (","). When the `AllowHeaders` field is configured with one or more headers, the gateway must return the `Access-Control-Allow-Headers` response header which value is present in the `AllowHeaders` field. If any header name in the `Access-Control-Request-Headers` request header is not included in the list of header names specified by the response header `Access-Control-Allow-Headers`, it will present an error on the client side. If any header name in the `Access-Control-Allow-Headers` response header does not recognize by the client, it will also occur an error on the client side. A wildcard indicates that the requests with all HTTP headers are allowed. If config contains the wildcard "*" in allowHeaders and the request is not credentialed, the `Access-Control-Allow-Headers` response header can either use the `*` wildcard or the value of Access-Control-Request-Headers from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Headers` response header. When also the `AllowCredentials` field is true and `AllowHeaders` field is specified with the `*` wildcard, the gateway must specify one or more HTTP headers in the value of the `Access-Control-Allow-Headers` response header. The value of the header `Access-Control-Allow-Headers` is same as the `Access-Control-Request-Headers` header provided by the client. If the header `Access-Control-Request-Headers` is not included in the request, the gateway will omit the `Access-Control-Allow-Headers` response header, instead of specifying the `*` wildcard. Support: Extended items: description: |- HTTPHeaderName is the name of an HTTP header. Valid values include: * "Authorization" * "Set-Cookie" Invalid values include: - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo headers are not currently supported by this type. - "/invalid" - "/ " is an invalid character maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string maxItems: 64 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowHeaders cannot contain '*' alongside other methods rule: '!(''*'' in self && self.size() > 1)' allowMethods: description: |- AllowMethods indicates which HTTP methods are supported for accessing the requested resource. Valid values are any method defined by RFC9110, along with the special value `*`, which represents all HTTP methods are allowed. Method names are case-sensitive, so these values are also case-sensitive. (See https://www.rfc-editor.org/rfc/rfc2616#section-5.1.1) Multiple method names in the value of the `Access-Control-Allow-Methods` response header are separated by a comma (","). A CORS-safelisted method is a method that is `GET`, `HEAD`, or `POST`. (See https://fetch.spec.whatwg.org/#cors-safelisted-method) The CORS-safelisted methods are always allowed, regardless of whether they are specified in the `AllowMethods` field. When the `AllowMethods` field is configured with one or more methods, the gateway must return the `Access-Control-Allow-Methods` response header which value is present in the `AllowMethods` field. If the HTTP method of the `Access-Control-Request-Method` request header is not included in the list of methods specified by the response header `Access-Control-Allow-Methods`, it will present an error on the client side. If config contains the wildcard "*" in allowMethods and the request is not credentialed, the `Access-Control-Allow-Methods` response header can either use the `*` wildcard or the value of Access-Control-Request-Method from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Methods` response header. When also the `AllowCredentials` field is true and `AllowMethods` field specified with the `*` wildcard, the gateway must specify one HTTP method in the value of the Access-Control-Allow-Methods response header. The value of the header `Access-Control-Allow-Methods` is same as the `Access-Control-Request-Method` header provided by the client. If the header `Access-Control-Request-Method` is not included in the request, the gateway will omit the `Access-Control-Allow-Methods` response header, instead of specifying the `*` wildcard. Support: Extended items: enum: - GET - HEAD - POST - PUT - DELETE - CONNECT - OPTIONS - TRACE - PATCH - '*' type: string maxItems: 9 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowMethods cannot contain '*' alongside other methods rule: '!(''*'' in self && self.size() > 1)' allowOrigins: description: |- AllowOrigins indicates whether the response can be shared with requested resource from the given `Origin`. The `Origin` consists of a scheme and a host, with an optional port, and takes the form `://(:)`. Valid values for scheme are: `http` and `https`. Valid values for port are any integer between 1 and 65535 (the list of available TCP/UDP ports). Note that, if not included, port `80` is assumed for `http` scheme origins, and port `443` is assumed for `https` origins. This may affect origin matching. The host part of the origin may contain the wildcard character `*`. These wildcard characters behave as follows: * `*` is a greedy match to the _left_, including any number of DNS labels to the left of its position. This also means that `*` will include any number of period `.` characters to the left of its position. * A wildcard by itself matches all hosts. An origin value that includes _only_ the `*` character indicates requests from all `Origin`s are allowed. When the `AllowOrigins` field is configured with multiple origins, it means the server supports clients from multiple origins. If the request `Origin` matches the configured allowed origins, the gateway must return the given `Origin` and sets value of the header `Access-Control-Allow-Origin` same as the `Origin` header provided by the client. The status code of a successful response to a "preflight" request is always an OK status (i.e., 204 or 200). If the request `Origin` does not match the configured allowed origins, the gateway returns 204/200 response but doesn't set the relevant cross-origin response headers. Alternatively, the gateway responds with 403 status to the "preflight" request is denied, coupled with omitting the CORS headers. The cross-origin request fails on the client side. Therefore, the client doesn't attempt the actual cross-origin request. Conversely, if the request `Origin` matches one of the configured allowed origins, the gateway sets the response header `Access-Control-Allow-Origin` to the same value as the `Origin` header provided by the client. When config has the wildcard ("*") in allowOrigins, and the request is not credentialed (e.g., it is a preflight request), the `Access-Control-Allow-Origin` response header either contains the wildcard as well or the Origin from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Origin` response header. When also the `AllowCredentials` field is true and `AllowOrigins` field specified with the `*` wildcard, the gateway must return a single origin in the value of the `Access-Control-Allow-Origin` response header, instead of specifying the `*` wildcard. The value of the header `Access-Control-Allow-Origin` is same as the `Origin` header provided by the client. Support: Extended items: description: |- The CORSOrigin MUST NOT be a relative URI, and it MUST follow the URI syntax and encoding rules specified in RFC3986. The CORSOrigin MUST include both a scheme (e.g., "http" or "spiffe") and a scheme-specific-part, or it should be a single '*' character. URIs that include an authority MUST include a fully qualified domain name or IP address as the host. maxLength: 253 minLength: 1 pattern: (^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$) type: string maxItems: 64 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowOrigins cannot contain '*' alongside other origins rule: '!(''*'' in self && self.size() > 1)' exposeHeaders: description: |- ExposeHeaders indicates which HTTP response headers can be exposed to client-side scripts in response to a cross-origin request. A CORS-safelisted response header is an HTTP header in a CORS response that it is considered safe to expose to the client scripts. The CORS-safelisted response headers include the following headers: `Cache-Control` `Content-Language` `Content-Length` `Content-Type` `Expires` `Last-Modified` `Pragma` (See https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name) The CORS-safelisted response headers are exposed to client by default. When an HTTP header name is specified using the `ExposeHeaders` field, this additional header will be exposed as part of the response to the client. Header names are not case-sensitive. Multiple header names in the value of the `Access-Control-Expose-Headers` response header are separated by a comma (","). A wildcard indicates that the responses with all HTTP headers are exposed to clients. The `Access-Control-Expose-Headers` response header can only use `*` wildcard as value when the request is not credentialed. When the `exposeHeaders` config field contains the "*" wildcard and the request is credentialed, the gateway cannot use the `*` wildcard in the `Access-Control-Expose-Headers` response header. Support: Extended items: description: |- HTTPHeaderName is the name of an HTTP header. Valid values include: * "Authorization" * "Set-Cookie" Invalid values include: - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo headers are not currently supported by this type. - "/invalid" - "/ " is an invalid character maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string maxItems: 64 type: array x-kubernetes-list-type: set maxAge: default: 5 description: |- MaxAge indicates the duration (in seconds) for the client to cache the results of a "preflight" request. The information provided by the `Access-Control-Allow-Methods` and `Access-Control-Allow-Headers` response headers can be cached by the client until the time specified by `Access-Control-Max-Age` elapses. The default value of `Access-Control-Max-Age` response header is 5 (seconds). When the `MaxAge` field is unspecified, the gateway sets the response header "Access-Control-Max-Age: 5" by default. format: int32 minimum: 1 type: integer type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the "filter" behavior. For example, resource "myroutefilter" in group "networking.example.net"). ExtensionRef MUST NOT be used for core and extended filters. This filter can be used multiple times within the same rule. Support: Implementation-specific properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "HTTPRoute" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object externalAuth: description: |- ExternalAuth configures settings related to sending request details to an external auth service. The external service MUST authenticate the request, and MAY authorize the request as well. If there is any problem communicating with the external service, this filter MUST fail closed. Support: Extended properties: backendRef: description: |- BackendRef is a reference to a backend to send authorization requests to. The backend must speak the selected protocol (GRPC or HTTP) on the referenced port. If the backend service requires TLS, use BackendTLSPolicy to tell the implementation to supply the TLS details to be used to connect to that backend. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' forwardBody: description: |- ForwardBody controls if requests to the authorization server should include the body of the client request; and if so, how big that body is allowed to be. It is expected that implementations will buffer the request body up to `forwardBody.maxSize` bytes. Bodies over that size must be rejected with a 4xx series error (413 or 403 are common examples), and fail processing of the filter. If unset, or `forwardBody.maxSize` is set to `0`, then the body will not be forwarded. Feature Name: HTTPRouteExternalAuthForwardBody properties: maxSize: description: |- MaxSize specifies how large in bytes the largest body that will be buffered and sent to the authorization server. If the body size is larger than `maxSize`, then the body sent to the authorization server must be truncated to `maxSize` bytes. Experimental note: This behavior needs to be checked against various dataplanes; it may need to be changed. See https://github.com/kubernetes-sigs/gateway-api/pull/4001#discussion_r2291405746 for more. If 0, the body will not be sent to the authorization server. type: integer type: object grpc: description: |- GRPCAuthConfig contains configuration for communication with ext_authz protocol-speaking backends. If unset, implementations must assume the default behavior for each included field is intended. properties: allowedHeaders: description: |- AllowedRequestHeaders specifies what headers from the client request will be sent to the authorization server. If this list is empty, then all headers must be sent. If the list has entries, only those entries must be sent. items: type: string maxItems: 64 type: array x-kubernetes-list-type: set type: object http: description: |- HTTPAuthConfig contains configuration for communication with HTTP-speaking backends. If unset, implementations must assume the default behavior for each included field is intended. properties: allowedHeaders: description: |- AllowedRequestHeaders specifies what additional headers from the client request will be sent to the authorization server. The following headers must always be sent to the authorization server, regardless of this setting: * `Host` * `Method` * `Path` * `Content-Length` * `Authorization` If this list is empty, then only those headers must be sent. Note that `Content-Length` has a special behavior, in that the length sent must be correct for the actual request to the external authorization server - that is, it must reflect the actual number of bytes sent in the body of the request to the authorization server. So if the `forwardBody` stanza is unset, or `forwardBody.maxSize` is set to `0`, then `Content-Length` must be `0`. If `forwardBody.maxSize` is set to anything other than `0`, then the `Content-Length` of the authorization request must be set to the actual number of bytes forwarded. items: type: string maxItems: 64 type: array x-kubernetes-list-type: set allowedResponseHeaders: description: |- AllowedResponseHeaders specifies what headers from the authorization response will be copied into the request to the backend. If this list is empty, then all headers from the authorization server except Authority or Host must be copied. items: type: string maxItems: 64 type: array x-kubernetes-list-type: set path: description: |- Path sets the prefix that paths from the client request will have added when forwarded to the authorization server. When empty or unspecified, no prefix is added. Valid values are the same as the "value" regex for path values in the `match` stanza, and the validation regex will screen out invalid paths in the same way. Even with the validation, implementations MUST sanitize this input before using it directly. maxLength: 1024 pattern: ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$ type: string type: object protocol: description: |- ExternalAuthProtocol describes which protocol to use when communicating with an ext_authz authorization server. When this is set to GRPC, each backend must use the Envoy ext_authz protocol on the port specified in `backendRefs`. Requests and responses are defined in the protobufs explained at: https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto When this is set to HTTP, each backend must respond with a `200` status code in on a successful authorization. Any other code is considered an authorization failure. Feature Names: GRPC Support - HTTPRouteExternalAuthGRPC HTTP Support - HTTPRouteExternalAuthHTTP enum: - HTTP - GRPC type: string required: - backendRef - protocol type: object x-kubernetes-validations: - message: grpc must be specified when protocol is set to 'GRPC' rule: 'self.protocol == ''GRPC'' ? has(self.grpc) : true' - message: protocol must be 'GRPC' when grpc is set rule: 'has(self.grpc) ? self.protocol == ''GRPC'' : true' - message: http must be specified when protocol is set to 'HTTP' rule: 'self.protocol == ''HTTP'' ? has(self.http) : true' - message: protocol must be 'HTTP' when http is set rule: 'has(self.http) ? self.protocol == ''HTTP'' : true' requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request headers. Support: Core properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object requestMirror: description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. This filter can be used multiple times within the same rule. Note that not all implementations will be able to support mirroring to multiple backends. Support: Extended properties: backendRef: description: |- BackendRef references a resource where mirrored requests are sent. Mirrored requests must be sent only to a single destination endpoint within this BackendRef, irrespective of how many endpoints are present within this BackendRef. If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the "ResolvedRefs" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the "ResolvedRefs" condition on the Route is set to `status: False`, with the "RefNotPermitted" reason and not configure this backend in the underlying implementation. In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. Support: Extended for Kubernetes Service Support: Implementation-specific for any other resource properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: description: |- Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. properties: denominator: default: 100 format: int32 minimum: 1 type: integer numerator: format: int32 minimum: 0 type: integer required: - numerator type: object x-kubernetes-validations: - message: numerator must be less than or equal to denominator rule: self.numerator <= self.denominator percent: description: |- Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. format: int32 maximum: 100 minimum: 0 type: integer required: - backendRef type: object x-kubernetes-validations: - message: Only one of percent or fraction may be specified in HTTPRequestMirrorFilter rule: '!(has(self.percent) && has(self.fraction))' requestRedirect: description: |- RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. Support: Core properties: hostname: description: |- Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname in the `Host` header of the request is used. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string path: description: |- Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. Support: Extended properties: replaceFullPath: description: |- ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. maxLength: 1024 type: string replacePrefixMatch: description: |- ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch of "/xyz" would be modified to "/xyz/bar". Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in the implementation setting the Accepted Condition for the Route to `status: False`. Request Path | Prefix Match | Replace Prefix | Modified Path maxLength: 1024 type: string type: description: |- Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - ReplaceFullPath - ReplacePrefixMatch type: string required: - type type: object x-kubernetes-validations: - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) : true' - message: type must be 'ReplaceFullPath' when replaceFullPath is set rule: 'has(self.replaceFullPath) ? self.type == ''ReplaceFullPath'' : true' - message: replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch' rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) : true' - message: type must be 'ReplacePrefixMatch' when replacePrefixMatch is set rule: 'has(self.replacePrefixMatch) ? self.type == ''ReplacePrefixMatch'' : true' port: description: |- Port is the port to be used in the value of the `Location` header in the response. If no port is specified, the redirect port MUST be derived using the following rules: * If redirect scheme is not-empty, the redirect port MUST be the well-known port associated with the redirect scheme. Specifically "http" to port 80 and "https" to port 443. If the redirect scheme does not have a well-known port, the listener port of the Gateway SHOULD be used. * If redirect scheme is empty, the redirect port MUST be the Gateway Listener port. Implementations SHOULD NOT add the port number in the 'Location' header in the following cases: * A Location header that will use HTTP (whether that is determined via the Listener protocol or the Scheme field) _and_ use port 80. * A Location header that will use HTTPS (whether that is determined via the Listener protocol or the Scheme field) _and_ use port 443. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer scheme: description: |- Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. Scheme redirects can affect the port of the redirect, for more information, refer to the documentation for the port field of this filter. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. Support: Extended enum: - http - https type: string statusCode: default: 302 description: |- StatusCode is the HTTP status code to be used in response. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. Support: Core enum: - 301 - 302 - 303 - 307 - 308 type: integer type: object responseHeaderModifier: description: |- ResponseHeaderModifier defines a schema for a filter that modifies response headers. Support: Extended properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object type: description: |- Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: - Core: Filter types and their corresponding configuration defined by "Support: Core" in this package, e.g. "RequestHeaderModifier". All implementations must support core filters. - Extended: Filter types and their corresponding configuration defined by "Support: Extended" in this package, e.g. "RequestMirror". Implementers are encouraged to support extended filters. - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to "ExtensionRef" for custom filters. Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - RequestHeaderModifier - ResponseHeaderModifier - RequestMirror - RequestRedirect - URLRewrite - ExtensionRef - CORS - ExternalAuth type: string urlRewrite: description: |- URLRewrite defines a schema for a filter that modifies a request during forwarding. Support: Extended properties: hostname: description: |- Hostname is the value to be used to replace the Host header value during forwarding. Support: Extended maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string path: description: |- Path defines a path rewrite. Support: Extended properties: replaceFullPath: description: |- ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. maxLength: 1024 type: string replacePrefixMatch: description: |- ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch of "/xyz" would be modified to "/xyz/bar". Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in the implementation setting the Accepted Condition for the Route to `status: False`. Request Path | Prefix Match | Replace Prefix | Modified Path maxLength: 1024 type: string type: description: |- Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - ReplaceFullPath - ReplacePrefixMatch type: string required: - type type: object x-kubernetes-validations: - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) : true' - message: type must be 'ReplaceFullPath' when replaceFullPath is set rule: 'has(self.replaceFullPath) ? self.type == ''ReplaceFullPath'' : true' - message: replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch' rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) : true' - message: type must be 'ReplacePrefixMatch' when replacePrefixMatch is set rule: 'has(self.replacePrefixMatch) ? self.type == ''ReplacePrefixMatch'' : true' type: object required: - type type: object x-kubernetes-validations: - message: filter.cors must be nil if the filter.type is not CORS rule: '!(has(self.cors) && self.type != ''CORS'')' - message: filter.cors must be specified for CORS filter.type rule: '!(!has(self.cors) && self.type == ''CORS'')' - message: filter.requestHeaderModifier must be nil if the filter.type is not RequestHeaderModifier rule: '!(has(self.requestHeaderModifier) && self.type != ''RequestHeaderModifier'')' - message: filter.requestHeaderModifier must be specified for RequestHeaderModifier filter.type rule: '!(!has(self.requestHeaderModifier) && self.type == ''RequestHeaderModifier'')' - message: filter.responseHeaderModifier must be nil if the filter.type is not ResponseHeaderModifier rule: '!(has(self.responseHeaderModifier) && self.type != ''ResponseHeaderModifier'')' - message: filter.responseHeaderModifier must be specified for ResponseHeaderModifier filter.type rule: '!(!has(self.responseHeaderModifier) && self.type == ''ResponseHeaderModifier'')' - message: filter.requestMirror must be nil if the filter.type is not RequestMirror rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' - message: filter.requestMirror must be specified for RequestMirror filter.type rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' - message: filter.requestRedirect must be nil if the filter.type is not RequestRedirect rule: '!(has(self.requestRedirect) && self.type != ''RequestRedirect'')' - message: filter.requestRedirect must be specified for RequestRedirect filter.type rule: '!(!has(self.requestRedirect) && self.type == ''RequestRedirect'')' - message: filter.urlRewrite must be nil if the filter.type is not URLRewrite rule: '!(has(self.urlRewrite) && self.type != ''URLRewrite'')' - message: filter.urlRewrite must be specified for URLRewrite filter.type rule: '!(!has(self.urlRewrite) && self.type == ''URLRewrite'')' - message: filter.extensionRef must be nil if the filter.type is not ExtensionRef rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' - message: filter.externalAuth must be nil if the filter.type is not ExternalAuth rule: '!(has(self.externalAuth) && self.type != ''ExternalAuth'')' - message: filter.externalAuth must be specified for ExternalAuth filter.type rule: '!(!has(self.externalAuth) && self.type == ''ExternalAuth'')' maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both rule: '!(self.exists(f, f.type == ''RequestRedirect'') && self.exists(f, f.type == ''URLRewrite''))' - message: RequestHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1 - message: ResponseHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() <= 1 - message: RequestRedirect filter cannot be repeated rule: self.filter(f, f.type == 'RequestRedirect').size() <= 1 - message: URLRewrite filter cannot be repeated rule: self.filter(f, f.type == 'URLRewrite').size() <= 1 group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer weight: default: 1 description: |- Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. Support for this field varies based on the context where used. format: int32 maximum: 1000000 minimum: 0 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' maxItems: 16 type: array x-kubernetes-list-type: atomic filters: description: |- Filters define the filters that are applied to requests that match this rule. Wherever possible, implementations SHOULD implement filters in the order they are specified. Implementations MAY choose to implement this ordering strictly, rejecting any combination or order of filters that cannot be supported. If implementations choose a strict interpretation of filter ordering, they MUST clearly document that behavior. To reject an invalid combination or order of filters, implementations SHOULD consider the Route Rules with this configuration invalid. If all Route Rules in a Route are invalid, the entire Route would be considered invalid. If only a portion of Route Rules are invalid, implementations MUST set the "PartiallyInvalid" condition for the Route. Conformance-levels at this level are defined based on the type of filter: - ALL core filters MUST be supported by all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. Specifying the same filter multiple times is not supported unless explicitly indicated in the filter. All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation cannot support other combinations of filters, they must clearly document that limitation. In cases where incompatible or unsupported filters are specified and cause the `Accepted` condition to be set to status `False`, implementations may use the `IncompatibleFilters` reason to specify this configuration error. Support: Core items: description: |- HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: cors: description: |- CORS defines a schema for a filter that responds to the cross-origin request based on HTTP response header. Support: Extended properties: allowCredentials: description: |- AllowCredentials indicates whether the actual cross-origin request allows to include credentials. When set to true, the gateway will include the `Access-Control-Allow-Credentials` response header with value true (case-sensitive). When set to false or omitted the gateway will omit the header `Access-Control-Allow-Credentials` entirely (this is the standard CORS behavior). Support: Extended type: boolean allowHeaders: description: |- AllowHeaders indicates which HTTP request headers are supported for accessing the requested resource. Header names are not case-sensitive. Multiple header names in the value of the `Access-Control-Allow-Headers` response header are separated by a comma (","). When the `AllowHeaders` field is configured with one or more headers, the gateway must return the `Access-Control-Allow-Headers` response header which value is present in the `AllowHeaders` field. If any header name in the `Access-Control-Request-Headers` request header is not included in the list of header names specified by the response header `Access-Control-Allow-Headers`, it will present an error on the client side. If any header name in the `Access-Control-Allow-Headers` response header does not recognize by the client, it will also occur an error on the client side. A wildcard indicates that the requests with all HTTP headers are allowed. If config contains the wildcard "*" in allowHeaders and the request is not credentialed, the `Access-Control-Allow-Headers` response header can either use the `*` wildcard or the value of Access-Control-Request-Headers from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Headers` response header. When also the `AllowCredentials` field is true and `AllowHeaders` field is specified with the `*` wildcard, the gateway must specify one or more HTTP headers in the value of the `Access-Control-Allow-Headers` response header. The value of the header `Access-Control-Allow-Headers` is same as the `Access-Control-Request-Headers` header provided by the client. If the header `Access-Control-Request-Headers` is not included in the request, the gateway will omit the `Access-Control-Allow-Headers` response header, instead of specifying the `*` wildcard. Support: Extended items: description: |- HTTPHeaderName is the name of an HTTP header. Valid values include: * "Authorization" * "Set-Cookie" Invalid values include: - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo headers are not currently supported by this type. - "/invalid" - "/ " is an invalid character maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string maxItems: 64 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowHeaders cannot contain '*' alongside other methods rule: '!(''*'' in self && self.size() > 1)' allowMethods: description: |- AllowMethods indicates which HTTP methods are supported for accessing the requested resource. Valid values are any method defined by RFC9110, along with the special value `*`, which represents all HTTP methods are allowed. Method names are case-sensitive, so these values are also case-sensitive. (See https://www.rfc-editor.org/rfc/rfc2616#section-5.1.1) Multiple method names in the value of the `Access-Control-Allow-Methods` response header are separated by a comma (","). A CORS-safelisted method is a method that is `GET`, `HEAD`, or `POST`. (See https://fetch.spec.whatwg.org/#cors-safelisted-method) The CORS-safelisted methods are always allowed, regardless of whether they are specified in the `AllowMethods` field. When the `AllowMethods` field is configured with one or more methods, the gateway must return the `Access-Control-Allow-Methods` response header which value is present in the `AllowMethods` field. If the HTTP method of the `Access-Control-Request-Method` request header is not included in the list of methods specified by the response header `Access-Control-Allow-Methods`, it will present an error on the client side. If config contains the wildcard "*" in allowMethods and the request is not credentialed, the `Access-Control-Allow-Methods` response header can either use the `*` wildcard or the value of Access-Control-Request-Method from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Methods` response header. When also the `AllowCredentials` field is true and `AllowMethods` field specified with the `*` wildcard, the gateway must specify one HTTP method in the value of the Access-Control-Allow-Methods response header. The value of the header `Access-Control-Allow-Methods` is same as the `Access-Control-Request-Method` header provided by the client. If the header `Access-Control-Request-Method` is not included in the request, the gateway will omit the `Access-Control-Allow-Methods` response header, instead of specifying the `*` wildcard. Support: Extended items: enum: - GET - HEAD - POST - PUT - DELETE - CONNECT - OPTIONS - TRACE - PATCH - '*' type: string maxItems: 9 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowMethods cannot contain '*' alongside other methods rule: '!(''*'' in self && self.size() > 1)' allowOrigins: description: |- AllowOrigins indicates whether the response can be shared with requested resource from the given `Origin`. The `Origin` consists of a scheme and a host, with an optional port, and takes the form `://(:)`. Valid values for scheme are: `http` and `https`. Valid values for port are any integer between 1 and 65535 (the list of available TCP/UDP ports). Note that, if not included, port `80` is assumed for `http` scheme origins, and port `443` is assumed for `https` origins. This may affect origin matching. The host part of the origin may contain the wildcard character `*`. These wildcard characters behave as follows: * `*` is a greedy match to the _left_, including any number of DNS labels to the left of its position. This also means that `*` will include any number of period `.` characters to the left of its position. * A wildcard by itself matches all hosts. An origin value that includes _only_ the `*` character indicates requests from all `Origin`s are allowed. When the `AllowOrigins` field is configured with multiple origins, it means the server supports clients from multiple origins. If the request `Origin` matches the configured allowed origins, the gateway must return the given `Origin` and sets value of the header `Access-Control-Allow-Origin` same as the `Origin` header provided by the client. The status code of a successful response to a "preflight" request is always an OK status (i.e., 204 or 200). If the request `Origin` does not match the configured allowed origins, the gateway returns 204/200 response but doesn't set the relevant cross-origin response headers. Alternatively, the gateway responds with 403 status to the "preflight" request is denied, coupled with omitting the CORS headers. The cross-origin request fails on the client side. Therefore, the client doesn't attempt the actual cross-origin request. Conversely, if the request `Origin` matches one of the configured allowed origins, the gateway sets the response header `Access-Control-Allow-Origin` to the same value as the `Origin` header provided by the client. When config has the wildcard ("*") in allowOrigins, and the request is not credentialed (e.g., it is a preflight request), the `Access-Control-Allow-Origin` response header either contains the wildcard as well or the Origin from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Origin` response header. When also the `AllowCredentials` field is true and `AllowOrigins` field specified with the `*` wildcard, the gateway must return a single origin in the value of the `Access-Control-Allow-Origin` response header, instead of specifying the `*` wildcard. The value of the header `Access-Control-Allow-Origin` is same as the `Origin` header provided by the client. Support: Extended items: description: |- The CORSOrigin MUST NOT be a relative URI, and it MUST follow the URI syntax and encoding rules specified in RFC3986. The CORSOrigin MUST include both a scheme (e.g., "http" or "spiffe") and a scheme-specific-part, or it should be a single '*' character. URIs that include an authority MUST include a fully qualified domain name or IP address as the host. maxLength: 253 minLength: 1 pattern: (^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$) type: string maxItems: 64 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowOrigins cannot contain '*' alongside other origins rule: '!(''*'' in self && self.size() > 1)' exposeHeaders: description: |- ExposeHeaders indicates which HTTP response headers can be exposed to client-side scripts in response to a cross-origin request. A CORS-safelisted response header is an HTTP header in a CORS response that it is considered safe to expose to the client scripts. The CORS-safelisted response headers include the following headers: `Cache-Control` `Content-Language` `Content-Length` `Content-Type` `Expires` `Last-Modified` `Pragma` (See https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name) The CORS-safelisted response headers are exposed to client by default. When an HTTP header name is specified using the `ExposeHeaders` field, this additional header will be exposed as part of the response to the client. Header names are not case-sensitive. Multiple header names in the value of the `Access-Control-Expose-Headers` response header are separated by a comma (","). A wildcard indicates that the responses with all HTTP headers are exposed to clients. The `Access-Control-Expose-Headers` response header can only use `*` wildcard as value when the request is not credentialed. When the `exposeHeaders` config field contains the "*" wildcard and the request is credentialed, the gateway cannot use the `*` wildcard in the `Access-Control-Expose-Headers` response header. Support: Extended items: description: |- HTTPHeaderName is the name of an HTTP header. Valid values include: * "Authorization" * "Set-Cookie" Invalid values include: - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo headers are not currently supported by this type. - "/invalid" - "/ " is an invalid character maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string maxItems: 64 type: array x-kubernetes-list-type: set maxAge: default: 5 description: |- MaxAge indicates the duration (in seconds) for the client to cache the results of a "preflight" request. The information provided by the `Access-Control-Allow-Methods` and `Access-Control-Allow-Headers` response headers can be cached by the client until the time specified by `Access-Control-Max-Age` elapses. The default value of `Access-Control-Max-Age` response header is 5 (seconds). When the `MaxAge` field is unspecified, the gateway sets the response header "Access-Control-Max-Age: 5" by default. format: int32 minimum: 1 type: integer type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the "filter" behavior. For example, resource "myroutefilter" in group "networking.example.net"). ExtensionRef MUST NOT be used for core and extended filters. This filter can be used multiple times within the same rule. Support: Implementation-specific properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "HTTPRoute" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object externalAuth: description: |- ExternalAuth configures settings related to sending request details to an external auth service. The external service MUST authenticate the request, and MAY authorize the request as well. If there is any problem communicating with the external service, this filter MUST fail closed. Support: Extended properties: backendRef: description: |- BackendRef is a reference to a backend to send authorization requests to. The backend must speak the selected protocol (GRPC or HTTP) on the referenced port. If the backend service requires TLS, use BackendTLSPolicy to tell the implementation to supply the TLS details to be used to connect to that backend. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' forwardBody: description: |- ForwardBody controls if requests to the authorization server should include the body of the client request; and if so, how big that body is allowed to be. It is expected that implementations will buffer the request body up to `forwardBody.maxSize` bytes. Bodies over that size must be rejected with a 4xx series error (413 or 403 are common examples), and fail processing of the filter. If unset, or `forwardBody.maxSize` is set to `0`, then the body will not be forwarded. Feature Name: HTTPRouteExternalAuthForwardBody properties: maxSize: description: |- MaxSize specifies how large in bytes the largest body that will be buffered and sent to the authorization server. If the body size is larger than `maxSize`, then the body sent to the authorization server must be truncated to `maxSize` bytes. Experimental note: This behavior needs to be checked against various dataplanes; it may need to be changed. See https://github.com/kubernetes-sigs/gateway-api/pull/4001#discussion_r2291405746 for more. If 0, the body will not be sent to the authorization server. type: integer type: object grpc: description: |- GRPCAuthConfig contains configuration for communication with ext_authz protocol-speaking backends. If unset, implementations must assume the default behavior for each included field is intended. properties: allowedHeaders: description: |- AllowedRequestHeaders specifies what headers from the client request will be sent to the authorization server. If this list is empty, then all headers must be sent. If the list has entries, only those entries must be sent. items: type: string maxItems: 64 type: array x-kubernetes-list-type: set type: object http: description: |- HTTPAuthConfig contains configuration for communication with HTTP-speaking backends. If unset, implementations must assume the default behavior for each included field is intended. properties: allowedHeaders: description: |- AllowedRequestHeaders specifies what additional headers from the client request will be sent to the authorization server. The following headers must always be sent to the authorization server, regardless of this setting: * `Host` * `Method` * `Path` * `Content-Length` * `Authorization` If this list is empty, then only those headers must be sent. Note that `Content-Length` has a special behavior, in that the length sent must be correct for the actual request to the external authorization server - that is, it must reflect the actual number of bytes sent in the body of the request to the authorization server. So if the `forwardBody` stanza is unset, or `forwardBody.maxSize` is set to `0`, then `Content-Length` must be `0`. If `forwardBody.maxSize` is set to anything other than `0`, then the `Content-Length` of the authorization request must be set to the actual number of bytes forwarded. items: type: string maxItems: 64 type: array x-kubernetes-list-type: set allowedResponseHeaders: description: |- AllowedResponseHeaders specifies what headers from the authorization response will be copied into the request to the backend. If this list is empty, then all headers from the authorization server except Authority or Host must be copied. items: type: string maxItems: 64 type: array x-kubernetes-list-type: set path: description: |- Path sets the prefix that paths from the client request will have added when forwarded to the authorization server. When empty or unspecified, no prefix is added. Valid values are the same as the "value" regex for path values in the `match` stanza, and the validation regex will screen out invalid paths in the same way. Even with the validation, implementations MUST sanitize this input before using it directly. maxLength: 1024 pattern: ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$ type: string type: object protocol: description: |- ExternalAuthProtocol describes which protocol to use when communicating with an ext_authz authorization server. When this is set to GRPC, each backend must use the Envoy ext_authz protocol on the port specified in `backendRefs`. Requests and responses are defined in the protobufs explained at: https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto When this is set to HTTP, each backend must respond with a `200` status code in on a successful authorization. Any other code is considered an authorization failure. Feature Names: GRPC Support - HTTPRouteExternalAuthGRPC HTTP Support - HTTPRouteExternalAuthHTTP enum: - HTTP - GRPC type: string required: - backendRef - protocol type: object x-kubernetes-validations: - message: grpc must be specified when protocol is set to 'GRPC' rule: 'self.protocol == ''GRPC'' ? has(self.grpc) : true' - message: protocol must be 'GRPC' when grpc is set rule: 'has(self.grpc) ? self.protocol == ''GRPC'' : true' - message: http must be specified when protocol is set to 'HTTP' rule: 'self.protocol == ''HTTP'' ? has(self.http) : true' - message: protocol must be 'HTTP' when http is set rule: 'has(self.http) ? self.protocol == ''HTTP'' : true' requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request headers. Support: Core properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object requestMirror: description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. This filter can be used multiple times within the same rule. Note that not all implementations will be able to support mirroring to multiple backends. Support: Extended properties: backendRef: description: |- BackendRef references a resource where mirrored requests are sent. Mirrored requests must be sent only to a single destination endpoint within this BackendRef, irrespective of how many endpoints are present within this BackendRef. If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the "ResolvedRefs" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the "ResolvedRefs" condition on the Route is set to `status: False`, with the "RefNotPermitted" reason and not configure this backend in the underlying implementation. In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. Support: Extended for Kubernetes Service Support: Implementation-specific for any other resource properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: description: |- Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. properties: denominator: default: 100 format: int32 minimum: 1 type: integer numerator: format: int32 minimum: 0 type: integer required: - numerator type: object x-kubernetes-validations: - message: numerator must be less than or equal to denominator rule: self.numerator <= self.denominator percent: description: |- Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. format: int32 maximum: 100 minimum: 0 type: integer required: - backendRef type: object x-kubernetes-validations: - message: Only one of percent or fraction may be specified in HTTPRequestMirrorFilter rule: '!(has(self.percent) && has(self.fraction))' requestRedirect: description: |- RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. Support: Core properties: hostname: description: |- Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname in the `Host` header of the request is used. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string path: description: |- Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. Support: Extended properties: replaceFullPath: description: |- ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. maxLength: 1024 type: string replacePrefixMatch: description: |- ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch of "/xyz" would be modified to "/xyz/bar". Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in the implementation setting the Accepted Condition for the Route to `status: False`. Request Path | Prefix Match | Replace Prefix | Modified Path maxLength: 1024 type: string type: description: |- Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - ReplaceFullPath - ReplacePrefixMatch type: string required: - type type: object x-kubernetes-validations: - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) : true' - message: type must be 'ReplaceFullPath' when replaceFullPath is set rule: 'has(self.replaceFullPath) ? self.type == ''ReplaceFullPath'' : true' - message: replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch' rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) : true' - message: type must be 'ReplacePrefixMatch' when replacePrefixMatch is set rule: 'has(self.replacePrefixMatch) ? self.type == ''ReplacePrefixMatch'' : true' port: description: |- Port is the port to be used in the value of the `Location` header in the response. If no port is specified, the redirect port MUST be derived using the following rules: * If redirect scheme is not-empty, the redirect port MUST be the well-known port associated with the redirect scheme. Specifically "http" to port 80 and "https" to port 443. If the redirect scheme does not have a well-known port, the listener port of the Gateway SHOULD be used. * If redirect scheme is empty, the redirect port MUST be the Gateway Listener port. Implementations SHOULD NOT add the port number in the 'Location' header in the following cases: * A Location header that will use HTTP (whether that is determined via the Listener protocol or the Scheme field) _and_ use port 80. * A Location header that will use HTTPS (whether that is determined via the Listener protocol or the Scheme field) _and_ use port 443. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer scheme: description: |- Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. Scheme redirects can affect the port of the redirect, for more information, refer to the documentation for the port field of this filter. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. Support: Extended enum: - http - https type: string statusCode: default: 302 description: |- StatusCode is the HTTP status code to be used in response. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. Support: Core enum: - 301 - 302 - 303 - 307 - 308 type: integer type: object responseHeaderModifier: description: |- ResponseHeaderModifier defines a schema for a filter that modifies response headers. Support: Extended properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object type: description: |- Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: - Core: Filter types and their corresponding configuration defined by "Support: Core" in this package, e.g. "RequestHeaderModifier". All implementations must support core filters. - Extended: Filter types and their corresponding configuration defined by "Support: Extended" in this package, e.g. "RequestMirror". Implementers are encouraged to support extended filters. - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to "ExtensionRef" for custom filters. Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - RequestHeaderModifier - ResponseHeaderModifier - RequestMirror - RequestRedirect - URLRewrite - ExtensionRef - CORS - ExternalAuth type: string urlRewrite: description: |- URLRewrite defines a schema for a filter that modifies a request during forwarding. Support: Extended properties: hostname: description: |- Hostname is the value to be used to replace the Host header value during forwarding. Support: Extended maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string path: description: |- Path defines a path rewrite. Support: Extended properties: replaceFullPath: description: |- ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. maxLength: 1024 type: string replacePrefixMatch: description: |- ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch of "/xyz" would be modified to "/xyz/bar". Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in the implementation setting the Accepted Condition for the Route to `status: False`. Request Path | Prefix Match | Replace Prefix | Modified Path maxLength: 1024 type: string type: description: |- Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - ReplaceFullPath - ReplacePrefixMatch type: string required: - type type: object x-kubernetes-validations: - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) : true' - message: type must be 'ReplaceFullPath' when replaceFullPath is set rule: 'has(self.replaceFullPath) ? self.type == ''ReplaceFullPath'' : true' - message: replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch' rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) : true' - message: type must be 'ReplacePrefixMatch' when replacePrefixMatch is set rule: 'has(self.replacePrefixMatch) ? self.type == ''ReplacePrefixMatch'' : true' type: object required: - type type: object x-kubernetes-validations: - message: filter.cors must be nil if the filter.type is not CORS rule: '!(has(self.cors) && self.type != ''CORS'')' - message: filter.cors must be specified for CORS filter.type rule: '!(!has(self.cors) && self.type == ''CORS'')' - message: filter.requestHeaderModifier must be nil if the filter.type is not RequestHeaderModifier rule: '!(has(self.requestHeaderModifier) && self.type != ''RequestHeaderModifier'')' - message: filter.requestHeaderModifier must be specified for RequestHeaderModifier filter.type rule: '!(!has(self.requestHeaderModifier) && self.type == ''RequestHeaderModifier'')' - message: filter.responseHeaderModifier must be nil if the filter.type is not ResponseHeaderModifier rule: '!(has(self.responseHeaderModifier) && self.type != ''ResponseHeaderModifier'')' - message: filter.responseHeaderModifier must be specified for ResponseHeaderModifier filter.type rule: '!(!has(self.responseHeaderModifier) && self.type == ''ResponseHeaderModifier'')' - message: filter.requestMirror must be nil if the filter.type is not RequestMirror rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' - message: filter.requestMirror must be specified for RequestMirror filter.type rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' - message: filter.requestRedirect must be nil if the filter.type is not RequestRedirect rule: '!(has(self.requestRedirect) && self.type != ''RequestRedirect'')' - message: filter.requestRedirect must be specified for RequestRedirect filter.type rule: '!(!has(self.requestRedirect) && self.type == ''RequestRedirect'')' - message: filter.urlRewrite must be nil if the filter.type is not URLRewrite rule: '!(has(self.urlRewrite) && self.type != ''URLRewrite'')' - message: filter.urlRewrite must be specified for URLRewrite filter.type rule: '!(!has(self.urlRewrite) && self.type == ''URLRewrite'')' - message: filter.extensionRef must be nil if the filter.type is not ExtensionRef rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' - message: filter.externalAuth must be nil if the filter.type is not ExternalAuth rule: '!(has(self.externalAuth) && self.type != ''ExternalAuth'')' - message: filter.externalAuth must be specified for ExternalAuth filter.type rule: '!(!has(self.externalAuth) && self.type == ''ExternalAuth'')' maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both rule: '!(self.exists(f, f.type == ''RequestRedirect'') && self.exists(f, f.type == ''URLRewrite''))' - message: RequestHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1 - message: ResponseHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() <= 1 - message: RequestRedirect filter cannot be repeated rule: self.filter(f, f.type == 'RequestRedirect').size() <= 1 - message: URLRewrite filter cannot be repeated rule: self.filter(f, f.type == 'URLRewrite').size() <= 1 matches: default: - path: type: PathPrefix value: / description: |- Matches define conditions used for matching the rule against incoming HTTP requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. For example, take the following matches configuration: ``` matches: - path: value: "/foo" headers: - name: "version" value: "v2" - path: value: "/v2/foo" ``` For a request to match against this rule, a request must satisfy EITHER of the two conditions: - path prefixed with `/foo` AND contains the header `version: v2` - path prefix of `/v2/foo` See the documentation for HTTPRouteMatch on how to specify multiple match conditions that should be ANDed together. If no matches are specified, the default is a prefix path match on "/", which has the effect of matching every HTTP request. Proxy or Load Balancer routing configuration generated from HTTPRoutes MUST prioritize matches based on the following criteria, continuing on ties. Across all rules specified on applicable Routes, precedence must be given to the match having: * "Exact" path match. * "Prefix" path match with largest number of characters. * Method match. * Largest number of header matches. * Largest number of query param matches. Note: The precedence of RegularExpression path matches are implementation-specific. If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by "{namespace}/{name}". If ties still exist within an HTTPRoute, matching precedence MUST be granted to the FIRST matching rule (in list order) with a match meeting the above criteria. When no rules matching a request have been successfully attached to the parent a request is coming from, a HTTP 404 status code MUST be returned. items: description: "HTTPRouteMatch defines the predicate used to match requests to a given\naction. Multiple match types are ANDed together, i.e. the match will\nevaluate to true only if all conditions are satisfied.\n\nFor example, the match below will match a HTTP request only if its path\nstarts with `/foo` AND it contains the `version: v1` header:\n\n```\nmatch:\n\n\tpath:\n\t \ value: \"/foo\"\n\theaders:\n\t- name: \"version\"\n\t \ value \"v1\"\n\n```" properties: headers: description: |- Headers specifies HTTP request header matchers. Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route. items: description: |- HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. When a header is repeated in an HTTP request, it is implementation-specific behavior as to how this is represented. Generally, proxies should follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding processing a repeated header, with special handling for "Set-Cookie". maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string type: default: Exact description: |- Type specifies how to match against the value of the header. Support: Core (Exact) Support: Implementation-specific (RegularExpression) Since RegularExpression HeaderMatchType has implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect. enum: - Exact - RegularExpression type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map method: description: |- Method specifies HTTP method matcher. When specified, this route will be matched only if the request has the specified method. Support: Extended enum: - GET - HEAD - POST - PUT - DELETE - CONNECT - OPTIONS - TRACE - PATCH type: string path: default: type: PathPrefix value: / description: |- Path specifies a HTTP request path matcher. If this field is not specified, a default prefix match on the "/" path is provided. properties: type: default: PathPrefix description: |- Type specifies how to match against the path Value. Support: Core (Exact, PathPrefix) Support: Implementation-specific (RegularExpression) enum: - Exact - PathPrefix - RegularExpression type: string value: default: / description: Value of the HTTP path to match against. maxLength: 1024 type: string type: object x-kubernetes-validations: - message: value must be an absolute path and start with '/' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? self.value.startsWith(''/'') : true' - message: must not contain '//' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''//'') : true' - message: must not contain '/./' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''/./'') : true' - message: must not contain '/../' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''/../'') : true' - message: must not contain '%2f' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''%2f'') : true' - message: must not contain '%2F' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''%2F'') : true' - message: must not contain '#' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''#'') : true' - message: must not end with '/..' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.endsWith(''/..'') : true' - message: must not end with '/.' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.endsWith(''/.'') : true' - message: type must be one of ['Exact', 'PathPrefix', 'RegularExpression'] rule: self.type in ['Exact','PathPrefix'] || self.type == 'RegularExpression' - message: must only contain valid characters (matching ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$) for types ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? self.value.matches(r"""^(?:[-A-Za-z0-9/._~!$&''()*+,;=:@]|[%][0-9a-fA-F]{2})+$""") : true' queryParams: description: |- QueryParams specifies HTTP query parameter matchers. Multiple match values are ANDed together, meaning, a request must match all the specified query parameters to select the route. Support: Extended items: description: |- HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP query parameters. properties: name: description: |- Name is the name of the HTTP query param to be matched. This must be an exact string match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). If multiple entries specify equivalent query param names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent query param name MUST be ignored. If a query param is repeated in an HTTP request, the behavior is purposely left undefined, since different data planes have different capabilities. However, it is *recommended* that implementations should match against the first value of the param if the data plane supports it, as this behavior is expected in other load balancing contexts outside of the Gateway API. Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string type: default: Exact description: |- Type specifies how to match against the value of the query parameter. Support: Extended (Exact) Support: Implementation-specific (RegularExpression) Since RegularExpression QueryParamMatchType has Implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect. enum: - Exact - RegularExpression type: string value: description: Value is the value of HTTP query param to be matched. maxLength: 1024 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object maxItems: 64 type: array x-kubernetes-list-type: atomic name: description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. Support: Extended maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string retry: description: |- Retry defines the configuration for when to retry an HTTP request. Support: Extended properties: attempts: description: |- Attempts specifies the maximum number of times an individual request from the gateway to a backend should be retried. If the maximum number of retries has been attempted without a successful response from the backend, the Gateway MUST return an error. When this field is unspecified, the number of times to attempt to retry a backend request is implementation-specific. Support: Extended type: integer backoff: description: |- Backoff specifies the minimum duration a Gateway should wait between retry attempts and is represented in Gateway API Duration formatting. For example, setting the `rules[].retry.backoff` field to the value `100ms` will cause a backend request to first be retried approximately 100 milliseconds after timing out or receiving a response code configured to be retriable. An implementation MAY use an exponential or alternative backoff strategy for subsequent retry attempts, MAY cap the maximum backoff duration to some amount greater than the specified minimum, and MAY add arbitrary jitter to stagger requests, as long as unsuccessful backend requests are not retried before the configured minimum duration. If a Request timeout (`rules[].timeouts.request`) is configured on the route, the entire duration of the initial request and any retry attempts MUST not exceed the Request timeout duration. If any retry attempts are still in progress when the Request timeout duration has been reached, these SHOULD be canceled if possible and the Gateway MUST immediately return a timeout error. If a BackendRequest timeout (`rules[].timeouts.backendRequest`) is configured on the route, any retry attempts which reach the configured BackendRequest timeout duration without a response SHOULD be canceled if possible and the Gateway should wait for at least the specified backoff duration before attempting to retry the backend request again. If a BackendRequest timeout is _not_ configured on the route, retry attempts MAY time out after an implementation default duration, or MAY remain pending until a configured Request timeout or implementation default duration for total request time is reached. When this field is unspecified, the time to wait between retry attempts is implementation-specific. Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string codes: description: |- Codes defines the HTTP response status codes for which a backend request should be retried. Support: Extended items: description: |- HTTPRouteRetryStatusCode defines an HTTP response status code for which a backend request should be retried. Implementations MUST support the following status codes as retriable: * 500 * 502 * 503 * 504 Implementations MAY support specifying additional discrete values in the 500-599 range. Implementations MAY support specifying discrete values in the 400-499 range, which are often inadvisable to retry. maximum: 599 minimum: 400 type: integer type: array x-kubernetes-list-type: atomic type: object sessionPersistence: description: |- SessionPersistence defines and configures session persistence for the route rule. Support: Extended properties: absoluteTimeout: description: |- AbsoluteTimeout defines the absolute timeout of the persistent session. Once the AbsoluteTimeout duration has elapsed, the session becomes invalid. Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string cookieConfig: description: |- CookieConfig provides configuration settings that are specific to cookie-based session persistence. Support: Core properties: lifetimeType: default: Session description: |- LifetimeType specifies whether the cookie has a permanent or session-based lifetime. A permanent cookie persists until its specified expiry time, defined by the Expires or Max-Age cookie attributes, while a session cookie is deleted when the current session ends. When set to "Permanent", AbsoluteTimeout indicates the cookie's lifetime via the Expires or Max-Age cookie attributes and is required. When set to "Session", AbsoluteTimeout indicates the absolute lifetime of the cookie tracked by the gateway and is optional. Defaults to "Session". Support: Core for "Session" type Support: Extended for "Permanent" type enum: - Permanent - Session type: string type: object idleTimeout: description: |- IdleTimeout defines the idle timeout of the persistent session. Once the session has been idle for more than the specified IdleTimeout duration, the session becomes invalid. Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string sessionName: description: |- SessionName defines the name of the persistent session token which may be reflected in the cookie or the header. Users should avoid reusing session names to prevent unintended consequences, such as rejection or unpredictable behavior. Support: Implementation-specific maxLength: 128 type: string type: default: Cookie description: |- Type defines the type of session persistence such as through the use of a header or cookie. Defaults to cookie based session persistence. Support: Core for "Cookie" type Support: Extended for "Header" type enum: - Cookie - Header type: string type: object x-kubernetes-validations: - message: AbsoluteTimeout must be specified when cookie lifetimeType is Permanent rule: '!has(self.cookieConfig) || !has(self.cookieConfig.lifetimeType) || self.cookieConfig.lifetimeType != ''Permanent'' || has(self.absoluteTimeout)' - message: cookieConfig can only be set with type Cookie rule: '!has(self.cookieConfig) || self.type == ''Cookie''' timeouts: description: |- Timeouts defines the timeouts that can be configured for an HTTP request. Support: Extended properties: backendRequest: description: |- BackendRequest specifies a timeout for an individual request from the gateway to a backend. This covers the time from when the request first starts being sent from the gateway to when the full response has been received from the backend. Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout completely. Implementations that cannot completely disable the timeout MUST instead interpret the zero duration as the longest possible value to which the timeout can be set. An entire client HTTP transaction with a gateway, covered by the Request timeout, may result in more than one call from the gateway to the destination backend, for example, if automatic retries are supported. The value of BackendRequest must be a Gateway API Duration string as defined by GEP-2257. When this field is unspecified, its behavior is implementation-specific; when specified, the value of BackendRequest must be no more than the value of the Request timeout (since the Request timeout encompasses the BackendRequest timeout). Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string request: description: |- Request specifies the maximum duration for a gateway to respond to an HTTP request. If the gateway has not been able to respond before this deadline is met, the gateway MUST return a timeout error. For example, setting the `rules.timeouts.request` field to the value `10s` in an `HTTPRoute` will cause a timeout if a client request is taking longer than 10 seconds to complete. Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout completely. Implementations that cannot completely disable the timeout MUST instead interpret the zero duration as the longest possible value to which the timeout can be set. This timeout is intended to cover as close to the whole request-response transaction as possible although an implementation MAY choose to start the timeout after the entire request stream has been received instead of immediately after the transaction is initiated by the client. The value of Request is a Gateway API Duration string as defined by GEP-2257. When this field is unspecified, request timeout behavior is implementation-specific. Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string type: object x-kubernetes-validations: - message: backendRequest timeout cannot be longer than request timeout rule: '!(has(self.request) && has(self.backendRequest) && duration(self.request) != duration(''0s'') && duration(self.backendRequest) > duration(self.request))' type: object x-kubernetes-validations: - message: RequestRedirect filter must not be used together with backendRefs rule: '(has(self.backendRefs) && size(self.backendRefs) > 0) ? (!has(self.filters) || self.filters.all(f, !has(f.requestRedirect))): true' - message: When using RequestRedirect filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified rule: '(has(self.filters) && self.filters.exists_one(f, has(f.requestRedirect) && has(f.requestRedirect.path) && f.requestRedirect.path.type == ''ReplacePrefixMatch'' && has(f.requestRedirect.path.replacePrefixMatch))) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != ''PathPrefix'') ? false : true) : true' - message: When using URLRewrite filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified rule: '(has(self.filters) && self.filters.exists_one(f, has(f.urlRewrite) && has(f.urlRewrite.path) && f.urlRewrite.path.type == ''ReplacePrefixMatch'' && has(f.urlRewrite.path.replacePrefixMatch))) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != ''PathPrefix'') ? false : true) : true' - message: Within backendRefs, when using RequestRedirect filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified rule: '(has(self.backendRefs) && self.backendRefs.exists_one(b, (has(b.filters) && b.filters.exists_one(f, has(f.requestRedirect) && has(f.requestRedirect.path) && f.requestRedirect.path.type == ''ReplacePrefixMatch'' && has(f.requestRedirect.path.replacePrefixMatch))) )) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != ''PathPrefix'') ? false : true) : true' - message: Within backendRefs, When using URLRewrite filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified rule: '(has(self.backendRefs) && self.backendRefs.exists_one(b, (has(b.filters) && b.filters.exists_one(f, has(f.urlRewrite) && has(f.urlRewrite.path) && f.urlRewrite.path.type == ''ReplacePrefixMatch'' && has(f.urlRewrite.path.replacePrefixMatch))) )) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != ''PathPrefix'') ? false : true) : true' maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: While 16 rules and 64 matches per rule are allowed, the total number of matches across all rules in a route must be less than 128 rule: '(self.size() > 0 ? self[0].matches.size() : 0) + (self.size() > 1 ? self[1].matches.size() : 0) + (self.size() > 2 ? self[2].matches.size() : 0) + (self.size() > 3 ? self[3].matches.size() : 0) + (self.size() > 4 ? self[4].matches.size() : 0) + (self.size() > 5 ? self[5].matches.size() : 0) + (self.size() > 6 ? self[6].matches.size() : 0) + (self.size() > 7 ? self[7].matches.size() : 0) + (self.size() > 8 ? self[8].matches.size() : 0) + (self.size() > 9 ? self[9].matches.size() : 0) + (self.size() > 10 ? self[10].matches.size() : 0) + (self.size() > 11 ? self[11].matches.size() : 0) + (self.size() > 12 ? self[12].matches.size() : 0) + (self.size() > 13 ? self[13].matches.size() : 0) + (self.size() > 14 ? self[14].matches.size() : 0) + (self.size() > 15 ? self[15].matches.size() : 0) <= 128' - message: Rule name must be unique within the route rule: self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) && l1.name == l2.name)) useDefaultGateways: description: |- UseDefaultGateways indicates the default Gateway scope to use for this Route. If unset (the default) or set to None, the Route will not be attached to any default Gateway; if set, it will be attached to any default Gateway supporting the named scope, subject to the usual rules about which Routes a Gateway is allowed to claim. Think carefully before using this functionality! The set of default Gateways supporting the requested scope can change over time without any notice to the Route author, and in many situations it will not be appropriate to request a default Gateway for a given Route -- for example, a Route with specific security requirements should almost certainly not use a default Gateway. enum: - All - None type: string type: object status: description: Status defines the current state of HTTPRoute. properties: parents: description: |- Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway. items: description: |- RouteParentStatus describes the status of a route with respect to an associated Parent. properties: conditions: description: |- Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the "Accepted" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. A Route MUST be considered "Accepted" if at least one of the Route's rules is implemented by the Gateway. There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: * The Route refers to a nonexistent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace to which the controller does not have access. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string parentRef: description: |- ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object required: - conditions - controllerName - parentRef type: object maxItems: 32 type: array x-kubernetes-list-type: atomic required: - parents type: object required: - spec type: object served: true storage: true subresources: status: {} - additionalPrinterColumns: - jsonPath: .spec.hostnames name: Hostnames type: string - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1beta1 schema: openAPIV3Schema: description: |- HTTPRoute provides a way to route HTTP requests. This includes the capability to match requests by hostname, path, header, or query param. Filters can be used to specify additional processing steps. Backends specify where matching requests should be routed. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of HTTPRoute. properties: hostnames: description: |- Hostnames defines a set of hostnames that should match against the HTTP Host header to select a HTTPRoute used to process the request. Implementations MUST ignore any port value specified in the HTTP Host header while performing a match and (absent of any applicable header modification configuration) MUST forward this header unmodified to the backend. Valid values for Hostnames are determined by RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. If a hostname is specified by both the Listener and HTTPRoute, there must be at least one intersecting hostname for the HTTPRoute to be attached to the Listener. For example: * A Listener with `test.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `*.example.com`, `test.example.com`, and `foo.test.example.com` would all match. On the other hand, `example.com` and `test.example.net` would not match. Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. If both the Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the HTTPRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. If both the Listener and HTTPRoute have specified hostnames, and none match with the criteria above, then the HTTPRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. overlapping wildcard matching and exact matching hostnames), precedence must be given to rules from the HTTPRoute with the largest number of: * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. Support: Core items: description: |- Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. Hostname can be "precise" which is a domain name without the terminating dot of a network host (e.g. "foo.example.com") or "wildcard", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed. maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string maxItems: 16 type: array x-kubernetes-list-type: atomic parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. For Services, that means the Service must either be in the same namespace for a "producer" route, or the mesh implementation must support and allow "consumer" routes for the referenced Service. ReferenceGrant is not applicable for governing ParentRefs to Services - it is not possible to create a "producer" route for a Service in a different namespace from the Route. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. ParentRefs must be _distinct_. This means either that: * They select different objects. If this is the case, then parentRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, `namespace`, and `name` must be unique across all parentRef entries in the Route. * They do not select different objects, but for each optional field used, each ParentRef that selects the same object must set the same set of optional fields to different values. If one ParentRef sets a combination of optional fields, all must set the same combination. Some examples: * If one ParentRef sets `sectionName`, all ParentRefs referencing the same object must also set `sectionName`. * If one ParentRef sets `port`, all ParentRefs referencing the same object must also set `port`. * If one ParentRef sets `sectionName` and `port`, all ParentRefs referencing the same object must also set `sectionName` and `port`. It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable other kinds of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object maxItems: 32 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__)) ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''') && (!has(p1.port) || p1.port == 0) == (!has(p2.port) || p2.port == 0)): true))' - message: sectionName or port must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || ( has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)) && (((!has(p1.port) || p1.port == 0) && (!has(p2.port) || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port)))) rules: default: - matches: - path: type: PathPrefix value: / description: Rules are a list of HTTP matchers, filters and actions. items: description: |- HTTPRouteRule defines semantics for matching an HTTP request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be sent. Failure behavior here depends on how many BackendRefs are specified and how many are invalid. If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive a 500 status code. See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. When a HTTPBackendRef is invalid, 500 status codes MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive a 500 status code. For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic must receive a 500. Implementations may choose how that 50 percent is determined. When a HTTPBackendRef refers to a Service that has no ready endpoints, implementations SHOULD return a 503 for requests to that backend instead. If an implementation chooses to do this, all of the above rules for 500 responses MUST also apply for responses that return a 503. Support: Core for Kubernetes Service Support: Extended for Kubernetes ServiceImport Support: Implementation-specific for any other resource Support for weight: Core items: description: |- HTTPBackendRef defines how a HTTPRoute forwards a HTTP request. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. Implementations supporting appProtocol SHOULD recognize the Kubernetes Standard Application Protocols defined in KEP-3726. If a Service appProtocol isn't specified, an implementation MAY infer the backend protocol through its own means. Implementations MAY infer the protocol from the Route type referring to the backend Service. If a Route is not able to send traffic to the backend using the specified protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. properties: filters: description: |- Filters defined at this level should be executed if and only if the request is being forwarded to the backend defined here. Support: Implementation-specific (For broader support of filters, use the Filters field in HTTPRouteRule.) items: description: |- HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: cors: description: |- CORS defines a schema for a filter that responds to the cross-origin request based on HTTP response header. Support: Extended properties: allowCredentials: description: |- AllowCredentials indicates whether the actual cross-origin request allows to include credentials. When set to true, the gateway will include the `Access-Control-Allow-Credentials` response header with value true (case-sensitive). When set to false or omitted the gateway will omit the header `Access-Control-Allow-Credentials` entirely (this is the standard CORS behavior). Support: Extended type: boolean allowHeaders: description: |- AllowHeaders indicates which HTTP request headers are supported for accessing the requested resource. Header names are not case-sensitive. Multiple header names in the value of the `Access-Control-Allow-Headers` response header are separated by a comma (","). When the `AllowHeaders` field is configured with one or more headers, the gateway must return the `Access-Control-Allow-Headers` response header which value is present in the `AllowHeaders` field. If any header name in the `Access-Control-Request-Headers` request header is not included in the list of header names specified by the response header `Access-Control-Allow-Headers`, it will present an error on the client side. If any header name in the `Access-Control-Allow-Headers` response header does not recognize by the client, it will also occur an error on the client side. A wildcard indicates that the requests with all HTTP headers are allowed. If config contains the wildcard "*" in allowHeaders and the request is not credentialed, the `Access-Control-Allow-Headers` response header can either use the `*` wildcard or the value of Access-Control-Request-Headers from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Headers` response header. When also the `AllowCredentials` field is true and `AllowHeaders` field is specified with the `*` wildcard, the gateway must specify one or more HTTP headers in the value of the `Access-Control-Allow-Headers` response header. The value of the header `Access-Control-Allow-Headers` is same as the `Access-Control-Request-Headers` header provided by the client. If the header `Access-Control-Request-Headers` is not included in the request, the gateway will omit the `Access-Control-Allow-Headers` response header, instead of specifying the `*` wildcard. Support: Extended items: description: |- HTTPHeaderName is the name of an HTTP header. Valid values include: * "Authorization" * "Set-Cookie" Invalid values include: - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo headers are not currently supported by this type. - "/invalid" - "/ " is an invalid character maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string maxItems: 64 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowHeaders cannot contain '*' alongside other methods rule: '!(''*'' in self && self.size() > 1)' allowMethods: description: |- AllowMethods indicates which HTTP methods are supported for accessing the requested resource. Valid values are any method defined by RFC9110, along with the special value `*`, which represents all HTTP methods are allowed. Method names are case-sensitive, so these values are also case-sensitive. (See https://www.rfc-editor.org/rfc/rfc2616#section-5.1.1) Multiple method names in the value of the `Access-Control-Allow-Methods` response header are separated by a comma (","). A CORS-safelisted method is a method that is `GET`, `HEAD`, or `POST`. (See https://fetch.spec.whatwg.org/#cors-safelisted-method) The CORS-safelisted methods are always allowed, regardless of whether they are specified in the `AllowMethods` field. When the `AllowMethods` field is configured with one or more methods, the gateway must return the `Access-Control-Allow-Methods` response header which value is present in the `AllowMethods` field. If the HTTP method of the `Access-Control-Request-Method` request header is not included in the list of methods specified by the response header `Access-Control-Allow-Methods`, it will present an error on the client side. If config contains the wildcard "*" in allowMethods and the request is not credentialed, the `Access-Control-Allow-Methods` response header can either use the `*` wildcard or the value of Access-Control-Request-Method from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Methods` response header. When also the `AllowCredentials` field is true and `AllowMethods` field specified with the `*` wildcard, the gateway must specify one HTTP method in the value of the Access-Control-Allow-Methods response header. The value of the header `Access-Control-Allow-Methods` is same as the `Access-Control-Request-Method` header provided by the client. If the header `Access-Control-Request-Method` is not included in the request, the gateway will omit the `Access-Control-Allow-Methods` response header, instead of specifying the `*` wildcard. Support: Extended items: enum: - GET - HEAD - POST - PUT - DELETE - CONNECT - OPTIONS - TRACE - PATCH - '*' type: string maxItems: 9 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowMethods cannot contain '*' alongside other methods rule: '!(''*'' in self && self.size() > 1)' allowOrigins: description: |- AllowOrigins indicates whether the response can be shared with requested resource from the given `Origin`. The `Origin` consists of a scheme and a host, with an optional port, and takes the form `://(:)`. Valid values for scheme are: `http` and `https`. Valid values for port are any integer between 1 and 65535 (the list of available TCP/UDP ports). Note that, if not included, port `80` is assumed for `http` scheme origins, and port `443` is assumed for `https` origins. This may affect origin matching. The host part of the origin may contain the wildcard character `*`. These wildcard characters behave as follows: * `*` is a greedy match to the _left_, including any number of DNS labels to the left of its position. This also means that `*` will include any number of period `.` characters to the left of its position. * A wildcard by itself matches all hosts. An origin value that includes _only_ the `*` character indicates requests from all `Origin`s are allowed. When the `AllowOrigins` field is configured with multiple origins, it means the server supports clients from multiple origins. If the request `Origin` matches the configured allowed origins, the gateway must return the given `Origin` and sets value of the header `Access-Control-Allow-Origin` same as the `Origin` header provided by the client. The status code of a successful response to a "preflight" request is always an OK status (i.e., 204 or 200). If the request `Origin` does not match the configured allowed origins, the gateway returns 204/200 response but doesn't set the relevant cross-origin response headers. Alternatively, the gateway responds with 403 status to the "preflight" request is denied, coupled with omitting the CORS headers. The cross-origin request fails on the client side. Therefore, the client doesn't attempt the actual cross-origin request. Conversely, if the request `Origin` matches one of the configured allowed origins, the gateway sets the response header `Access-Control-Allow-Origin` to the same value as the `Origin` header provided by the client. When config has the wildcard ("*") in allowOrigins, and the request is not credentialed (e.g., it is a preflight request), the `Access-Control-Allow-Origin` response header either contains the wildcard as well or the Origin from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Origin` response header. When also the `AllowCredentials` field is true and `AllowOrigins` field specified with the `*` wildcard, the gateway must return a single origin in the value of the `Access-Control-Allow-Origin` response header, instead of specifying the `*` wildcard. The value of the header `Access-Control-Allow-Origin` is same as the `Origin` header provided by the client. Support: Extended items: description: |- The CORSOrigin MUST NOT be a relative URI, and it MUST follow the URI syntax and encoding rules specified in RFC3986. The CORSOrigin MUST include both a scheme (e.g., "http" or "spiffe") and a scheme-specific-part, or it should be a single '*' character. URIs that include an authority MUST include a fully qualified domain name or IP address as the host. maxLength: 253 minLength: 1 pattern: (^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$) type: string maxItems: 64 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowOrigins cannot contain '*' alongside other origins rule: '!(''*'' in self && self.size() > 1)' exposeHeaders: description: |- ExposeHeaders indicates which HTTP response headers can be exposed to client-side scripts in response to a cross-origin request. A CORS-safelisted response header is an HTTP header in a CORS response that it is considered safe to expose to the client scripts. The CORS-safelisted response headers include the following headers: `Cache-Control` `Content-Language` `Content-Length` `Content-Type` `Expires` `Last-Modified` `Pragma` (See https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name) The CORS-safelisted response headers are exposed to client by default. When an HTTP header name is specified using the `ExposeHeaders` field, this additional header will be exposed as part of the response to the client. Header names are not case-sensitive. Multiple header names in the value of the `Access-Control-Expose-Headers` response header are separated by a comma (","). A wildcard indicates that the responses with all HTTP headers are exposed to clients. The `Access-Control-Expose-Headers` response header can only use `*` wildcard as value when the request is not credentialed. When the `exposeHeaders` config field contains the "*" wildcard and the request is credentialed, the gateway cannot use the `*` wildcard in the `Access-Control-Expose-Headers` response header. Support: Extended items: description: |- HTTPHeaderName is the name of an HTTP header. Valid values include: * "Authorization" * "Set-Cookie" Invalid values include: - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo headers are not currently supported by this type. - "/invalid" - "/ " is an invalid character maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string maxItems: 64 type: array x-kubernetes-list-type: set maxAge: default: 5 description: |- MaxAge indicates the duration (in seconds) for the client to cache the results of a "preflight" request. The information provided by the `Access-Control-Allow-Methods` and `Access-Control-Allow-Headers` response headers can be cached by the client until the time specified by `Access-Control-Max-Age` elapses. The default value of `Access-Control-Max-Age` response header is 5 (seconds). When the `MaxAge` field is unspecified, the gateway sets the response header "Access-Control-Max-Age: 5" by default. format: int32 minimum: 1 type: integer type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the "filter" behavior. For example, resource "myroutefilter" in group "networking.example.net"). ExtensionRef MUST NOT be used for core and extended filters. This filter can be used multiple times within the same rule. Support: Implementation-specific properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "HTTPRoute" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object externalAuth: description: |- ExternalAuth configures settings related to sending request details to an external auth service. The external service MUST authenticate the request, and MAY authorize the request as well. If there is any problem communicating with the external service, this filter MUST fail closed. Support: Extended properties: backendRef: description: |- BackendRef is a reference to a backend to send authorization requests to. The backend must speak the selected protocol (GRPC or HTTP) on the referenced port. If the backend service requires TLS, use BackendTLSPolicy to tell the implementation to supply the TLS details to be used to connect to that backend. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' forwardBody: description: |- ForwardBody controls if requests to the authorization server should include the body of the client request; and if so, how big that body is allowed to be. It is expected that implementations will buffer the request body up to `forwardBody.maxSize` bytes. Bodies over that size must be rejected with a 4xx series error (413 or 403 are common examples), and fail processing of the filter. If unset, or `forwardBody.maxSize` is set to `0`, then the body will not be forwarded. Feature Name: HTTPRouteExternalAuthForwardBody properties: maxSize: description: |- MaxSize specifies how large in bytes the largest body that will be buffered and sent to the authorization server. If the body size is larger than `maxSize`, then the body sent to the authorization server must be truncated to `maxSize` bytes. Experimental note: This behavior needs to be checked against various dataplanes; it may need to be changed. See https://github.com/kubernetes-sigs/gateway-api/pull/4001#discussion_r2291405746 for more. If 0, the body will not be sent to the authorization server. type: integer type: object grpc: description: |- GRPCAuthConfig contains configuration for communication with ext_authz protocol-speaking backends. If unset, implementations must assume the default behavior for each included field is intended. properties: allowedHeaders: description: |- AllowedRequestHeaders specifies what headers from the client request will be sent to the authorization server. If this list is empty, then all headers must be sent. If the list has entries, only those entries must be sent. items: type: string maxItems: 64 type: array x-kubernetes-list-type: set type: object http: description: |- HTTPAuthConfig contains configuration for communication with HTTP-speaking backends. If unset, implementations must assume the default behavior for each included field is intended. properties: allowedHeaders: description: |- AllowedRequestHeaders specifies what additional headers from the client request will be sent to the authorization server. The following headers must always be sent to the authorization server, regardless of this setting: * `Host` * `Method` * `Path` * `Content-Length` * `Authorization` If this list is empty, then only those headers must be sent. Note that `Content-Length` has a special behavior, in that the length sent must be correct for the actual request to the external authorization server - that is, it must reflect the actual number of bytes sent in the body of the request to the authorization server. So if the `forwardBody` stanza is unset, or `forwardBody.maxSize` is set to `0`, then `Content-Length` must be `0`. If `forwardBody.maxSize` is set to anything other than `0`, then the `Content-Length` of the authorization request must be set to the actual number of bytes forwarded. items: type: string maxItems: 64 type: array x-kubernetes-list-type: set allowedResponseHeaders: description: |- AllowedResponseHeaders specifies what headers from the authorization response will be copied into the request to the backend. If this list is empty, then all headers from the authorization server except Authority or Host must be copied. items: type: string maxItems: 64 type: array x-kubernetes-list-type: set path: description: |- Path sets the prefix that paths from the client request will have added when forwarded to the authorization server. When empty or unspecified, no prefix is added. Valid values are the same as the "value" regex for path values in the `match` stanza, and the validation regex will screen out invalid paths in the same way. Even with the validation, implementations MUST sanitize this input before using it directly. maxLength: 1024 pattern: ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$ type: string type: object protocol: description: |- ExternalAuthProtocol describes which protocol to use when communicating with an ext_authz authorization server. When this is set to GRPC, each backend must use the Envoy ext_authz protocol on the port specified in `backendRefs`. Requests and responses are defined in the protobufs explained at: https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto When this is set to HTTP, each backend must respond with a `200` status code in on a successful authorization. Any other code is considered an authorization failure. Feature Names: GRPC Support - HTTPRouteExternalAuthGRPC HTTP Support - HTTPRouteExternalAuthHTTP enum: - HTTP - GRPC type: string required: - backendRef - protocol type: object x-kubernetes-validations: - message: grpc must be specified when protocol is set to 'GRPC' rule: 'self.protocol == ''GRPC'' ? has(self.grpc) : true' - message: protocol must be 'GRPC' when grpc is set rule: 'has(self.grpc) ? self.protocol == ''GRPC'' : true' - message: http must be specified when protocol is set to 'HTTP' rule: 'self.protocol == ''HTTP'' ? has(self.http) : true' - message: protocol must be 'HTTP' when http is set rule: 'has(self.http) ? self.protocol == ''HTTP'' : true' requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request headers. Support: Core properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object requestMirror: description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. This filter can be used multiple times within the same rule. Note that not all implementations will be able to support mirroring to multiple backends. Support: Extended properties: backendRef: description: |- BackendRef references a resource where mirrored requests are sent. Mirrored requests must be sent only to a single destination endpoint within this BackendRef, irrespective of how many endpoints are present within this BackendRef. If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the "ResolvedRefs" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the "ResolvedRefs" condition on the Route is set to `status: False`, with the "RefNotPermitted" reason and not configure this backend in the underlying implementation. In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. Support: Extended for Kubernetes Service Support: Implementation-specific for any other resource properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: description: |- Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. properties: denominator: default: 100 format: int32 minimum: 1 type: integer numerator: format: int32 minimum: 0 type: integer required: - numerator type: object x-kubernetes-validations: - message: numerator must be less than or equal to denominator rule: self.numerator <= self.denominator percent: description: |- Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. format: int32 maximum: 100 minimum: 0 type: integer required: - backendRef type: object x-kubernetes-validations: - message: Only one of percent or fraction may be specified in HTTPRequestMirrorFilter rule: '!(has(self.percent) && has(self.fraction))' requestRedirect: description: |- RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. Support: Core properties: hostname: description: |- Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname in the `Host` header of the request is used. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string path: description: |- Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. Support: Extended properties: replaceFullPath: description: |- ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. maxLength: 1024 type: string replacePrefixMatch: description: |- ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch of "/xyz" would be modified to "/xyz/bar". Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in the implementation setting the Accepted Condition for the Route to `status: False`. Request Path | Prefix Match | Replace Prefix | Modified Path maxLength: 1024 type: string type: description: |- Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - ReplaceFullPath - ReplacePrefixMatch type: string required: - type type: object x-kubernetes-validations: - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) : true' - message: type must be 'ReplaceFullPath' when replaceFullPath is set rule: 'has(self.replaceFullPath) ? self.type == ''ReplaceFullPath'' : true' - message: replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch' rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) : true' - message: type must be 'ReplacePrefixMatch' when replacePrefixMatch is set rule: 'has(self.replacePrefixMatch) ? self.type == ''ReplacePrefixMatch'' : true' port: description: |- Port is the port to be used in the value of the `Location` header in the response. If no port is specified, the redirect port MUST be derived using the following rules: * If redirect scheme is not-empty, the redirect port MUST be the well-known port associated with the redirect scheme. Specifically "http" to port 80 and "https" to port 443. If the redirect scheme does not have a well-known port, the listener port of the Gateway SHOULD be used. * If redirect scheme is empty, the redirect port MUST be the Gateway Listener port. Implementations SHOULD NOT add the port number in the 'Location' header in the following cases: * A Location header that will use HTTP (whether that is determined via the Listener protocol or the Scheme field) _and_ use port 80. * A Location header that will use HTTPS (whether that is determined via the Listener protocol or the Scheme field) _and_ use port 443. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer scheme: description: |- Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. Scheme redirects can affect the port of the redirect, for more information, refer to the documentation for the port field of this filter. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. Support: Extended enum: - http - https type: string statusCode: default: 302 description: |- StatusCode is the HTTP status code to be used in response. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. Support: Core enum: - 301 - 302 - 303 - 307 - 308 type: integer type: object responseHeaderModifier: description: |- ResponseHeaderModifier defines a schema for a filter that modifies response headers. Support: Extended properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object type: description: |- Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: - Core: Filter types and their corresponding configuration defined by "Support: Core" in this package, e.g. "RequestHeaderModifier". All implementations must support core filters. - Extended: Filter types and their corresponding configuration defined by "Support: Extended" in this package, e.g. "RequestMirror". Implementers are encouraged to support extended filters. - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to "ExtensionRef" for custom filters. Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - RequestHeaderModifier - ResponseHeaderModifier - RequestMirror - RequestRedirect - URLRewrite - ExtensionRef - CORS - ExternalAuth type: string urlRewrite: description: |- URLRewrite defines a schema for a filter that modifies a request during forwarding. Support: Extended properties: hostname: description: |- Hostname is the value to be used to replace the Host header value during forwarding. Support: Extended maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string path: description: |- Path defines a path rewrite. Support: Extended properties: replaceFullPath: description: |- ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. maxLength: 1024 type: string replacePrefixMatch: description: |- ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch of "/xyz" would be modified to "/xyz/bar". Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in the implementation setting the Accepted Condition for the Route to `status: False`. Request Path | Prefix Match | Replace Prefix | Modified Path maxLength: 1024 type: string type: description: |- Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - ReplaceFullPath - ReplacePrefixMatch type: string required: - type type: object x-kubernetes-validations: - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) : true' - message: type must be 'ReplaceFullPath' when replaceFullPath is set rule: 'has(self.replaceFullPath) ? self.type == ''ReplaceFullPath'' : true' - message: replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch' rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) : true' - message: type must be 'ReplacePrefixMatch' when replacePrefixMatch is set rule: 'has(self.replacePrefixMatch) ? self.type == ''ReplacePrefixMatch'' : true' type: object required: - type type: object x-kubernetes-validations: - message: filter.cors must be nil if the filter.type is not CORS rule: '!(has(self.cors) && self.type != ''CORS'')' - message: filter.cors must be specified for CORS filter.type rule: '!(!has(self.cors) && self.type == ''CORS'')' - message: filter.requestHeaderModifier must be nil if the filter.type is not RequestHeaderModifier rule: '!(has(self.requestHeaderModifier) && self.type != ''RequestHeaderModifier'')' - message: filter.requestHeaderModifier must be specified for RequestHeaderModifier filter.type rule: '!(!has(self.requestHeaderModifier) && self.type == ''RequestHeaderModifier'')' - message: filter.responseHeaderModifier must be nil if the filter.type is not ResponseHeaderModifier rule: '!(has(self.responseHeaderModifier) && self.type != ''ResponseHeaderModifier'')' - message: filter.responseHeaderModifier must be specified for ResponseHeaderModifier filter.type rule: '!(!has(self.responseHeaderModifier) && self.type == ''ResponseHeaderModifier'')' - message: filter.requestMirror must be nil if the filter.type is not RequestMirror rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' - message: filter.requestMirror must be specified for RequestMirror filter.type rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' - message: filter.requestRedirect must be nil if the filter.type is not RequestRedirect rule: '!(has(self.requestRedirect) && self.type != ''RequestRedirect'')' - message: filter.requestRedirect must be specified for RequestRedirect filter.type rule: '!(!has(self.requestRedirect) && self.type == ''RequestRedirect'')' - message: filter.urlRewrite must be nil if the filter.type is not URLRewrite rule: '!(has(self.urlRewrite) && self.type != ''URLRewrite'')' - message: filter.urlRewrite must be specified for URLRewrite filter.type rule: '!(!has(self.urlRewrite) && self.type == ''URLRewrite'')' - message: filter.extensionRef must be nil if the filter.type is not ExtensionRef rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' - message: filter.externalAuth must be nil if the filter.type is not ExternalAuth rule: '!(has(self.externalAuth) && self.type != ''ExternalAuth'')' - message: filter.externalAuth must be specified for ExternalAuth filter.type rule: '!(!has(self.externalAuth) && self.type == ''ExternalAuth'')' maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both rule: '!(self.exists(f, f.type == ''RequestRedirect'') && self.exists(f, f.type == ''URLRewrite''))' - message: RequestHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1 - message: ResponseHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() <= 1 - message: RequestRedirect filter cannot be repeated rule: self.filter(f, f.type == 'RequestRedirect').size() <= 1 - message: URLRewrite filter cannot be repeated rule: self.filter(f, f.type == 'URLRewrite').size() <= 1 group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer weight: default: 1 description: |- Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. Support for this field varies based on the context where used. format: int32 maximum: 1000000 minimum: 0 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' maxItems: 16 type: array x-kubernetes-list-type: atomic filters: description: |- Filters define the filters that are applied to requests that match this rule. Wherever possible, implementations SHOULD implement filters in the order they are specified. Implementations MAY choose to implement this ordering strictly, rejecting any combination or order of filters that cannot be supported. If implementations choose a strict interpretation of filter ordering, they MUST clearly document that behavior. To reject an invalid combination or order of filters, implementations SHOULD consider the Route Rules with this configuration invalid. If all Route Rules in a Route are invalid, the entire Route would be considered invalid. If only a portion of Route Rules are invalid, implementations MUST set the "PartiallyInvalid" condition for the Route. Conformance-levels at this level are defined based on the type of filter: - ALL core filters MUST be supported by all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. Specifying the same filter multiple times is not supported unless explicitly indicated in the filter. All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation cannot support other combinations of filters, they must clearly document that limitation. In cases where incompatible or unsupported filters are specified and cause the `Accepted` condition to be set to status `False`, implementations may use the `IncompatibleFilters` reason to specify this configuration error. Support: Core items: description: |- HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: cors: description: |- CORS defines a schema for a filter that responds to the cross-origin request based on HTTP response header. Support: Extended properties: allowCredentials: description: |- AllowCredentials indicates whether the actual cross-origin request allows to include credentials. When set to true, the gateway will include the `Access-Control-Allow-Credentials` response header with value true (case-sensitive). When set to false or omitted the gateway will omit the header `Access-Control-Allow-Credentials` entirely (this is the standard CORS behavior). Support: Extended type: boolean allowHeaders: description: |- AllowHeaders indicates which HTTP request headers are supported for accessing the requested resource. Header names are not case-sensitive. Multiple header names in the value of the `Access-Control-Allow-Headers` response header are separated by a comma (","). When the `AllowHeaders` field is configured with one or more headers, the gateway must return the `Access-Control-Allow-Headers` response header which value is present in the `AllowHeaders` field. If any header name in the `Access-Control-Request-Headers` request header is not included in the list of header names specified by the response header `Access-Control-Allow-Headers`, it will present an error on the client side. If any header name in the `Access-Control-Allow-Headers` response header does not recognize by the client, it will also occur an error on the client side. A wildcard indicates that the requests with all HTTP headers are allowed. If config contains the wildcard "*" in allowHeaders and the request is not credentialed, the `Access-Control-Allow-Headers` response header can either use the `*` wildcard or the value of Access-Control-Request-Headers from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Headers` response header. When also the `AllowCredentials` field is true and `AllowHeaders` field is specified with the `*` wildcard, the gateway must specify one or more HTTP headers in the value of the `Access-Control-Allow-Headers` response header. The value of the header `Access-Control-Allow-Headers` is same as the `Access-Control-Request-Headers` header provided by the client. If the header `Access-Control-Request-Headers` is not included in the request, the gateway will omit the `Access-Control-Allow-Headers` response header, instead of specifying the `*` wildcard. Support: Extended items: description: |- HTTPHeaderName is the name of an HTTP header. Valid values include: * "Authorization" * "Set-Cookie" Invalid values include: - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo headers are not currently supported by this type. - "/invalid" - "/ " is an invalid character maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string maxItems: 64 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowHeaders cannot contain '*' alongside other methods rule: '!(''*'' in self && self.size() > 1)' allowMethods: description: |- AllowMethods indicates which HTTP methods are supported for accessing the requested resource. Valid values are any method defined by RFC9110, along with the special value `*`, which represents all HTTP methods are allowed. Method names are case-sensitive, so these values are also case-sensitive. (See https://www.rfc-editor.org/rfc/rfc2616#section-5.1.1) Multiple method names in the value of the `Access-Control-Allow-Methods` response header are separated by a comma (","). A CORS-safelisted method is a method that is `GET`, `HEAD`, or `POST`. (See https://fetch.spec.whatwg.org/#cors-safelisted-method) The CORS-safelisted methods are always allowed, regardless of whether they are specified in the `AllowMethods` field. When the `AllowMethods` field is configured with one or more methods, the gateway must return the `Access-Control-Allow-Methods` response header which value is present in the `AllowMethods` field. If the HTTP method of the `Access-Control-Request-Method` request header is not included in the list of methods specified by the response header `Access-Control-Allow-Methods`, it will present an error on the client side. If config contains the wildcard "*" in allowMethods and the request is not credentialed, the `Access-Control-Allow-Methods` response header can either use the `*` wildcard or the value of Access-Control-Request-Method from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Methods` response header. When also the `AllowCredentials` field is true and `AllowMethods` field specified with the `*` wildcard, the gateway must specify one HTTP method in the value of the Access-Control-Allow-Methods response header. The value of the header `Access-Control-Allow-Methods` is same as the `Access-Control-Request-Method` header provided by the client. If the header `Access-Control-Request-Method` is not included in the request, the gateway will omit the `Access-Control-Allow-Methods` response header, instead of specifying the `*` wildcard. Support: Extended items: enum: - GET - HEAD - POST - PUT - DELETE - CONNECT - OPTIONS - TRACE - PATCH - '*' type: string maxItems: 9 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowMethods cannot contain '*' alongside other methods rule: '!(''*'' in self && self.size() > 1)' allowOrigins: description: |- AllowOrigins indicates whether the response can be shared with requested resource from the given `Origin`. The `Origin` consists of a scheme and a host, with an optional port, and takes the form `://(:)`. Valid values for scheme are: `http` and `https`. Valid values for port are any integer between 1 and 65535 (the list of available TCP/UDP ports). Note that, if not included, port `80` is assumed for `http` scheme origins, and port `443` is assumed for `https` origins. This may affect origin matching. The host part of the origin may contain the wildcard character `*`. These wildcard characters behave as follows: * `*` is a greedy match to the _left_, including any number of DNS labels to the left of its position. This also means that `*` will include any number of period `.` characters to the left of its position. * A wildcard by itself matches all hosts. An origin value that includes _only_ the `*` character indicates requests from all `Origin`s are allowed. When the `AllowOrigins` field is configured with multiple origins, it means the server supports clients from multiple origins. If the request `Origin` matches the configured allowed origins, the gateway must return the given `Origin` and sets value of the header `Access-Control-Allow-Origin` same as the `Origin` header provided by the client. The status code of a successful response to a "preflight" request is always an OK status (i.e., 204 or 200). If the request `Origin` does not match the configured allowed origins, the gateway returns 204/200 response but doesn't set the relevant cross-origin response headers. Alternatively, the gateway responds with 403 status to the "preflight" request is denied, coupled with omitting the CORS headers. The cross-origin request fails on the client side. Therefore, the client doesn't attempt the actual cross-origin request. Conversely, if the request `Origin` matches one of the configured allowed origins, the gateway sets the response header `Access-Control-Allow-Origin` to the same value as the `Origin` header provided by the client. When config has the wildcard ("*") in allowOrigins, and the request is not credentialed (e.g., it is a preflight request), the `Access-Control-Allow-Origin` response header either contains the wildcard as well or the Origin from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Origin` response header. When also the `AllowCredentials` field is true and `AllowOrigins` field specified with the `*` wildcard, the gateway must return a single origin in the value of the `Access-Control-Allow-Origin` response header, instead of specifying the `*` wildcard. The value of the header `Access-Control-Allow-Origin` is same as the `Origin` header provided by the client. Support: Extended items: description: |- The CORSOrigin MUST NOT be a relative URI, and it MUST follow the URI syntax and encoding rules specified in RFC3986. The CORSOrigin MUST include both a scheme (e.g., "http" or "spiffe") and a scheme-specific-part, or it should be a single '*' character. URIs that include an authority MUST include a fully qualified domain name or IP address as the host. maxLength: 253 minLength: 1 pattern: (^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$) type: string maxItems: 64 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowOrigins cannot contain '*' alongside other origins rule: '!(''*'' in self && self.size() > 1)' exposeHeaders: description: |- ExposeHeaders indicates which HTTP response headers can be exposed to client-side scripts in response to a cross-origin request. A CORS-safelisted response header is an HTTP header in a CORS response that it is considered safe to expose to the client scripts. The CORS-safelisted response headers include the following headers: `Cache-Control` `Content-Language` `Content-Length` `Content-Type` `Expires` `Last-Modified` `Pragma` (See https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name) The CORS-safelisted response headers are exposed to client by default. When an HTTP header name is specified using the `ExposeHeaders` field, this additional header will be exposed as part of the response to the client. Header names are not case-sensitive. Multiple header names in the value of the `Access-Control-Expose-Headers` response header are separated by a comma (","). A wildcard indicates that the responses with all HTTP headers are exposed to clients. The `Access-Control-Expose-Headers` response header can only use `*` wildcard as value when the request is not credentialed. When the `exposeHeaders` config field contains the "*" wildcard and the request is credentialed, the gateway cannot use the `*` wildcard in the `Access-Control-Expose-Headers` response header. Support: Extended items: description: |- HTTPHeaderName is the name of an HTTP header. Valid values include: * "Authorization" * "Set-Cookie" Invalid values include: - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo headers are not currently supported by this type. - "/invalid" - "/ " is an invalid character maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string maxItems: 64 type: array x-kubernetes-list-type: set maxAge: default: 5 description: |- MaxAge indicates the duration (in seconds) for the client to cache the results of a "preflight" request. The information provided by the `Access-Control-Allow-Methods` and `Access-Control-Allow-Headers` response headers can be cached by the client until the time specified by `Access-Control-Max-Age` elapses. The default value of `Access-Control-Max-Age` response header is 5 (seconds). When the `MaxAge` field is unspecified, the gateway sets the response header "Access-Control-Max-Age: 5" by default. format: int32 minimum: 1 type: integer type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the "filter" behavior. For example, resource "myroutefilter" in group "networking.example.net"). ExtensionRef MUST NOT be used for core and extended filters. This filter can be used multiple times within the same rule. Support: Implementation-specific properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "HTTPRoute" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object externalAuth: description: |- ExternalAuth configures settings related to sending request details to an external auth service. The external service MUST authenticate the request, and MAY authorize the request as well. If there is any problem communicating with the external service, this filter MUST fail closed. Support: Extended properties: backendRef: description: |- BackendRef is a reference to a backend to send authorization requests to. The backend must speak the selected protocol (GRPC or HTTP) on the referenced port. If the backend service requires TLS, use BackendTLSPolicy to tell the implementation to supply the TLS details to be used to connect to that backend. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' forwardBody: description: |- ForwardBody controls if requests to the authorization server should include the body of the client request; and if so, how big that body is allowed to be. It is expected that implementations will buffer the request body up to `forwardBody.maxSize` bytes. Bodies over that size must be rejected with a 4xx series error (413 or 403 are common examples), and fail processing of the filter. If unset, or `forwardBody.maxSize` is set to `0`, then the body will not be forwarded. Feature Name: HTTPRouteExternalAuthForwardBody properties: maxSize: description: |- MaxSize specifies how large in bytes the largest body that will be buffered and sent to the authorization server. If the body size is larger than `maxSize`, then the body sent to the authorization server must be truncated to `maxSize` bytes. Experimental note: This behavior needs to be checked against various dataplanes; it may need to be changed. See https://github.com/kubernetes-sigs/gateway-api/pull/4001#discussion_r2291405746 for more. If 0, the body will not be sent to the authorization server. type: integer type: object grpc: description: |- GRPCAuthConfig contains configuration for communication with ext_authz protocol-speaking backends. If unset, implementations must assume the default behavior for each included field is intended. properties: allowedHeaders: description: |- AllowedRequestHeaders specifies what headers from the client request will be sent to the authorization server. If this list is empty, then all headers must be sent. If the list has entries, only those entries must be sent. items: type: string maxItems: 64 type: array x-kubernetes-list-type: set type: object http: description: |- HTTPAuthConfig contains configuration for communication with HTTP-speaking backends. If unset, implementations must assume the default behavior for each included field is intended. properties: allowedHeaders: description: |- AllowedRequestHeaders specifies what additional headers from the client request will be sent to the authorization server. The following headers must always be sent to the authorization server, regardless of this setting: * `Host` * `Method` * `Path` * `Content-Length` * `Authorization` If this list is empty, then only those headers must be sent. Note that `Content-Length` has a special behavior, in that the length sent must be correct for the actual request to the external authorization server - that is, it must reflect the actual number of bytes sent in the body of the request to the authorization server. So if the `forwardBody` stanza is unset, or `forwardBody.maxSize` is set to `0`, then `Content-Length` must be `0`. If `forwardBody.maxSize` is set to anything other than `0`, then the `Content-Length` of the authorization request must be set to the actual number of bytes forwarded. items: type: string maxItems: 64 type: array x-kubernetes-list-type: set allowedResponseHeaders: description: |- AllowedResponseHeaders specifies what headers from the authorization response will be copied into the request to the backend. If this list is empty, then all headers from the authorization server except Authority or Host must be copied. items: type: string maxItems: 64 type: array x-kubernetes-list-type: set path: description: |- Path sets the prefix that paths from the client request will have added when forwarded to the authorization server. When empty or unspecified, no prefix is added. Valid values are the same as the "value" regex for path values in the `match` stanza, and the validation regex will screen out invalid paths in the same way. Even with the validation, implementations MUST sanitize this input before using it directly. maxLength: 1024 pattern: ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$ type: string type: object protocol: description: |- ExternalAuthProtocol describes which protocol to use when communicating with an ext_authz authorization server. When this is set to GRPC, each backend must use the Envoy ext_authz protocol on the port specified in `backendRefs`. Requests and responses are defined in the protobufs explained at: https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/auth/v3/external_auth.proto When this is set to HTTP, each backend must respond with a `200` status code in on a successful authorization. Any other code is considered an authorization failure. Feature Names: GRPC Support - HTTPRouteExternalAuthGRPC HTTP Support - HTTPRouteExternalAuthHTTP enum: - HTTP - GRPC type: string required: - backendRef - protocol type: object x-kubernetes-validations: - message: grpc must be specified when protocol is set to 'GRPC' rule: 'self.protocol == ''GRPC'' ? has(self.grpc) : true' - message: protocol must be 'GRPC' when grpc is set rule: 'has(self.grpc) ? self.protocol == ''GRPC'' : true' - message: http must be specified when protocol is set to 'HTTP' rule: 'self.protocol == ''HTTP'' ? has(self.http) : true' - message: protocol must be 'HTTP' when http is set rule: 'has(self.http) ? self.protocol == ''HTTP'' : true' requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request headers. Support: Core properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object requestMirror: description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. This filter can be used multiple times within the same rule. Note that not all implementations will be able to support mirroring to multiple backends. Support: Extended properties: backendRef: description: |- BackendRef references a resource where mirrored requests are sent. Mirrored requests must be sent only to a single destination endpoint within this BackendRef, irrespective of how many endpoints are present within this BackendRef. If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the "ResolvedRefs" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the "ResolvedRefs" condition on the Route is set to `status: False`, with the "RefNotPermitted" reason and not configure this backend in the underlying implementation. In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. Support: Extended for Kubernetes Service Support: Implementation-specific for any other resource properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: description: |- Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. properties: denominator: default: 100 format: int32 minimum: 1 type: integer numerator: format: int32 minimum: 0 type: integer required: - numerator type: object x-kubernetes-validations: - message: numerator must be less than or equal to denominator rule: self.numerator <= self.denominator percent: description: |- Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. format: int32 maximum: 100 minimum: 0 type: integer required: - backendRef type: object x-kubernetes-validations: - message: Only one of percent or fraction may be specified in HTTPRequestMirrorFilter rule: '!(has(self.percent) && has(self.fraction))' requestRedirect: description: |- RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. Support: Core properties: hostname: description: |- Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname in the `Host` header of the request is used. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string path: description: |- Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. Support: Extended properties: replaceFullPath: description: |- ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. maxLength: 1024 type: string replacePrefixMatch: description: |- ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch of "/xyz" would be modified to "/xyz/bar". Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in the implementation setting the Accepted Condition for the Route to `status: False`. Request Path | Prefix Match | Replace Prefix | Modified Path maxLength: 1024 type: string type: description: |- Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - ReplaceFullPath - ReplacePrefixMatch type: string required: - type type: object x-kubernetes-validations: - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) : true' - message: type must be 'ReplaceFullPath' when replaceFullPath is set rule: 'has(self.replaceFullPath) ? self.type == ''ReplaceFullPath'' : true' - message: replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch' rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) : true' - message: type must be 'ReplacePrefixMatch' when replacePrefixMatch is set rule: 'has(self.replacePrefixMatch) ? self.type == ''ReplacePrefixMatch'' : true' port: description: |- Port is the port to be used in the value of the `Location` header in the response. If no port is specified, the redirect port MUST be derived using the following rules: * If redirect scheme is not-empty, the redirect port MUST be the well-known port associated with the redirect scheme. Specifically "http" to port 80 and "https" to port 443. If the redirect scheme does not have a well-known port, the listener port of the Gateway SHOULD be used. * If redirect scheme is empty, the redirect port MUST be the Gateway Listener port. Implementations SHOULD NOT add the port number in the 'Location' header in the following cases: * A Location header that will use HTTP (whether that is determined via the Listener protocol or the Scheme field) _and_ use port 80. * A Location header that will use HTTPS (whether that is determined via the Listener protocol or the Scheme field) _and_ use port 443. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer scheme: description: |- Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. Scheme redirects can affect the port of the redirect, for more information, refer to the documentation for the port field of this filter. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. Support: Extended enum: - http - https type: string statusCode: default: 302 description: |- StatusCode is the HTTP status code to be used in response. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. Support: Core enum: - 301 - 302 - 303 - 307 - 308 type: integer type: object responseHeaderModifier: description: |- ResponseHeaderModifier defines a schema for a filter that modifies response headers. Support: Extended properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object type: description: |- Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: - Core: Filter types and their corresponding configuration defined by "Support: Core" in this package, e.g. "RequestHeaderModifier". All implementations must support core filters. - Extended: Filter types and their corresponding configuration defined by "Support: Extended" in this package, e.g. "RequestMirror". Implementers are encouraged to support extended filters. - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to "ExtensionRef" for custom filters. Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - RequestHeaderModifier - ResponseHeaderModifier - RequestMirror - RequestRedirect - URLRewrite - ExtensionRef - CORS - ExternalAuth type: string urlRewrite: description: |- URLRewrite defines a schema for a filter that modifies a request during forwarding. Support: Extended properties: hostname: description: |- Hostname is the value to be used to replace the Host header value during forwarding. Support: Extended maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string path: description: |- Path defines a path rewrite. Support: Extended properties: replaceFullPath: description: |- ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. maxLength: 1024 type: string replacePrefixMatch: description: |- ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch of "/xyz" would be modified to "/xyz/bar". Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in the implementation setting the Accepted Condition for the Route to `status: False`. Request Path | Prefix Match | Replace Prefix | Modified Path maxLength: 1024 type: string type: description: |- Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - ReplaceFullPath - ReplacePrefixMatch type: string required: - type type: object x-kubernetes-validations: - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) : true' - message: type must be 'ReplaceFullPath' when replaceFullPath is set rule: 'has(self.replaceFullPath) ? self.type == ''ReplaceFullPath'' : true' - message: replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch' rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) : true' - message: type must be 'ReplacePrefixMatch' when replacePrefixMatch is set rule: 'has(self.replacePrefixMatch) ? self.type == ''ReplacePrefixMatch'' : true' type: object required: - type type: object x-kubernetes-validations: - message: filter.cors must be nil if the filter.type is not CORS rule: '!(has(self.cors) && self.type != ''CORS'')' - message: filter.cors must be specified for CORS filter.type rule: '!(!has(self.cors) && self.type == ''CORS'')' - message: filter.requestHeaderModifier must be nil if the filter.type is not RequestHeaderModifier rule: '!(has(self.requestHeaderModifier) && self.type != ''RequestHeaderModifier'')' - message: filter.requestHeaderModifier must be specified for RequestHeaderModifier filter.type rule: '!(!has(self.requestHeaderModifier) && self.type == ''RequestHeaderModifier'')' - message: filter.responseHeaderModifier must be nil if the filter.type is not ResponseHeaderModifier rule: '!(has(self.responseHeaderModifier) && self.type != ''ResponseHeaderModifier'')' - message: filter.responseHeaderModifier must be specified for ResponseHeaderModifier filter.type rule: '!(!has(self.responseHeaderModifier) && self.type == ''ResponseHeaderModifier'')' - message: filter.requestMirror must be nil if the filter.type is not RequestMirror rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' - message: filter.requestMirror must be specified for RequestMirror filter.type rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' - message: filter.requestRedirect must be nil if the filter.type is not RequestRedirect rule: '!(has(self.requestRedirect) && self.type != ''RequestRedirect'')' - message: filter.requestRedirect must be specified for RequestRedirect filter.type rule: '!(!has(self.requestRedirect) && self.type == ''RequestRedirect'')' - message: filter.urlRewrite must be nil if the filter.type is not URLRewrite rule: '!(has(self.urlRewrite) && self.type != ''URLRewrite'')' - message: filter.urlRewrite must be specified for URLRewrite filter.type rule: '!(!has(self.urlRewrite) && self.type == ''URLRewrite'')' - message: filter.extensionRef must be nil if the filter.type is not ExtensionRef rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' - message: filter.externalAuth must be nil if the filter.type is not ExternalAuth rule: '!(has(self.externalAuth) && self.type != ''ExternalAuth'')' - message: filter.externalAuth must be specified for ExternalAuth filter.type rule: '!(!has(self.externalAuth) && self.type == ''ExternalAuth'')' maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both rule: '!(self.exists(f, f.type == ''RequestRedirect'') && self.exists(f, f.type == ''URLRewrite''))' - message: RequestHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1 - message: ResponseHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() <= 1 - message: RequestRedirect filter cannot be repeated rule: self.filter(f, f.type == 'RequestRedirect').size() <= 1 - message: URLRewrite filter cannot be repeated rule: self.filter(f, f.type == 'URLRewrite').size() <= 1 matches: default: - path: type: PathPrefix value: / description: |- Matches define conditions used for matching the rule against incoming HTTP requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. For example, take the following matches configuration: ``` matches: - path: value: "/foo" headers: - name: "version" value: "v2" - path: value: "/v2/foo" ``` For a request to match against this rule, a request must satisfy EITHER of the two conditions: - path prefixed with `/foo` AND contains the header `version: v2` - path prefix of `/v2/foo` See the documentation for HTTPRouteMatch on how to specify multiple match conditions that should be ANDed together. If no matches are specified, the default is a prefix path match on "/", which has the effect of matching every HTTP request. Proxy or Load Balancer routing configuration generated from HTTPRoutes MUST prioritize matches based on the following criteria, continuing on ties. Across all rules specified on applicable Routes, precedence must be given to the match having: * "Exact" path match. * "Prefix" path match with largest number of characters. * Method match. * Largest number of header matches. * Largest number of query param matches. Note: The precedence of RegularExpression path matches are implementation-specific. If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by "{namespace}/{name}". If ties still exist within an HTTPRoute, matching precedence MUST be granted to the FIRST matching rule (in list order) with a match meeting the above criteria. When no rules matching a request have been successfully attached to the parent a request is coming from, a HTTP 404 status code MUST be returned. items: description: "HTTPRouteMatch defines the predicate used to match requests to a given\naction. Multiple match types are ANDed together, i.e. the match will\nevaluate to true only if all conditions are satisfied.\n\nFor example, the match below will match a HTTP request only if its path\nstarts with `/foo` AND it contains the `version: v1` header:\n\n```\nmatch:\n\n\tpath:\n\t \ value: \"/foo\"\n\theaders:\n\t- name: \"version\"\n\t \ value \"v1\"\n\n```" properties: headers: description: |- Headers specifies HTTP request header matchers. Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route. items: description: |- HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. When a header is repeated in an HTTP request, it is implementation-specific behavior as to how this is represented. Generally, proxies should follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding processing a repeated header, with special handling for "Set-Cookie". maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string type: default: Exact description: |- Type specifies how to match against the value of the header. Support: Core (Exact) Support: Implementation-specific (RegularExpression) Since RegularExpression HeaderMatchType has implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect. enum: - Exact - RegularExpression type: string value: description: |- Value is the value of HTTP Header to be matched. Must consist of printable US-ASCII characters, optionally separated by single tabs or spaces. See: https://tools.ietf.org/html/rfc7230#section-3.2 maxLength: 4096 minLength: 1 pattern: ^[!-~]+([\t ]?[!-~]+)*$ type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map method: description: |- Method specifies HTTP method matcher. When specified, this route will be matched only if the request has the specified method. Support: Extended enum: - GET - HEAD - POST - PUT - DELETE - CONNECT - OPTIONS - TRACE - PATCH type: string path: default: type: PathPrefix value: / description: |- Path specifies a HTTP request path matcher. If this field is not specified, a default prefix match on the "/" path is provided. properties: type: default: PathPrefix description: |- Type specifies how to match against the path Value. Support: Core (Exact, PathPrefix) Support: Implementation-specific (RegularExpression) enum: - Exact - PathPrefix - RegularExpression type: string value: default: / description: Value of the HTTP path to match against. maxLength: 1024 type: string type: object x-kubernetes-validations: - message: value must be an absolute path and start with '/' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? self.value.startsWith(''/'') : true' - message: must not contain '//' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''//'') : true' - message: must not contain '/./' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''/./'') : true' - message: must not contain '/../' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''/../'') : true' - message: must not contain '%2f' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''%2f'') : true' - message: must not contain '%2F' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''%2F'') : true' - message: must not contain '#' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''#'') : true' - message: must not end with '/..' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.endsWith(''/..'') : true' - message: must not end with '/.' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.endsWith(''/.'') : true' - message: type must be one of ['Exact', 'PathPrefix', 'RegularExpression'] rule: self.type in ['Exact','PathPrefix'] || self.type == 'RegularExpression' - message: must only contain valid characters (matching ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$) for types ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? self.value.matches(r"""^(?:[-A-Za-z0-9/._~!$&''()*+,;=:@]|[%][0-9a-fA-F]{2})+$""") : true' queryParams: description: |- QueryParams specifies HTTP query parameter matchers. Multiple match values are ANDed together, meaning, a request must match all the specified query parameters to select the route. Support: Extended items: description: |- HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP query parameters. properties: name: description: |- Name is the name of the HTTP query param to be matched. This must be an exact string match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). If multiple entries specify equivalent query param names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent query param name MUST be ignored. If a query param is repeated in an HTTP request, the behavior is purposely left undefined, since different data planes have different capabilities. However, it is *recommended* that implementations should match against the first value of the param if the data plane supports it, as this behavior is expected in other load balancing contexts outside of the Gateway API. Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string type: default: Exact description: |- Type specifies how to match against the value of the query parameter. Support: Extended (Exact) Support: Implementation-specific (RegularExpression) Since RegularExpression QueryParamMatchType has Implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect. enum: - Exact - RegularExpression type: string value: description: Value is the value of HTTP query param to be matched. maxLength: 1024 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object maxItems: 64 type: array x-kubernetes-list-type: atomic name: description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. Support: Extended maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string retry: description: |- Retry defines the configuration for when to retry an HTTP request. Support: Extended properties: attempts: description: |- Attempts specifies the maximum number of times an individual request from the gateway to a backend should be retried. If the maximum number of retries has been attempted without a successful response from the backend, the Gateway MUST return an error. When this field is unspecified, the number of times to attempt to retry a backend request is implementation-specific. Support: Extended type: integer backoff: description: |- Backoff specifies the minimum duration a Gateway should wait between retry attempts and is represented in Gateway API Duration formatting. For example, setting the `rules[].retry.backoff` field to the value `100ms` will cause a backend request to first be retried approximately 100 milliseconds after timing out or receiving a response code configured to be retriable. An implementation MAY use an exponential or alternative backoff strategy for subsequent retry attempts, MAY cap the maximum backoff duration to some amount greater than the specified minimum, and MAY add arbitrary jitter to stagger requests, as long as unsuccessful backend requests are not retried before the configured minimum duration. If a Request timeout (`rules[].timeouts.request`) is configured on the route, the entire duration of the initial request and any retry attempts MUST not exceed the Request timeout duration. If any retry attempts are still in progress when the Request timeout duration has been reached, these SHOULD be canceled if possible and the Gateway MUST immediately return a timeout error. If a BackendRequest timeout (`rules[].timeouts.backendRequest`) is configured on the route, any retry attempts which reach the configured BackendRequest timeout duration without a response SHOULD be canceled if possible and the Gateway should wait for at least the specified backoff duration before attempting to retry the backend request again. If a BackendRequest timeout is _not_ configured on the route, retry attempts MAY time out after an implementation default duration, or MAY remain pending until a configured Request timeout or implementation default duration for total request time is reached. When this field is unspecified, the time to wait between retry attempts is implementation-specific. Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string codes: description: |- Codes defines the HTTP response status codes for which a backend request should be retried. Support: Extended items: description: |- HTTPRouteRetryStatusCode defines an HTTP response status code for which a backend request should be retried. Implementations MUST support the following status codes as retriable: * 500 * 502 * 503 * 504 Implementations MAY support specifying additional discrete values in the 500-599 range. Implementations MAY support specifying discrete values in the 400-499 range, which are often inadvisable to retry. maximum: 599 minimum: 400 type: integer type: array x-kubernetes-list-type: atomic type: object sessionPersistence: description: |- SessionPersistence defines and configures session persistence for the route rule. Support: Extended properties: absoluteTimeout: description: |- AbsoluteTimeout defines the absolute timeout of the persistent session. Once the AbsoluteTimeout duration has elapsed, the session becomes invalid. Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string cookieConfig: description: |- CookieConfig provides configuration settings that are specific to cookie-based session persistence. Support: Core properties: lifetimeType: default: Session description: |- LifetimeType specifies whether the cookie has a permanent or session-based lifetime. A permanent cookie persists until its specified expiry time, defined by the Expires or Max-Age cookie attributes, while a session cookie is deleted when the current session ends. When set to "Permanent", AbsoluteTimeout indicates the cookie's lifetime via the Expires or Max-Age cookie attributes and is required. When set to "Session", AbsoluteTimeout indicates the absolute lifetime of the cookie tracked by the gateway and is optional. Defaults to "Session". Support: Core for "Session" type Support: Extended for "Permanent" type enum: - Permanent - Session type: string type: object idleTimeout: description: |- IdleTimeout defines the idle timeout of the persistent session. Once the session has been idle for more than the specified IdleTimeout duration, the session becomes invalid. Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string sessionName: description: |- SessionName defines the name of the persistent session token which may be reflected in the cookie or the header. Users should avoid reusing session names to prevent unintended consequences, such as rejection or unpredictable behavior. Support: Implementation-specific maxLength: 128 type: string type: default: Cookie description: |- Type defines the type of session persistence such as through the use of a header or cookie. Defaults to cookie based session persistence. Support: Core for "Cookie" type Support: Extended for "Header" type enum: - Cookie - Header type: string type: object x-kubernetes-validations: - message: AbsoluteTimeout must be specified when cookie lifetimeType is Permanent rule: '!has(self.cookieConfig) || !has(self.cookieConfig.lifetimeType) || self.cookieConfig.lifetimeType != ''Permanent'' || has(self.absoluteTimeout)' - message: cookieConfig can only be set with type Cookie rule: '!has(self.cookieConfig) || self.type == ''Cookie''' timeouts: description: |- Timeouts defines the timeouts that can be configured for an HTTP request. Support: Extended properties: backendRequest: description: |- BackendRequest specifies a timeout for an individual request from the gateway to a backend. This covers the time from when the request first starts being sent from the gateway to when the full response has been received from the backend. Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout completely. Implementations that cannot completely disable the timeout MUST instead interpret the zero duration as the longest possible value to which the timeout can be set. An entire client HTTP transaction with a gateway, covered by the Request timeout, may result in more than one call from the gateway to the destination backend, for example, if automatic retries are supported. The value of BackendRequest must be a Gateway API Duration string as defined by GEP-2257. When this field is unspecified, its behavior is implementation-specific; when specified, the value of BackendRequest must be no more than the value of the Request timeout (since the Request timeout encompasses the BackendRequest timeout). Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string request: description: |- Request specifies the maximum duration for a gateway to respond to an HTTP request. If the gateway has not been able to respond before this deadline is met, the gateway MUST return a timeout error. For example, setting the `rules.timeouts.request` field to the value `10s` in an `HTTPRoute` will cause a timeout if a client request is taking longer than 10 seconds to complete. Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout completely. Implementations that cannot completely disable the timeout MUST instead interpret the zero duration as the longest possible value to which the timeout can be set. This timeout is intended to cover as close to the whole request-response transaction as possible although an implementation MAY choose to start the timeout after the entire request stream has been received instead of immediately after the transaction is initiated by the client. The value of Request is a Gateway API Duration string as defined by GEP-2257. When this field is unspecified, request timeout behavior is implementation-specific. Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string type: object x-kubernetes-validations: - message: backendRequest timeout cannot be longer than request timeout rule: '!(has(self.request) && has(self.backendRequest) && duration(self.request) != duration(''0s'') && duration(self.backendRequest) > duration(self.request))' type: object x-kubernetes-validations: - message: RequestRedirect filter must not be used together with backendRefs rule: '(has(self.backendRefs) && size(self.backendRefs) > 0) ? (!has(self.filters) || self.filters.all(f, !has(f.requestRedirect))): true' - message: When using RequestRedirect filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified rule: '(has(self.filters) && self.filters.exists_one(f, has(f.requestRedirect) && has(f.requestRedirect.path) && f.requestRedirect.path.type == ''ReplacePrefixMatch'' && has(f.requestRedirect.path.replacePrefixMatch))) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != ''PathPrefix'') ? false : true) : true' - message: When using URLRewrite filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified rule: '(has(self.filters) && self.filters.exists_one(f, has(f.urlRewrite) && has(f.urlRewrite.path) && f.urlRewrite.path.type == ''ReplacePrefixMatch'' && has(f.urlRewrite.path.replacePrefixMatch))) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != ''PathPrefix'') ? false : true) : true' - message: Within backendRefs, when using RequestRedirect filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified rule: '(has(self.backendRefs) && self.backendRefs.exists_one(b, (has(b.filters) && b.filters.exists_one(f, has(f.requestRedirect) && has(f.requestRedirect.path) && f.requestRedirect.path.type == ''ReplacePrefixMatch'' && has(f.requestRedirect.path.replacePrefixMatch))) )) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != ''PathPrefix'') ? false : true) : true' - message: Within backendRefs, When using URLRewrite filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified rule: '(has(self.backendRefs) && self.backendRefs.exists_one(b, (has(b.filters) && b.filters.exists_one(f, has(f.urlRewrite) && has(f.urlRewrite.path) && f.urlRewrite.path.type == ''ReplacePrefixMatch'' && has(f.urlRewrite.path.replacePrefixMatch))) )) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != ''PathPrefix'') ? false : true) : true' maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: While 16 rules and 64 matches per rule are allowed, the total number of matches across all rules in a route must be less than 128 rule: '(self.size() > 0 ? self[0].matches.size() : 0) + (self.size() > 1 ? self[1].matches.size() : 0) + (self.size() > 2 ? self[2].matches.size() : 0) + (self.size() > 3 ? self[3].matches.size() : 0) + (self.size() > 4 ? self[4].matches.size() : 0) + (self.size() > 5 ? self[5].matches.size() : 0) + (self.size() > 6 ? self[6].matches.size() : 0) + (self.size() > 7 ? self[7].matches.size() : 0) + (self.size() > 8 ? self[8].matches.size() : 0) + (self.size() > 9 ? self[9].matches.size() : 0) + (self.size() > 10 ? self[10].matches.size() : 0) + (self.size() > 11 ? self[11].matches.size() : 0) + (self.size() > 12 ? self[12].matches.size() : 0) + (self.size() > 13 ? self[13].matches.size() : 0) + (self.size() > 14 ? self[14].matches.size() : 0) + (self.size() > 15 ? self[15].matches.size() : 0) <= 128' - message: Rule name must be unique within the route rule: self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) && l1.name == l2.name)) useDefaultGateways: description: |- UseDefaultGateways indicates the default Gateway scope to use for this Route. If unset (the default) or set to None, the Route will not be attached to any default Gateway; if set, it will be attached to any default Gateway supporting the named scope, subject to the usual rules about which Routes a Gateway is allowed to claim. Think carefully before using this functionality! The set of default Gateways supporting the requested scope can change over time without any notice to the Route author, and in many situations it will not be appropriate to request a default Gateway for a given Route -- for example, a Route with specific security requirements should almost certainly not use a default Gateway. enum: - All - None type: string type: object status: description: Status defines the current state of HTTPRoute. properties: parents: description: |- Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway. items: description: |- RouteParentStatus describes the status of a route with respect to an associated Parent. properties: conditions: description: |- Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the "Accepted" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. A Route MUST be considered "Accepted" if at least one of the Route's rules is implemented by the Gateway. There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: * The Route refers to a nonexistent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace to which the controller does not have access. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string parentRef: description: |- ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object required: - conditions - controllerName - parentRef type: object maxItems: 32 type: array x-kubernetes-list-type: atomic required: - parents type: object required: - spec type: object served: true storage: false subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/experimental/gateway.networking.k8s.io_listenersets.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: experimental name: listenersets.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: ListenerSet listKind: ListenerSetList plural: listenersets shortNames: - lset singular: listenerset scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .status.conditions[?(@.type=="Accepted")].status name: Accepted type: string - jsonPath: .status.conditions[?(@.type=="Programmed")].status name: Programmed type: string - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1 schema: openAPIV3Schema: description: |- ListenerSet defines a set of additional listeners to attach to an existing Gateway. This resource provides a mechanism to merge multiple listeners into a single Gateway. The parent Gateway must explicitly allow ListenerSet attachment through its AllowedListeners configuration. By default, Gateways do not allow ListenerSet attachment. Routes can attach to a ListenerSet by specifying it as a parentRef, and can optionally target specific listeners using the sectionName field. Policy Attachment: - Policies that attach to a ListenerSet apply to all listeners defined in that resource - Policies do not impact listeners in the parent Gateway - Different ListenerSets attached to the same Gateway can have different policies - If an implementation cannot apply a policy to specific listeners, it should reject the policy ReferenceGrant Semantics: - ReferenceGrants applied to a Gateway are not inherited by child ListenerSets - ReferenceGrants applied to a ListenerSet do not grant permission to the parent Gateway's listeners - A ListenerSet can reference secrets/backends in its own namespace without a ReferenceGrant Gateway Integration: - The parent Gateway's status will include "AttachedListenerSets" which is the count of ListenerSets that have successfully attached to a Gateway A ListenerSet is successfully attached to a Gateway when all the following conditions are met: - The ListenerSet is selected by the Gateway's AllowedListeners field - The ListenerSet has a valid ParentRef selecting the Gateway - The ListenerSet's status has the condition "Accepted: true" properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of ListenerSet. properties: listeners: description: |- Listeners associated with this ListenerSet. Listeners define logical endpoints that are bound on this referenced parent Gateway's addresses. Listeners in a `Gateway` and their attached `ListenerSets` are concatenated as a list when programming the underlying infrastructure. Each listener name does not need to be unique across the Gateway and ListenerSets. See ListenerEntry.Name for more details. Implementations MUST treat the parent Gateway as having the merged list of all listeners from itself and attached ListenerSets using the following precedence: 1. "parent" Gateway 2. ListenerSet ordered by creation time (oldest first) 3. ListenerSet ordered alphabetically by "{namespace}/{name}". An implementation MAY reject listeners by setting the ListenerEntryStatus `Accepted` condition to False with the Reason `TooManyListeners` If a listener has a conflict, this will be reported in the Status.ListenerEntryStatus setting the `Conflicted` condition to True. Implementations SHOULD be cautious about what information from the parent or siblings are reported to avoid accidentally leaking sensitive information that the child would not otherwise have access to. This can include contents of secrets etc. items: properties: allowedRoutes: default: namespaces: from: Same description: |- AllowedRoutes defines the types of routes that MAY be attached to a Listener and the trusted namespaces where those Route resources MAY be present. Although a client request may match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: * The most specific match as defined by the Route type. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of "2020-09-08 01:02:03" is given precedence over a Route with a creation timestamp of "2020-09-08 01:02:04". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. All valid rules within a Route attached to this Listener should be implemented. Invalid Route rules can be ignored (sometimes that will mean the full Route). If a Route rule transitions from valid to invalid, support for that Route rule should be dropped to ensure consistency. For example, even if a filter specified by a Route rule is invalid, the rest of the rules within that Route should still be supported. properties: kinds: description: |- Kinds specifies the groups and kinds of Routes that are allowed to bind to this Gateway Listener. When unspecified or empty, the kinds of Routes selected are determined using the Listener protocol. A RouteGroupKind MUST correspond to kinds of Routes that are compatible with the application protocol specified in the Listener's Protocol field. If an implementation does not support or recognize this resource type, it MUST set the "ResolvedRefs" condition to False for this Listener with the "InvalidRouteKinds" reason. Support: Core items: description: RouteGroupKind indicates the group and kind of a Route resource. properties: group: default: gateway.networking.k8s.io description: Group is the group of the Route. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is the kind of the Route. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string required: - kind type: object maxItems: 8 type: array x-kubernetes-list-type: atomic namespaces: default: from: Same description: |- Namespaces indicates namespaces from which Routes may be attached to this Listener. This is restricted to the namespace of this Gateway by default. Support: Core properties: from: default: Same description: |- From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. Support: Core enum: - All - Selector - Same type: string selector: description: |- Selector must be specified when From is set to "Selector". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of "From". Support: Core properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic type: object type: object hostname: description: |- Hostname specifies the virtual hostname to match for protocol types that define this concept. When unspecified, all hostnames are matched. This field is ignored for protocols that don't require hostname based matching. Implementations MUST apply Hostname matching appropriately for each of the following protocols: * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP protocol layers as described above. If an implementation does not ensure that both the SNI and Host header match the Listener hostname, it MUST clearly document that. For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, there MUST be an intersection between the values for a Route to be accepted. For more information, refer to the Route specific Hostnames documentation. Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string name: description: |- Name is the name of the Listener. This name MUST be unique within a ListenerSet. Name is not required to be unique across a Gateway and ListenerSets. Routes can attach to a Listener by having a ListenerSet as a parentRef and setting the SectionName maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string port: description: |- Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. format: int32 maximum: 65535 minimum: 1 type: integer protocol: description: Protocol specifies the network protocol this listener expects to receive. maxLength: 255 minLength: 1 pattern: ^[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ type: string tls: description: |- TLS is the TLS configuration for the Listener. This field is required if the Protocol field is "HTTPS" or "TLS". It is invalid to set this field if the Protocol field is "HTTP", "TCP", or "UDP". The association of SNIs to Certificate defined in ListenerTLSConfig is defined based on the Hostname field for this listener. The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. properties: certificateRefs: description: |- CertificateRefs contains a series of references to Kubernetes objects that contains TLS certificates and private keys. These certificates are used to establish a TLS handshake for requests that match the hostname of the associated listener. A single CertificateRef to a Kubernetes Secret has "Core" support. Implementations MAY choose to support attaching multiple certificates to a Listener, but this behavior is implementation-specific. References to a resource in different namespace are invalid UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the "ResolvedRefs" condition MUST be set to False for this listener with the "RefNotPermitted" reason. This field is required to have at least one element when the mode is set to "Terminate" (default) and is optional otherwise. CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls Support: Implementation-specific (More than one reference or other resource types) items: description: |- SecretObjectReference identifies an API object including its namespace, defaulting to Secret. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Secret description: Kind is kind of the referent. For example "Secret". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - name type: object maxItems: 64 type: array x-kubernetes-list-type: atomic mode: default: Terminate description: |- Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificates to be specified in some way, such as populating the certificateRefs field. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. The certificateRefs field is ignored in this mode. Support: Core enum: - Terminate - Passthrough type: string options: additionalProperties: description: |- AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. maxLength: 4096 minLength: 0 type: string description: |- Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. Support: Implementation-specific maxProperties: 16 type: object type: object x-kubernetes-validations: - message: certificateRefs or options must be specified when mode is Terminate rule: 'self.mode == ''Terminate'' ? size(self.certificateRefs) > 0 || size(self.options) > 0 : true' required: - name - port - protocol type: object maxItems: 64 minItems: 1 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map x-kubernetes-validations: - message: tls must not be specified for protocols ['HTTP', 'TCP', 'UDP'] rule: 'self.all(l, l.protocol in [''HTTP'', ''TCP'', ''UDP''] ? !has(l.tls) : true)' - message: tls mode must be Terminate for protocol HTTPS rule: 'self.all(l, (l.protocol == ''HTTPS'' && has(l.tls)) ? (l.tls.mode == '''' || l.tls.mode == ''Terminate'') : true)' - message: tls mode must be set for protocol TLS rule: 'self.all(l, (l.protocol == ''TLS'' ? has(l.tls) && has(l.tls.mode) && l.tls.mode != '''' : true))' - message: hostname must not be specified for protocols ['TCP', 'UDP'] rule: 'self.all(l, l.protocol in [''TCP'', ''UDP''] ? (!has(l.hostname) || l.hostname == '''') : true)' - message: Listener name must be unique within the Gateway rule: self.all(l1, self.exists_one(l2, l1.name == l2.name)) - message: Combination of port, protocol and hostname must be unique for each listener rule: 'self.all(l1, !has(l1.port) || self.exists_one(l2, has(l2.port) && l1.port == l2.port && l1.protocol == l2.protocol && (has(l1.hostname) && has(l2.hostname) ? l1.hostname == l2.hostname : !has(l1.hostname) && !has(l2.hostname))))' parentRef: description: ParentRef references the Gateway that the listeners are attached to. properties: group: default: gateway.networking.k8s.io description: Group is the group of the referent. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: Kind is kind of the referent. For example "Gateway". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. If not present, the namespace of the referent is assumed to be the same as the namespace of the referring object. maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - name type: object required: - listeners - parentRef type: object status: default: conditions: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Programmed description: Status defines the current state of ListenerSet. properties: conditions: default: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Programmed description: |- Conditions describe the current conditions of the ListenerSet. Implementations MUST express ListenerSet conditions using the `ListenerSetConditionType` and `ListenerSetConditionReason` constants so that operators and tools can converge on a common vocabulary to describe ListenerSet state. Known condition types are: * "Accepted" * "Programmed" items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map listeners: description: Listeners provide status for each unique listener port defined in the Spec. items: description: ListenerStatus is the status associated with a Listener. properties: attachedRoutes: description: |- AttachedRoutes represents the total number of Routes that have been successfully attached to this Listener. Successful attachment of a Route to a Listener is based solely on the combination of the AllowedRoutes field on the corresponding Listener and the Route's ParentRefs field. A Route is successfully attached to a Listener when it is selected by the Listener's AllowedRoutes field AND the Route has a valid ParentRef selecting the whole Gateway resource or a specific Listener as a parent resource (more detail on attachment semantics can be found in the documentation on the various Route kinds ParentRefs fields). Listener status does not impact successful attachment, i.e. the AttachedRoutes field count MUST be set for Listeners, even if the Accepted condition of an individual Listener is set to "False". The AttachedRoutes number represents the number of Routes with the Accepted condition set to "True" that have been attached to this Listener. Routes with any other value for the Accepted condition MUST NOT be included in this count. Uses for this field include troubleshooting Route attachment and measuring blast radius/impact of changes to a Listener. format: int32 type: integer conditions: description: Conditions describe the current condition of this listener. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map name: description: Name is the name of the Listener that this status corresponds to. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string supportedKinds: description: |- SupportedKinds is the list indicating the Kinds supported by this listener. This MUST represent the kinds supported by an implementation for that Listener configuration. If kinds are specified in Spec that are not supported, they MUST NOT appear in this list and an implementation MUST set the "ResolvedRefs" condition to "False" with the "InvalidRouteKinds" reason. If both valid and invalid Route kinds are specified, the implementation MUST reference the valid Route kinds that have been specified. items: description: RouteGroupKind indicates the group and kind of a Route resource. properties: group: default: gateway.networking.k8s.io description: Group is the group of the Route. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is the kind of the Route. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string required: - kind type: object maxItems: 8 type: array x-kubernetes-list-type: atomic required: - attachedRoutes - conditions - name type: object maxItems: 64 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object required: - spec type: object served: true storage: true subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/experimental/gateway.networking.k8s.io_referencegrants.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: experimental name: referencegrants.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: ReferenceGrant listKind: ReferenceGrantList plural: referencegrants shortNames: - refgrant singular: referencegrant scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1 schema: openAPIV3Schema: description: |- ReferenceGrant identifies kinds of resources in other namespaces that are trusted to reference the specified kinds of resources in the same namespace as the policy. Each ReferenceGrant can be used to represent a unique trust relationship. Additional Reference Grants can be used to add to the set of trusted sources of inbound references for the namespace they are defined within. All cross-namespace references in Gateway API (with the exception of cross-namespace Gateway-route attachment) require a ReferenceGrant. ReferenceGrant is a form of runtime verification allowing users to assert which cross-namespace object references are permitted. Implementations that support ReferenceGrant MUST NOT permit cross-namespace references which have no grant, and MUST respond to the removal of a grant by revoking the access that the grant allowed. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of ReferenceGrant. properties: from: description: |- From describes the trusted namespaces and kinds that can reference the resources described in "To". Each entry in this list MUST be considered to be an additional place that references can be valid from, or to put this another way, entries MUST be combined using OR. Support: Core items: description: ReferenceGrantFrom describes trusted namespaces and kinds. properties: group: description: |- Group is the group of the referent. When empty, the Kubernetes core API group is inferred. Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: |- Kind is the kind of the referent. Although implementations may support additional resources, the following types are part of the "Core" support level for this field. When used to permit a SecretObjectReference: * Gateway When used to permit a BackendObjectReference: * GRPCRoute * HTTPRoute * TCPRoute * TLSRoute * UDPRoute maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string namespace: description: |- Namespace is the namespace of the referent. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - group - kind - namespace type: object maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic to: description: |- To describes the resources that may be referenced by the resources described in "From". Each entry in this list MUST be considered to be an additional place that references can be valid to, or to put this another way, entries MUST be combined using OR. Support: Core items: description: |- ReferenceGrantTo describes what Kinds are allowed as targets of the references. properties: group: description: |- Group is the group of the referent. When empty, the Kubernetes core API group is inferred. Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: |- Kind is the kind of the referent. Although implementations may support additional resources, the following types are part of the "Core" support level for this field: * Secret when used to permit a SecretObjectReference * Service when used to permit a BackendObjectReference maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. When unspecified, this policy refers to all resources of the specified Group and Kind in the local namespace. maxLength: 253 minLength: 1 type: string required: - group - kind type: object maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic required: - from - to type: object type: object served: true storage: false subresources: {} - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1beta1 schema: openAPIV3Schema: description: |- ReferenceGrant identifies kinds of resources in other namespaces that are trusted to reference the specified kinds of resources in the same namespace as the policy. Each ReferenceGrant can be used to represent a unique trust relationship. Additional Reference Grants can be used to add to the set of trusted sources of inbound references for the namespace they are defined within. All cross-namespace references in Gateway API (with the exception of cross-namespace Gateway-route attachment) require a ReferenceGrant. ReferenceGrant is a form of runtime verification allowing users to assert which cross-namespace object references are permitted. Implementations that support ReferenceGrant MUST NOT permit cross-namespace references which have no grant, and MUST respond to the removal of a grant by revoking the access that the grant allowed. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of ReferenceGrant. properties: from: description: |- From describes the trusted namespaces and kinds that can reference the resources described in "To". Each entry in this list MUST be considered to be an additional place that references can be valid from, or to put this another way, entries MUST be combined using OR. Support: Core items: description: ReferenceGrantFrom describes trusted namespaces and kinds. properties: group: description: |- Group is the group of the referent. When empty, the Kubernetes core API group is inferred. Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: |- Kind is the kind of the referent. Although implementations may support additional resources, the following types are part of the "Core" support level for this field. When used to permit a SecretObjectReference: * Gateway When used to permit a BackendObjectReference: * GRPCRoute * HTTPRoute * TCPRoute * TLSRoute * UDPRoute maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string namespace: description: |- Namespace is the namespace of the referent. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - group - kind - namespace type: object maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic to: description: |- To describes the resources that may be referenced by the resources described in "From". Each entry in this list MUST be considered to be an additional place that references can be valid to, or to put this another way, entries MUST be combined using OR. Support: Core items: description: |- ReferenceGrantTo describes what Kinds are allowed as targets of the references. properties: group: description: |- Group is the group of the referent. When empty, the Kubernetes core API group is inferred. Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: |- Kind is the kind of the referent. Although implementations may support additional resources, the following types are part of the "Core" support level for this field: * Secret when used to permit a SecretObjectReference * Service when used to permit a BackendObjectReference maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. When unspecified, this policy refers to all resources of the specified Group and Kind in the local namespace. maxLength: 253 minLength: 1 type: string required: - group - kind type: object maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic required: - from - to type: object type: object served: true storage: true subresources: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/experimental/gateway.networking.k8s.io_tcproutes.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: experimental name: tcproutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: TCPRoute listKind: TCPRouteList plural: tcproutes singular: tcproute scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1alpha2 schema: openAPIV3Schema: description: |- TCPRoute provides a way to route TCP requests. When combined with a Gateway listener, it can be used to forward connections on the port specified by the listener to a set of backends specified by the TCPRoute. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of TCPRoute. properties: parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. For Services, that means the Service must either be in the same namespace for a "producer" route, or the mesh implementation must support and allow "consumer" routes for the referenced Service. ReferenceGrant is not applicable for governing ParentRefs to Services - it is not possible to create a "producer" route for a Service in a different namespace from the Route. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. ParentRefs must be _distinct_. This means either that: * They select different objects. If this is the case, then parentRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, `namespace`, and `name` must be unique across all parentRef entries in the Route. * They do not select different objects, but for each optional field used, each ParentRef that selects the same object must set the same set of optional fields to different values. If one ParentRef sets a combination of optional fields, all must set the same combination. Some examples: * If one ParentRef sets `sectionName`, all ParentRefs referencing the same object must also set `sectionName`. * If one ParentRef sets `port`, all ParentRefs referencing the same object must also set `port`. * If one ParentRef sets `sectionName` and `port`, all ParentRefs referencing the same object must also set `sectionName` and `port`. It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable other kinds of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object maxItems: 32 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__)) ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''') && (!has(p1.port) || p1.port == 0) == (!has(p2.port) || p2.port == 0)): true))' - message: sectionName or port must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || ( has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)) && (((!has(p1.port) || p1.port == 0) && (!has(p2.port) || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port)))) rules: description: Rules are a list of TCP matchers and actions. items: description: TCPRouteRule is the configuration for a given rule. properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a nonexistent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Connection rejections must respect weight; if an invalid backend is requested to have 80% of connections, then 80% of connections must be rejected instead. Support: Core for Kubernetes Service Support: Extended for Kubernetes ServiceImport Support: Implementation-specific for any other resource Support for weight: Extended items: description: |- BackendRef defines how a Route should forward a request to a Kubernetes resource. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. Implementations supporting appProtocol SHOULD recognize the Kubernetes Standard Application Protocols defined in KEP-3726. If a Service appProtocol isn't specified, an implementation MAY infer the backend protocol through its own means. Implementations MAY infer the protocol from the Route type referring to the backend Service. If a Route is not able to send traffic to the backend using the specified protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. Note that when the BackendTLSPolicy object is enabled by the implementation, there are some extra rules about validity to consider here. See the fields where this struct is used for more information about the exact behavior. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer weight: default: 1 description: |- Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. Support for this field varies based on the context where used. format: int32 maximum: 1000000 minimum: 0 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic name: description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. Support: Extended maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - backendRefs type: object maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: Rule name must be unique within the route rule: self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) && l1.name == l2.name)) useDefaultGateways: description: |- UseDefaultGateways indicates the default Gateway scope to use for this Route. If unset (the default) or set to None, the Route will not be attached to any default Gateway; if set, it will be attached to any default Gateway supporting the named scope, subject to the usual rules about which Routes a Gateway is allowed to claim. Think carefully before using this functionality! The set of default Gateways supporting the requested scope can change over time without any notice to the Route author, and in many situations it will not be appropriate to request a default Gateway for a given Route -- for example, a Route with specific security requirements should almost certainly not use a default Gateway. enum: - All - None type: string required: - rules type: object status: description: Status defines the current state of TCPRoute. properties: parents: description: |- Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway. items: description: |- RouteParentStatus describes the status of a route with respect to an associated Parent. properties: conditions: description: |- Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the "Accepted" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. A Route MUST be considered "Accepted" if at least one of the Route's rules is implemented by the Gateway. There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: * The Route refers to a nonexistent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace to which the controller does not have access. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string parentRef: description: |- ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object required: - conditions - controllerName - parentRef type: object maxItems: 32 type: array x-kubernetes-list-type: atomic required: - parents type: object required: - spec type: object served: true storage: true subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/experimental/gateway.networking.k8s.io_tlsroutes.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: experimental name: tlsroutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: TLSRoute listKind: TLSRouteList plural: tlsroutes singular: tlsroute scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1 schema: openAPIV3Schema: description: |- The TLSRoute resource is similar to TCPRoute, but can be configured to match against TLS-specific metadata. This allows more flexibility in matching streams for a given TLS listener. If you need to forward traffic to a single target for a TLS listener, you could choose to use a TCPRoute with a TLS listener. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of TLSRoute. properties: hostnames: description: |- Hostnames defines a set of SNI hostnames that should match against the SNI attribute of TLS ClientHello message in TLS handshake. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed in SNI hostnames per RFC 6066. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. items: description: |- Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. Hostname can be "precise" which is a domain name without the terminating dot of a network host (e.g. "foo.example.com") or "wildcard", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed. maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: Hostnames cannot contain an IP rule: self.all(h, !isIP(h)) - message: Hostnames must be valid based on RFC-1123 rule: 'self.all(h, !h.contains(''*'') ? h.matches(''^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)$'') : true)' - message: Wildcards on hostnames must be the first label, and the rest of hostname must be valid based on RFC-1123 rule: 'self.all(h, h.contains(''*'') ? (h.startsWith(''*.'') && h.substring(2).matches(''^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)$'')) : true)' parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. For Services, that means the Service must either be in the same namespace for a "producer" route, or the mesh implementation must support and allow "consumer" routes for the referenced Service. ReferenceGrant is not applicable for governing ParentRefs to Services - it is not possible to create a "producer" route for a Service in a different namespace from the Route. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. ParentRefs must be _distinct_. This means either that: * They select different objects. If this is the case, then parentRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, `namespace`, and `name` must be unique across all parentRef entries in the Route. * They do not select different objects, but for each optional field used, each ParentRef that selects the same object must set the same set of optional fields to different values. If one ParentRef sets a combination of optional fields, all must set the same combination. Some examples: * If one ParentRef sets `sectionName`, all ParentRefs referencing the same object must also set `sectionName`. * If one ParentRef sets `port`, all ParentRefs referencing the same object must also set `port`. * If one ParentRef sets `sectionName` and `port`, all ParentRefs referencing the same object must also set `sectionName` and `port`. It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable other kinds of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object maxItems: 32 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__)) ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''') && (!has(p1.port) || p1.port == 0) == (!has(p2.port) || p2.port == 0)): true))' - message: sectionName or port must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || ( has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)) && (((!has(p1.port) || p1.port == 0) && (!has(p2.port) || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port)))) rules: description: Rules are a list of actions. items: description: TLSRouteRule is the configuration for a given rule. properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a nonexistent resource or a Service with no endpoints), the rule performs no forwarding; if no filters are specified that would result in a response being sent, the underlying implementation must actively reject request attempts to this backend, by rejecting the connection or returning a 500 status code. Request rejections must respect weight; if an invalid backend is requested to have 80% of requests, then 80% of requests must be rejected instead. Support: Core for Kubernetes Service Support: Extended for Kubernetes ServiceImport Support: Implementation-specific for any other resource Support for weight: Extended items: description: |- BackendRef defines how a Route should forward a request to a Kubernetes resource. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. Implementations supporting appProtocol SHOULD recognize the Kubernetes Standard Application Protocols defined in KEP-3726. If a Service appProtocol isn't specified, an implementation MAY infer the backend protocol through its own means. Implementations MAY infer the protocol from the Route type referring to the backend Service. If a Route is not able to send traffic to the backend using the specified protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. Note that when the BackendTLSPolicy object is enabled by the implementation, there are some extra rules about validity to consider here. See the fields where this struct is used for more information about the exact behavior. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer weight: default: 1 description: |- Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. Support for this field varies based on the context where used. format: int32 maximum: 1000000 minimum: 0 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic name: description: Name is the name of the route rule. This name MUST be unique within a Route if it is set. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - backendRefs type: object maxItems: 1 minItems: 1 type: array x-kubernetes-list-type: atomic useDefaultGateways: description: |- UseDefaultGateways indicates the default Gateway scope to use for this Route. If unset (the default) or set to None, the Route will not be attached to any default Gateway; if set, it will be attached to any default Gateway supporting the named scope, subject to the usual rules about which Routes a Gateway is allowed to claim. Think carefully before using this functionality! The set of default Gateways supporting the requested scope can change over time without any notice to the Route author, and in many situations it will not be appropriate to request a default Gateway for a given Route -- for example, a Route with specific security requirements should almost certainly not use a default Gateway. enum: - All - None type: string required: - hostnames - rules type: object status: description: Status defines the current state of TLSRoute. properties: parents: description: |- Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway. items: description: |- RouteParentStatus describes the status of a route with respect to an associated Parent. properties: conditions: description: |- Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the "Accepted" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. A Route MUST be considered "Accepted" if at least one of the Route's rules is implemented by the Gateway. There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: * The Route refers to a nonexistent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace to which the controller does not have access. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string parentRef: description: |- ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object required: - conditions - controllerName - parentRef type: object maxItems: 32 type: array x-kubernetes-list-type: atomic required: - parents type: object required: - spec type: object served: true storage: true subresources: status: {} - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date deprecated: true deprecationWarning: The v1alpha2 version of TLSRoute has been deprecated and will be removed in a future release of the API. Please upgrade to v1. name: v1alpha2 schema: openAPIV3Schema: description: |- The TLSRoute resource is similar to TCPRoute, but can be configured to match against TLS-specific metadata. This allows more flexibility in matching streams for a given TLS listener. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of TLSRoute. properties: hostnames: description: |- Hostnames defines a set of SNI names that should match against the SNI attribute of TLS ClientHello message in TLS handshake. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed in SNI names per RFC 6066. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. If a hostname is specified by both the Listener and TLSRoute, there must be at least one intersecting hostname for the TLSRoute to be attached to the Listener. For example: * A Listener with `test.example.com` as the hostname matches TLSRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches TLSRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `test.example.com` and `*.example.com` would both match. On the other hand, `example.com` and `test.example.net` would not match. If both the Listener and TLSRoute have specified hostnames, any TLSRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the TLSRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. If both the Listener and TLSRoute have specified hostnames, and none match with the criteria above, then the TLSRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. Support: Core items: description: |- Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. Hostname can be "precise" which is a domain name without the terminating dot of a network host (e.g. "foo.example.com") or "wildcard", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed. maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string maxItems: 16 type: array x-kubernetes-list-type: atomic parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. For Services, that means the Service must either be in the same namespace for a "producer" route, or the mesh implementation must support and allow "consumer" routes for the referenced Service. ReferenceGrant is not applicable for governing ParentRefs to Services - it is not possible to create a "producer" route for a Service in a different namespace from the Route. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. ParentRefs must be _distinct_. This means either that: * They select different objects. If this is the case, then parentRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, `namespace`, and `name` must be unique across all parentRef entries in the Route. * They do not select different objects, but for each optional field used, each ParentRef that selects the same object must set the same set of optional fields to different values. If one ParentRef sets a combination of optional fields, all must set the same combination. Some examples: * If one ParentRef sets `sectionName`, all ParentRefs referencing the same object must also set `sectionName`. * If one ParentRef sets `port`, all ParentRefs referencing the same object must also set `port`. * If one ParentRef sets `sectionName` and `port`, all ParentRefs referencing the same object must also set `sectionName` and `port`. It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable other kinds of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object maxItems: 32 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__)) ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''') && (!has(p1.port) || p1.port == 0) == (!has(p2.port) || p2.port == 0)): true))' - message: sectionName or port must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || ( has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)) && (((!has(p1.port) || p1.port == 0) && (!has(p2.port) || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port)))) rules: description: Rules are a list of TLS matchers and actions. items: description: TLSRouteRule is the configuration for a given rule. properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a nonexistent resource or a Service with no endpoints), the rule performs no forwarding; if no filters are specified that would result in a response being sent, the underlying implementation must actively reject request attempts to this backend, by rejecting the connection or returning a 500 status code. Request rejections must respect weight; if an invalid backend is requested to have 80% of requests, then 80% of requests must be rejected instead. Support: Core for Kubernetes Service Support: Extended for Kubernetes ServiceImport Support: Implementation-specific for any other resource Support for weight: Extended items: description: |- BackendRef defines how a Route should forward a request to a Kubernetes resource. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. Implementations supporting appProtocol SHOULD recognize the Kubernetes Standard Application Protocols defined in KEP-3726. If a Service appProtocol isn't specified, an implementation MAY infer the backend protocol through its own means. Implementations MAY infer the protocol from the Route type referring to the backend Service. If a Route is not able to send traffic to the backend using the specified protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. Note that when the BackendTLSPolicy object is enabled by the implementation, there are some extra rules about validity to consider here. See the fields where this struct is used for more information about the exact behavior. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer weight: default: 1 description: |- Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. Support for this field varies based on the context where used. format: int32 maximum: 1000000 minimum: 0 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic name: description: Name is the name of the route rule. This name MUST be unique within a Route if it is set. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - backendRefs type: object maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: Rule name must be unique within the route rule: self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) && l1.name == l2.name)) useDefaultGateways: description: |- UseDefaultGateways indicates the default Gateway scope to use for this Route. If unset (the default) or set to None, the Route will not be attached to any default Gateway; if set, it will be attached to any default Gateway supporting the named scope, subject to the usual rules about which Routes a Gateway is allowed to claim. Think carefully before using this functionality! The set of default Gateways supporting the requested scope can change over time without any notice to the Route author, and in many situations it will not be appropriate to request a default Gateway for a given Route -- for example, a Route with specific security requirements should almost certainly not use a default Gateway. enum: - All - None type: string required: - rules type: object status: description: Status defines the current state of TLSRoute. properties: parents: description: |- Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway. items: description: |- RouteParentStatus describes the status of a route with respect to an associated Parent. properties: conditions: description: |- Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the "Accepted" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. A Route MUST be considered "Accepted" if at least one of the Route's rules is implemented by the Gateway. There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: * The Route refers to a nonexistent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace to which the controller does not have access. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string parentRef: description: |- ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object required: - conditions - controllerName - parentRef type: object maxItems: 32 type: array x-kubernetes-list-type: atomic required: - parents type: object required: - spec type: object served: true storage: false subresources: status: {} - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date deprecated: true deprecationWarning: The v1alpha3 version of TLSRoute has been deprecated and will be removed in a future release of the API. Please upgrade to v1. name: v1alpha3 schema: openAPIV3Schema: description: |- The TLSRoute resource is similar to TCPRoute, but can be configured to match against TLS-specific metadata. This allows more flexibility in matching streams for a given TLS listener. If you need to forward traffic to a single target for a TLS listener, you could choose to use a TCPRoute with a TLS listener. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of TLSRoute. properties: hostnames: description: |- Hostnames defines a set of SNI hostnames that should match against the SNI attribute of TLS ClientHello message in TLS handshake. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed in SNI hostnames per RFC 6066. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. items: description: |- Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. Hostname can be "precise" which is a domain name without the terminating dot of a network host (e.g. "foo.example.com") or "wildcard", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed. maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: Hostnames cannot contain an IP rule: self.all(h, !isIP(h)) - message: Hostnames must be valid based on RFC-1123 rule: 'self.all(h, !h.contains(''*'') ? h.matches(''^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)$'') : true)' - message: Wildcards on hostnames must be the first label, and the rest of hostname must be valid based on RFC-1123 rule: 'self.all(h, h.contains(''*'') ? (h.startsWith(''*.'') && h.substring(2).matches(''^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)$'')) : true)' parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. For Services, that means the Service must either be in the same namespace for a "producer" route, or the mesh implementation must support and allow "consumer" routes for the referenced Service. ReferenceGrant is not applicable for governing ParentRefs to Services - it is not possible to create a "producer" route for a Service in a different namespace from the Route. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. ParentRefs must be _distinct_. This means either that: * They select different objects. If this is the case, then parentRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, `namespace`, and `name` must be unique across all parentRef entries in the Route. * They do not select different objects, but for each optional field used, each ParentRef that selects the same object must set the same set of optional fields to different values. If one ParentRef sets a combination of optional fields, all must set the same combination. Some examples: * If one ParentRef sets `sectionName`, all ParentRefs referencing the same object must also set `sectionName`. * If one ParentRef sets `port`, all ParentRefs referencing the same object must also set `port`. * If one ParentRef sets `sectionName` and `port`, all ParentRefs referencing the same object must also set `sectionName` and `port`. It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable other kinds of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object maxItems: 32 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__)) ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''') && (!has(p1.port) || p1.port == 0) == (!has(p2.port) || p2.port == 0)): true))' - message: sectionName or port must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || ( has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)) && (((!has(p1.port) || p1.port == 0) && (!has(p2.port) || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port)))) rules: description: Rules are a list of actions. items: description: TLSRouteRule is the configuration for a given rule. properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a nonexistent resource or a Service with no endpoints), the rule performs no forwarding; if no filters are specified that would result in a response being sent, the underlying implementation must actively reject request attempts to this backend, by rejecting the connection or returning a 500 status code. Request rejections must respect weight; if an invalid backend is requested to have 80% of requests, then 80% of requests must be rejected instead. Support: Core for Kubernetes Service Support: Extended for Kubernetes ServiceImport Support: Implementation-specific for any other resource Support for weight: Extended items: description: |- BackendRef defines how a Route should forward a request to a Kubernetes resource. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. Implementations supporting appProtocol SHOULD recognize the Kubernetes Standard Application Protocols defined in KEP-3726. If a Service appProtocol isn't specified, an implementation MAY infer the backend protocol through its own means. Implementations MAY infer the protocol from the Route type referring to the backend Service. If a Route is not able to send traffic to the backend using the specified protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. Note that when the BackendTLSPolicy object is enabled by the implementation, there are some extra rules about validity to consider here. See the fields where this struct is used for more information about the exact behavior. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer weight: default: 1 description: |- Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. Support for this field varies based on the context where used. format: int32 maximum: 1000000 minimum: 0 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic name: description: Name is the name of the route rule. This name MUST be unique within a Route if it is set. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - backendRefs type: object maxItems: 1 minItems: 1 type: array x-kubernetes-list-type: atomic useDefaultGateways: description: |- UseDefaultGateways indicates the default Gateway scope to use for this Route. If unset (the default) or set to None, the Route will not be attached to any default Gateway; if set, it will be attached to any default Gateway supporting the named scope, subject to the usual rules about which Routes a Gateway is allowed to claim. Think carefully before using this functionality! The set of default Gateways supporting the requested scope can change over time without any notice to the Route author, and in many situations it will not be appropriate to request a default Gateway for a given Route -- for example, a Route with specific security requirements should almost certainly not use a default Gateway. enum: - All - None type: string required: - hostnames - rules type: object status: description: Status defines the current state of TLSRoute. properties: parents: description: |- Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway. items: description: |- RouteParentStatus describes the status of a route with respect to an associated Parent. properties: conditions: description: |- Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the "Accepted" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. A Route MUST be considered "Accepted" if at least one of the Route's rules is implemented by the Gateway. There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: * The Route refers to a nonexistent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace to which the controller does not have access. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string parentRef: description: |- ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object required: - conditions - controllerName - parentRef type: object maxItems: 32 type: array x-kubernetes-list-type: atomic required: - parents type: object required: - spec type: object served: true storage: false subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/experimental/gateway.networking.k8s.io_udproutes.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: experimental name: udproutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: UDPRoute listKind: UDPRouteList plural: udproutes singular: udproute scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1alpha2 schema: openAPIV3Schema: description: |- UDPRoute provides a way to route UDP traffic. When combined with a Gateway listener, it can be used to forward traffic on the port specified by the listener to a set of backends specified by the UDPRoute. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of UDPRoute. properties: parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. For Services, that means the Service must either be in the same namespace for a "producer" route, or the mesh implementation must support and allow "consumer" routes for the referenced Service. ReferenceGrant is not applicable for governing ParentRefs to Services - it is not possible to create a "producer" route for a Service in a different namespace from the Route. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. ParentRefs must be _distinct_. This means either that: * They select different objects. If this is the case, then parentRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, `namespace`, and `name` must be unique across all parentRef entries in the Route. * They do not select different objects, but for each optional field used, each ParentRef that selects the same object must set the same set of optional fields to different values. If one ParentRef sets a combination of optional fields, all must set the same combination. Some examples: * If one ParentRef sets `sectionName`, all ParentRefs referencing the same object must also set `sectionName`. * If one ParentRef sets `port`, all ParentRefs referencing the same object must also set `port`. * If one ParentRef sets `sectionName` and `port`, all ParentRefs referencing the same object must also set `sectionName` and `port`. It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable other kinds of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object maxItems: 32 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName or port must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__)) ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''') && (!has(p1.port) || p1.port == 0) == (!has(p2.port) || p2.port == 0)): true))' - message: sectionName or port must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || ( has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)) && (((!has(p1.port) || p1.port == 0) && (!has(p2.port) || p2.port == 0)) || (has(p1.port) && has(p2.port) && p1.port == p2.port)))) rules: description: Rules are a list of UDP matchers and actions. items: description: UDPRouteRule is the configuration for a given rule. properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a nonexistent resource or a Service with no endpoints), the underlying implementation MUST actively reject connection attempts to this backend. Packet drops must respect weight; if an invalid backend is requested to have 80% of the packets, then 80% of packets must be dropped instead. Support: Core for Kubernetes Service Support: Extended for Kubernetes ServiceImport Support: Implementation-specific for any other resource Support for weight: Extended items: description: |- BackendRef defines how a Route should forward a request to a Kubernetes resource. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. When the BackendRef points to a Kubernetes Service, implementations SHOULD honor the appProtocol field if it is set for the target Service Port. Implementations supporting appProtocol SHOULD recognize the Kubernetes Standard Application Protocols defined in KEP-3726. If a Service appProtocol isn't specified, an implementation MAY infer the backend protocol through its own means. Implementations MAY infer the protocol from the Route type referring to the backend Service. If a Route is not able to send traffic to the backend using the specified protocol then the backend is considered invalid. Implementations MUST set the "ResolvedRefs" condition to "False" with the "UnsupportedProtocol" reason. Note that when the BackendTLSPolicy object is enabled by the implementation, there are some extra rules about validity to consider here. See the fields where this struct is used for more information about the exact behavior. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer weight: default: 1 description: |- Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. Support for this field varies based on the context where used. format: int32 maximum: 1000000 minimum: 0 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic name: description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. Support: Extended maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - backendRefs type: object maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: Rule name must be unique within the route rule: self.all(l1, !has(l1.name) || self.exists_one(l2, has(l2.name) && l1.name == l2.name)) useDefaultGateways: description: |- UseDefaultGateways indicates the default Gateway scope to use for this Route. If unset (the default) or set to None, the Route will not be attached to any default Gateway; if set, it will be attached to any default Gateway supporting the named scope, subject to the usual rules about which Routes a Gateway is allowed to claim. Think carefully before using this functionality! The set of default Gateways supporting the requested scope can change over time without any notice to the Route author, and in many situations it will not be appropriate to request a default Gateway for a given Route -- for example, a Route with specific security requirements should almost certainly not use a default Gateway. enum: - All - None type: string required: - rules type: object status: description: Status defines the current state of UDPRoute. properties: parents: description: |- Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway. items: description: |- RouteParentStatus describes the status of a route with respect to an associated Parent. properties: conditions: description: |- Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the "Accepted" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. A Route MUST be considered "Accepted" if at least one of the Route's rules is implemented by the Gateway. There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: * The Route refers to a nonexistent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace to which the controller does not have access. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string parentRef: description: |- ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object required: - conditions - controllerName - parentRef type: object maxItems: 32 type: array x-kubernetes-list-type: atomic required: - parents type: object required: - spec type: object served: true storage: true subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/experimental/gateway.networking.k8s.io_vap_safeupgrades.yaml ================================================ apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicy metadata: annotations: gateway.networking.k8s.io/bundle-version: v1.5.0-dev gateway.networking.k8s.io/channel: standard name: "safe-upgrades.gateway.networking.k8s.io" spec: failurePolicy: Fail matchConstraints: resourceRules: - apiGroups: ["apiextensions.k8s.io"] apiVersions: ["v1"] operations: ["CREATE", "UPDATE"] resources: ["*"] validations: - expression: "object.spec.group != 'gateway.networking.k8s.io' || ( has(object.metadata.annotations) && object.metadata.annotations.exists(k, k == 'gateway.networking.k8s.io/channel') && object.metadata.annotations['gateway.networking.k8s.io/channel'] == 'standard' )" message: "Installing experimental CRDs on top of standard channel CRDs is prohibited by default. Uninstall ValidatingAdmissionPolicy safe-upgrades.gateway.networking.k8s.io to install experimental CRDs on top of standard channel CRDs." reason: Invalid - expression: "object.spec.group != 'gateway.networking.k8s.io' || (has(object.metadata.annotations) && object.metadata.annotations.exists(k, k == 'gateway.networking.k8s.io/bundle-version') && !matches(object.metadata.annotations['gateway.networking.k8s.io/bundle-version'], 'v1.[0-3].\\\\d+') && !matches(object.metadata.annotations['gateway.networking.k8s.io/bundle-version'], 'v0'))" #TODO Kubernetes 1.37: Migrate to kubernetes semver library message: "Installing CRDs with version before v1.5.0 is prohibited by default. Uninstall ValidatingAdmissionPolicy safe-upgrades.gateway.networking.k8s.io to install older versions." reason: Invalid --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicyBinding metadata: annotations: gateway.networking.k8s.io/bundle-version: v1.5.0-dev gateway.networking.k8s.io/channel: standard name: safe-upgrades.gateway.networking.k8s.io spec: policyName: safe-upgrades.gateway.networking.k8s.io validationActions: [Deny] matchResources: resourceRules: - apiGroups: ["apiextensions.k8s.io"] apiVersions: ["v1"] resources: ["customresourcedefinitions"] operations: ["CREATE", "UPDATE"] ================================================ FILE: pkg/gateway/crds/experimental/gateway.networking.x-k8s.io_xbackendtrafficpolicies.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: experimental labels: gateway.networking.k8s.io/policy: Direct name: xbackendtrafficpolicies.gateway.networking.x-k8s.io spec: group: gateway.networking.x-k8s.io names: categories: - gateway-api kind: XBackendTrafficPolicy listKind: XBackendTrafficPolicyList plural: xbackendtrafficpolicies shortNames: - xbtrafficpolicy singular: xbackendtrafficpolicy scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1alpha1 schema: openAPIV3Schema: description: |- XBackendTrafficPolicy defines the configuration for how traffic to a target backend should be handled. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of BackendTrafficPolicy. properties: retryConstraint: description: |- RetryConstraint defines the configuration for when to allow or prevent further retries to a target backend, by dynamically calculating a 'retry budget'. This budget is calculated based on the percentage of incoming traffic composed of retries over a given time interval. Once the budget is exceeded, additional retries will be rejected. For example, if the retry budget interval is 10 seconds, there have been 1000 active requests in the past 10 seconds, and the allowed percentage of requests that can be retried is 20% (the default), then 200 of those requests may be composed of retries. Active requests will only be considered for the duration of the interval when calculating the retry budget. Retrying the same original request multiple times within the retry budget interval will lead to each retry being counted towards calculating the budget. Configuring a RetryConstraint in BackendTrafficPolicy is compatible with HTTPRoute Retry settings for each HTTPRouteRule that targets the same backend. While the HTTPRouteRule Retry stanza can specify whether a request will be retried, and the number of retry attempts each client may perform, RetryConstraint helps prevent cascading failures such as retry storms during periods of consistent failures. After the retry budget has been exceeded, additional retries to the backend MUST return a 503 response to the client. Additional configurations for defining a constraint on retries MAY be defined in the future. Support: Extended properties: budget: default: interval: 10s percent: 20 description: Budget holds the details of the retry budget configuration. properties: interval: default: 10s description: |- Interval defines the duration in which requests will be considered for calculating the budget for retries. Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string x-kubernetes-validations: - message: interval cannot be greater than one hour or less than one second rule: '!(duration(self) < duration(''1s'') || duration(self) > duration(''1h''))' percent: default: 20 description: |- Percent defines the maximum percentage of active requests that may be made up of retries. Support: Extended maximum: 100 minimum: 0 type: integer type: object minRetryRate: default: count: 10 interval: 1s description: |- MinRetryRate defines the minimum rate of retries that will be allowable over a specified duration of time. The effective overall minimum rate of retries targeting the backend service may be much higher, as there can be any number of clients which are applying this setting locally. This ensures that requests can still be retried during periods of low traffic, where the budget for retries may be calculated as a very low value. Support: Extended properties: count: description: |- Count specifies the number of requests per time interval. Support: Extended maximum: 1000000 minimum: 1 type: integer interval: description: |- Interval specifies the divisor of the rate of requests, the amount of time during which the given count of requests occur. Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string x-kubernetes-validations: - message: interval cannot be greater than one hour rule: '!(duration(self) == duration(''0s'') || duration(self) > duration(''1h''))' type: object type: object sessionPersistence: description: |- SessionPersistence defines and configures session persistence for the backend. Support: Extended properties: absoluteTimeout: description: |- AbsoluteTimeout defines the absolute timeout of the persistent session. Once the AbsoluteTimeout duration has elapsed, the session becomes invalid. Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string cookieConfig: description: |- CookieConfig provides configuration settings that are specific to cookie-based session persistence. Support: Core properties: lifetimeType: default: Session description: |- LifetimeType specifies whether the cookie has a permanent or session-based lifetime. A permanent cookie persists until its specified expiry time, defined by the Expires or Max-Age cookie attributes, while a session cookie is deleted when the current session ends. When set to "Permanent", AbsoluteTimeout indicates the cookie's lifetime via the Expires or Max-Age cookie attributes and is required. When set to "Session", AbsoluteTimeout indicates the absolute lifetime of the cookie tracked by the gateway and is optional. Defaults to "Session". Support: Core for "Session" type Support: Extended for "Permanent" type enum: - Permanent - Session type: string type: object idleTimeout: description: |- IdleTimeout defines the idle timeout of the persistent session. Once the session has been idle for more than the specified IdleTimeout duration, the session becomes invalid. Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string sessionName: description: |- SessionName defines the name of the persistent session token which may be reflected in the cookie or the header. Users should avoid reusing session names to prevent unintended consequences, such as rejection or unpredictable behavior. Support: Implementation-specific maxLength: 128 type: string type: default: Cookie description: |- Type defines the type of session persistence such as through the use of a header or cookie. Defaults to cookie based session persistence. Support: Core for "Cookie" type Support: Extended for "Header" type enum: - Cookie - Header type: string type: object x-kubernetes-validations: - message: AbsoluteTimeout must be specified when cookie lifetimeType is Permanent rule: '!has(self.cookieConfig) || !has(self.cookieConfig.lifetimeType) || self.cookieConfig.lifetimeType != ''Permanent'' || has(self.absoluteTimeout)' - message: cookieConfig can only be set with type Cookie rule: '!has(self.cookieConfig) || self.type == ''Cookie''' targetRefs: description: |- TargetRefs identifies API object(s) to apply this policy to. Currently, Backends (A grouping of like endpoints such as Service, ServiceImport, or any implementation-specific backendRef) are the only valid API target references. Currently, a TargetRef cannot be scoped to a specific port on a Service. items: description: |- LocalPolicyTargetReference identifies an API object to apply a direct or inherited policy to. This should be used as part of Policy resources that can target Gateway API resources. For more information on how this policy attachment model works, and a sample Policy resource, refer to the policy attachment documentation for Gateway API. properties: group: description: Group is the group of the target resource. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the target resource. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the target resource. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object maxItems: 16 minItems: 1 type: array x-kubernetes-list-map-keys: - group - kind - name x-kubernetes-list-type: map required: - targetRefs type: object status: description: Status defines the current state of BackendTrafficPolicy. properties: ancestors: description: |- Ancestors is a list of ancestor resources (usually Gateways) that are associated with the policy, and the status of the policy with respect to each ancestor. When this policy attaches to a parent, the controller that manages the parent and the ancestors MUST add an entry to this list when the controller first sees the policy and SHOULD update the entry as appropriate when the relevant ancestor is modified. Note that choosing the relevant ancestor is left to the Policy designers; an important part of Policy design is designing the right object level at which to namespace this status. Note also that implementations MUST ONLY populate ancestor status for the Ancestor resources they are responsible for. Implementations MUST use the ControllerName field to uniquely identify the entries in this list that they are responsible for. Note that to achieve this, the list of PolicyAncestorStatus structs MUST be treated as a map with a composite key, made up of the AncestorRef and ControllerName fields combined. A maximum of 16 ancestors will be represented in this list. An empty list means the Policy is not relevant for any ancestors. If this slice is full, implementations MUST NOT add further entries. Instead they MUST consider the policy unimplementable and signal that on any related resources such as the ancestor that would be referenced here. For example, if this list was full on BackendTLSPolicy, no additional Gateways would be able to reference the Service targeted by the BackendTLSPolicy. items: description: |- PolicyAncestorStatus describes the status of a route with respect to an associated Ancestor. Ancestors refer to objects that are either the Target of a policy or above it in terms of object hierarchy. For example, if a policy targets a Service, the Policy's Ancestors are, in order, the Service, the HTTPRoute, the Gateway, and the GatewayClass. Almost always, in this hierarchy, the Gateway will be the most useful object to place Policy status on, so we recommend that implementations SHOULD use Gateway as the PolicyAncestorStatus object unless the designers have a _very_ good reason otherwise. In the context of policy attachment, the Ancestor is used to distinguish which resource results in a distinct application of this policy. For example, if a policy targets a Service, it may have a distinct result per attached Gateway. Policies targeting the same resource may have different effects depending on the ancestors of those resources. For example, different Gateways targeting the same Service may have different capabilities, especially if they have different underlying implementations. For example, in BackendTLSPolicy, the Policy attaches to a Service that is used as a backend in a HTTPRoute that is itself attached to a Gateway. In this case, the relevant object for status is the Gateway, and that is the ancestor object referred to in this status. Note that a parent is also an ancestor, so for objects where the parent is the relevant object for status, this struct SHOULD still be used. This struct is intended to be used in a slice that's effectively a map, with a composite key made up of the AncestorRef and the ControllerName. properties: ancestorRef: description: |- AncestorRef corresponds with a ParentRef in the spec that this PolicyAncestorStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. ParentRefs from a Route to a Service in the same namespace are "producer" routes, which apply default routing rules to inbound connections from any namespace to the Service. ParentRefs from a Route to a Service in a different namespace are "consumer" routes, and these routing rules are only applied to outbound connections originating from the same namespace as the Route, for which the intended destination of the connections are a Service targeted as a ParentRef of the Route. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. When the parent resource is a Service, this targets a specific port in the Service spec. When both Port (experimental) and SectionName are specified, the name and port of the selected port must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object conditions: description: Conditions describes the status of the Policy with respect to the given Ancestor. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string required: - ancestorRef - conditions - controllerName type: object maxItems: 16 type: array x-kubernetes-list-type: atomic required: - ancestors type: object required: - spec type: object served: true storage: true subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/experimental/gateway.networking.x-k8s.io_xlistenersets.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.0 gateway.networking.k8s.io/channel: experimental name: xlistenersets.gateway.networking.x-k8s.io spec: group: gateway.networking.x-k8s.io names: categories: - gateway-api kind: XListenerSet listKind: XListenerSetList plural: xlistenersets shortNames: - lset singular: xlistenerset scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .status.conditions[?(@.type=="Accepted")].status name: Accepted type: string - jsonPath: .status.conditions[?(@.type=="Programmed")].status name: Programmed type: string - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1alpha1 schema: openAPIV3Schema: description: |- XListenerSet defines a set of additional listeners to attach to an existing Gateway. This resource provides a mechanism to merge multiple listeners into a single Gateway. The parent Gateway must explicitly allow ListenerSet attachment through its AllowedListeners configuration. By default, Gateways do not allow ListenerSet attachment. Routes can attach to a ListenerSet by specifying it as a parentRef, and can optionally target specific listeners using the sectionName field. Policy Attachment: - Policies that attach to a ListenerSet apply to all listeners defined in that resource - Policies do not impact listeners in the parent Gateway - Different ListenerSets attached to the same Gateway can have different policies - If an implementation cannot apply a policy to specific listeners, it should reject the policy ReferenceGrant Semantics: - ReferenceGrants applied to a Gateway are not inherited by child ListenerSets - ReferenceGrants applied to a ListenerSet do not grant permission to the parent Gateway's listeners - A ListenerSet can reference secrets/backends in its own namespace without a ReferenceGrant Gateway Integration: - The parent Gateway's status will include an "AttachedListenerSets" condition - This condition will be: - True: when AllowedListeners is set and at least one child ListenerSet is attached - False: when AllowedListeners is set but no valid listeners are attached, or when AllowedListeners is not set or false - Unknown: when no AllowedListeners config is present properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of ListenerSet. properties: listeners: description: |- Listeners associated with this ListenerSet. Listeners define logical endpoints that are bound on this referenced parent Gateway's addresses. Listeners in a `Gateway` and their attached `ListenerSets` are concatenated as a list when programming the underlying infrastructure. Each listener name does not need to be unique across the Gateway and ListenerSets. See ListenerEntry.Name for more details. Implementations MUST treat the parent Gateway as having the merged list of all listeners from itself and attached ListenerSets using the following precedence: 1. "parent" Gateway 2. ListenerSet ordered by creation time (oldest first) 3. ListenerSet ordered alphabetically by "{namespace}/{name}". An implementation MAY reject listeners by setting the ListenerEntryStatus `Accepted` condition to False with the Reason `TooManyListeners` If a listener has a conflict, this will be reported in the Status.ListenerEntryStatus setting the `Conflicted` condition to True. Implementations SHOULD be cautious about what information from the parent or siblings are reported to avoid accidentally leaking sensitive information that the child would not otherwise have access to. This can include contents of secrets etc. items: properties: allowedRoutes: default: namespaces: from: Same description: |- AllowedRoutes defines the types of routes that MAY be attached to a Listener and the trusted namespaces where those Route resources MAY be present. Although a client request may match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: * The most specific match as defined by the Route type. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of "2020-09-08 01:02:03" is given precedence over a Route with a creation timestamp of "2020-09-08 01:02:04". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. All valid rules within a Route attached to this Listener should be implemented. Invalid Route rules can be ignored (sometimes that will mean the full Route). If a Route rule transitions from valid to invalid, support for that Route rule should be dropped to ensure consistency. For example, even if a filter specified by a Route rule is invalid, the rest of the rules within that Route should still be supported. properties: kinds: description: |- Kinds specifies the groups and kinds of Routes that are allowed to bind to this Gateway Listener. When unspecified or empty, the kinds of Routes selected are determined using the Listener protocol. A RouteGroupKind MUST correspond to kinds of Routes that are compatible with the application protocol specified in the Listener's Protocol field. If an implementation does not support or recognize this resource type, it MUST set the "ResolvedRefs" condition to False for this Listener with the "InvalidRouteKinds" reason. Support: Core items: description: RouteGroupKind indicates the group and kind of a Route resource. properties: group: default: gateway.networking.k8s.io description: Group is the group of the Route. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is the kind of the Route. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string required: - kind type: object maxItems: 8 type: array x-kubernetes-list-type: atomic namespaces: default: from: Same description: |- Namespaces indicates namespaces from which Routes may be attached to this Listener. This is restricted to the namespace of this Gateway by default. Support: Core properties: from: default: Same description: |- From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. Support: Core enum: - All - Selector - Same type: string selector: description: |- Selector must be specified when From is set to "Selector". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of "From". Support: Core properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic type: object type: object hostname: description: |- Hostname specifies the virtual hostname to match for protocol types that define this concept. When unspecified, all hostnames are matched. This field is ignored for protocols that don't require hostname based matching. Implementations MUST apply Hostname matching appropriately for each of the following protocols: * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP protocol layers as described above. If an implementation does not ensure that both the SNI and Host header match the Listener hostname, it MUST clearly document that. For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, there MUST be an intersection between the values for a Route to be accepted. For more information, refer to the Route specific Hostnames documentation. Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string name: description: |- Name is the name of the Listener. This name MUST be unique within a ListenerSet. Name is not required to be unique across a Gateway and ListenerSets. Routes can attach to a Listener by having a ListenerSet as a parentRef and setting the SectionName maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string port: default: 0 description: |- Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. If the port is not set or specified as zero, the implementation will assign a unique port. If the implementation does not support dynamic port assignment, it MUST set `Accepted` condition to `False` with the `UnsupportedPort` reason. format: int32 maximum: 65535 minimum: 0 type: integer protocol: description: Protocol specifies the network protocol this listener expects to receive. maxLength: 255 minLength: 1 pattern: ^[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ type: string tls: description: |- TLS is the TLS configuration for the Listener. This field is required if the Protocol field is "HTTPS" or "TLS". It is invalid to set this field if the Protocol field is "HTTP", "TCP", or "UDP". The association of SNIs to Certificate defined in ListenerTLSConfig is defined based on the Hostname field for this listener. The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. properties: certificateRefs: description: |- CertificateRefs contains a series of references to Kubernetes objects that contains TLS certificates and private keys. These certificates are used to establish a TLS handshake for requests that match the hostname of the associated listener. A single CertificateRef to a Kubernetes Secret has "Core" support. Implementations MAY choose to support attaching multiple certificates to a Listener, but this behavior is implementation-specific. References to a resource in different namespace are invalid UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the "ResolvedRefs" condition MUST be set to False for this listener with the "RefNotPermitted" reason. This field is required to have at least one element when the mode is set to "Terminate" (default) and is optional otherwise. CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls Support: Implementation-specific (More than one reference or other resource types) items: description: |- SecretObjectReference identifies an API object including its namespace, defaulting to Secret. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Secret description: Kind is kind of the referent. For example "Secret". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - name type: object maxItems: 64 type: array x-kubernetes-list-type: atomic mode: default: Terminate description: |- Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificates to be specified in some way, such as populating the certificateRefs field. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. The certificateRefs field is ignored in this mode. Support: Core enum: - Terminate - Passthrough type: string options: additionalProperties: description: |- AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. maxLength: 4096 minLength: 0 type: string description: |- Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. Support: Implementation-specific maxProperties: 16 type: object type: object x-kubernetes-validations: - message: certificateRefs or options must be specified when mode is Terminate rule: 'self.mode == ''Terminate'' ? size(self.certificateRefs) > 0 || size(self.options) > 0 : true' required: - name - protocol type: object maxItems: 64 minItems: 1 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map x-kubernetes-validations: - message: tls must not be specified for protocols ['HTTP', 'TCP', 'UDP'] rule: 'self.all(l, l.protocol in [''HTTP'', ''TCP'', ''UDP''] ? !has(l.tls) : true)' - message: tls mode must be Terminate for protocol HTTPS rule: 'self.all(l, (l.protocol == ''HTTPS'' && has(l.tls)) ? (l.tls.mode == '''' || l.tls.mode == ''Terminate'') : true)' - message: hostname must not be specified for protocols ['TCP', 'UDP'] rule: 'self.all(l, l.protocol in [''TCP'', ''UDP''] ? (!has(l.hostname) || l.hostname == '''') : true)' - message: Listener name must be unique within the Gateway rule: self.all(l1, self.exists_one(l2, l1.name == l2.name)) - message: Combination of port, protocol and hostname must be unique for each listener rule: 'self.all(l1, !has(l1.port) || self.exists_one(l2, has(l2.port) && l1.port == l2.port && l1.protocol == l2.protocol && (has(l1.hostname) && has(l2.hostname) ? l1.hostname == l2.hostname : !has(l1.hostname) && !has(l2.hostname))))' parentRef: description: ParentRef references the Gateway that the listeners are attached to. properties: group: default: gateway.networking.k8s.io description: Group is the group of the referent. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: Kind is kind of the referent. For example "Gateway". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. If not present, the namespace of the referent is assumed to be the same as the namespace of the referring object. maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - name type: object required: - listeners - parentRef type: object status: default: conditions: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Programmed description: Status defines the current state of ListenerSet. properties: conditions: default: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Programmed description: |- Conditions describe the current conditions of the ListenerSet. Implementations MUST express ListenerSet conditions using the `ListenerSetConditionType` and `ListenerSetConditionReason` constants so that operators and tools can converge on a common vocabulary to describe ListenerSet state. Known condition types are: * "Accepted" * "Programmed" items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map listeners: description: Listeners provide status for each unique listener port defined in the Spec. items: description: ListenerStatus is the status associated with a Listener. properties: attachedRoutes: description: |- AttachedRoutes represents the total number of Routes that have been successfully attached to this Listener. Successful attachment of a Route to a Listener is based solely on the combination of the AllowedRoutes field on the corresponding Listener and the Route's ParentRefs field. A Route is successfully attached to a Listener when it is selected by the Listener's AllowedRoutes field AND the Route has a valid ParentRef selecting the whole Gateway resource or a specific Listener as a parent resource (more detail on attachment semantics can be found in the documentation on the various Route kinds ParentRefs fields). Listener or Route status does not impact successful attachment, i.e. the AttachedRoutes field count MUST be set for Listeners with condition Accepted: false and MUST count successfully attached Routes that may themselves have Accepted: false conditions. Uses for this field include troubleshooting Route attachment and measuring blast radius/impact of changes to a Listener. format: int32 type: integer conditions: description: Conditions describe the current condition of this listener. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map name: description: Name is the name of the Listener that this status corresponds to. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string port: description: Port is the network port the listener is configured to listen on. format: int32 maximum: 65535 minimum: 1 type: integer supportedKinds: description: |- SupportedKinds is the list indicating the Kinds supported by this listener. This MUST represent the kinds an implementation supports for that Listener configuration. If kinds are specified in Spec that are not supported, they MUST NOT appear in this list and an implementation MUST set the "ResolvedRefs" condition to "False" with the "InvalidRouteKinds" reason. If both valid and invalid Route kinds are specified, the implementation MUST reference the valid Route kinds that have been specified. items: description: RouteGroupKind indicates the group and kind of a Route resource. properties: group: default: gateway.networking.k8s.io description: Group is the group of the Route. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is the kind of the Route. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string required: - kind type: object maxItems: 8 type: array x-kubernetes-list-type: atomic required: - attachedRoutes - conditions - name - port - supportedKinds type: object maxItems: 64 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object required: - spec type: object served: true storage: true subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/experimental/gateway.networking.x-k8s.io_xmeshes.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: experimental name: xmeshes.gateway.networking.x-k8s.io spec: group: gateway.networking.x-k8s.io names: categories: - gateway-api kind: XMesh listKind: XMeshList plural: xmeshes shortNames: - mesh singular: xmesh scope: Cluster versions: - additionalPrinterColumns: - jsonPath: .status.conditions[?(@.type=="Accepted")].status name: Accepted type: string - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1alpha1 schema: openAPIV3Schema: description: XMesh defines mesh-wide characteristics of a GAMMA-compliant service mesh. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of XMesh. properties: controllerName: description: |- ControllerName is the name of a controller that is managing Gateway API resources for mesh traffic management. The value of this field MUST be a domain prefixed path. Example: "example.com/awesome-mesh". This field is not mutable and cannot be empty. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string x-kubernetes-validations: - message: Value is immutable rule: self == oldSelf description: description: Description optionally provides a human-readable description of a Mesh. maxLength: 64 type: string parametersRef: description: |- ParametersRef is an optional reference to a resource that contains implementation-specific configuration for this Mesh. If no implementation-specific parameters are needed, this field MUST be omitted. ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, or an implementation-specific custom resource. The resource can be cluster-scoped or namespace-scoped. If the referent cannot be found, refers to an unsupported kind, or when the data within that resource is malformed, the Mesh MUST be rejected with the "Accepted" status condition set to "False" and an "InvalidParameters" reason. Support: Implementation-specific properties: group: description: Group is the group of the referent. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. This field is required when referring to a Namespace-scoped resource and MUST be unset when referring to a Cluster-scoped resource. maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - group - kind - name type: object required: - controllerName type: object status: default: conditions: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted description: Status defines the current state of XMesh. properties: conditions: default: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Programmed description: |- Conditions is the current status from the controller for this Mesh. Controllers should prefer to publish conditions using values of MeshConditionType for the type of each Condition. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map supportedFeatures: description: |- SupportedFeatures is the set of features the Mesh support. It MUST be sorted in ascending alphabetical order by the Name key. items: properties: name: description: |- FeatureName is used to describe distinct features that are covered by conformance tests. type: string required: - name type: object maxItems: 64 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object required: - spec type: object served: true storage: true subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/experimental/kustomization.yaml ================================================ resources: - gateway.networking.k8s.io_backendtlspolicies.yaml - gateway.networking.k8s.io_gatewayclasses.yaml - gateway.networking.k8s.io_gateways.yaml - gateway.networking.k8s.io_grpcroutes.yaml - gateway.networking.k8s.io_httproutes.yaml - gateway.networking.k8s.io_listenersets.yaml - gateway.networking.k8s.io_referencegrants.yaml - gateway.networking.k8s.io_tcproutes.yaml - gateway.networking.k8s.io_tlsroutes.yaml - gateway.networking.k8s.io_udproutes.yaml - gateway.networking.k8s.io_vap_safeupgrades.yaml - gateway.networking.x-k8s.io_xbackendtrafficpolicies.yaml - gateway.networking.x-k8s.io_xmeshes.yaml ================================================ FILE: pkg/gateway/crds/kustomization.yaml ================================================ resources: - standard/gateway.networking.k8s.io_gatewayclasses.yaml - standard/gateway.networking.k8s.io_gateways.yaml - standard/gateway.networking.k8s.io_grpcroutes.yaml - standard/gateway.networking.k8s.io_httproutes.yaml - standard/gateway.networking.k8s.io_referencegrants.yaml - standard/gateway.networking.k8s.io_backendtlspolicies.yaml - standard/gateway.networking.k8s.io_listenersets.yaml - standard/gateway.networking.k8s.io_tlsroutes.yaml - standard/gateway.networking.k8s.io_vap_safeupgrades.yaml ================================================ FILE: pkg/gateway/crds/standard/gateway.networking.k8s.io_backendtlspolicies.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: standard labels: gateway.networking.k8s.io/policy: Direct name: backendtlspolicies.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: BackendTLSPolicy listKind: BackendTLSPolicyList plural: backendtlspolicies shortNames: - btlspolicy singular: backendtlspolicy scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1 schema: openAPIV3Schema: description: |- BackendTLSPolicy provides a way to configure how a Gateway connects to a Backend via TLS. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of BackendTLSPolicy. properties: options: additionalProperties: description: |- AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. maxLength: 4096 minLength: 0 type: string description: |- Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. Support: Implementation-specific maxProperties: 16 type: object targetRefs: description: |- TargetRefs identifies an API object to apply the policy to. Note that this config applies to the entire referenced resource by default, but this default may change in the future to provide a more granular application of the policy. TargetRefs must be _distinct_. This means either that: * They select different targets. If this is the case, then targetRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, and `name` must be unique across all targetRef entries in the BackendTLSPolicy. * They select different sectionNames in the same target. When more than one BackendTLSPolicy selects the same target and sectionName, implementations MUST determine precedence using the following criteria, continuing on ties: * The older policy by creation timestamp takes precedence. For example, a policy with a creation timestamp of "2021-07-15 01:02:03" MUST be given precedence over a policy with a creation timestamp of "2021-07-15 01:02:04". * The policy appearing first in alphabetical order by {namespace}/{name}. For example, a policy named `foo/bar` is given precedence over a policy named `foo/baz`. For any BackendTLSPolicy that does not take precedence, the implementation MUST ensure the `Accepted` Condition is set to `status: False`, with Reason `Conflicted`. Implementations SHOULD NOT support more than one targetRef at this time. Although the API technically allows for this, the current guidance for conflict resolution and status handling is lacking. Until that can be clarified in a future release, the safest approach is to support a single targetRef. Support Levels: * Extended: Kubernetes Service referenced by HTTPRoute backendRefs. * Implementation-Specific: Services not connected via HTTPRoute, and any other kind of backend. Implementations MAY use BackendTLSPolicy for: - Services not referenced by any Route (e.g., infrastructure services) - Gateway feature backends (e.g., ExternalAuth, rate-limiting services) - Service mesh workload-to-service communication - Other resource types beyond Service Implementations SHOULD aim to ensure that BackendTLSPolicy behavior is consistent, even outside of the extended HTTPRoute -(backendRef) -> Service path. They SHOULD clearly document how BackendTLSPolicy is interpreted in these scenarios, including: - Which resources beyond Service are supported - How the policy is discovered and applied - Any implementation-specific semantics or restrictions Note that this config applies to the entire referenced resource by default, but this default may change in the future to provide a more granular application of the policy. items: description: |- LocalPolicyTargetReferenceWithSectionName identifies an API object to apply a direct policy to. This should be used as part of Policy resources that can target single resources. For more information on how this policy attachment mode works, and a sample Policy resource, refer to the policy attachment documentation for Gateway API. Note: This should only be used for direct policy attachment when references to SectionName are actually needed. In all other cases, LocalPolicyTargetReference should be used. properties: group: description: Group is the group of the target resource. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the target resource. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the target resource. maxLength: 253 minLength: 1 type: string sectionName: description: |- SectionName is the name of a section within the target resource. When unspecified, this targetRef targets the entire resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name * HTTPRoute: HTTPRouteRule name * Service: Port name If a SectionName is specified, but does not exist on the targeted object, the Policy must fail to attach, and the policy implementation should record a `ResolvedRefs` or similar Condition in the Policy's status. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - group - kind - name type: object maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName must be specified when targetRefs includes 2 or more references to the same target rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''')) : true))' - message: sectionName must be unique when targetRefs includes 2 or more references to the same target rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)))) validation: description: Validation contains backend TLS validation configuration. properties: caCertificateRefs: description: |- CACertificateRefs contains one or more references to Kubernetes objects that contain a PEM-encoded TLS CA certificate bundle, which is used to validate a TLS handshake between the Gateway and backend Pod. If CACertificateRefs is empty or unspecified, then WellKnownCACertificates must be specified. Only one of CACertificateRefs or WellKnownCACertificates may be specified, not both. If CACertificateRefs is empty or unspecified, the configuration for WellKnownCACertificates MUST be honored instead if supported by the implementation. A CACertificateRef is invalid if: * It refers to a resource that cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured (e.g., a ConfigMap does not contain a key named `ca.crt`). In this case, the Reason must be set to `InvalidCACertificateRef` and the Message of the Condition must indicate which reference is invalid and why. * It refers to an unknown or unsupported kind of resource. In this case, the Reason must be set to `InvalidKind` and the Message of the Condition must explain which kind of resource is unknown or unsupported. * It refers to a resource in another namespace. This may change in future spec updates. Implementations MAY choose to perform further validation of the certificate content (e.g., checking expiry or enforcing specific formats). In such cases, an implementation-specific Reason and Message must be set for the invalid reference. In all cases, the implementation MUST ensure the `ResolvedRefs` Condition on the BackendTLSPolicy is set to `status: False`, with a Reason and Message that indicate the cause of the error. Connections using an invalid CACertificateRef MUST fail, and the client MUST receive an HTTP 5xx error response. If ALL CACertificateRefs are invalid, the implementation MUST also ensure the `Accepted` Condition on the BackendTLSPolicy is set to `status: False`, with a Reason `NoValidCACertificate`. A single CACertificateRef to a Kubernetes ConfigMap kind has "Core" support. Implementations MAY choose to support attaching multiple certificates to a backend, but this behavior is implementation-specific. Support: Core - An optional single reference to a Kubernetes ConfigMap, with the CA certificate in a key named `ca.crt`. Support: Implementation-specific - More than one reference, other kinds of resources, or a single reference that includes multiple certificates. items: description: |- LocalObjectReference identifies an API object within the namespace of the referrer. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "HTTPRoute" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object maxItems: 8 type: array x-kubernetes-list-type: atomic hostname: description: |- Hostname is used for two purposes in the connection between Gateways and backends: 1. Hostname MUST be used as the SNI to connect to the backend (RFC 6066). 2. Hostname MUST be used for authentication and MUST match the certificate served by the matching backend, unless SubjectAltNames is specified. 3. If SubjectAltNames are specified, Hostname can be used for certificate selection but MUST NOT be used for authentication. If you want to use the value of the Hostname field for authentication, you MUST add it to the SubjectAltNames list. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string subjectAltNames: description: |- SubjectAltNames contains one or more Subject Alternative Names. When specified the certificate served from the backend MUST have at least one Subject Alternate Name matching one of the specified SubjectAltNames. Support: Extended items: description: SubjectAltName represents Subject Alternative Name. properties: hostname: description: |- Hostname contains Subject Alternative Name specified in DNS name format. Required when Type is set to Hostname, ignored otherwise. Support: Core maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string type: description: |- Type determines the format of the Subject Alternative Name. Always required. Support: Core enum: - Hostname - URI type: string uri: description: |- URI contains Subject Alternative Name specified in a full URI format. It MUST include both a scheme (e.g., "http" or "ftp") and a scheme-specific-part. Common values include SPIFFE IDs like "spiffe://mycluster.example.com/ns/myns/sa/svc1sa". Required when Type is set to URI, ignored otherwise. Support: Core maxLength: 253 minLength: 1 pattern: ^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))? type: string required: - type type: object x-kubernetes-validations: - message: SubjectAltName element must contain Hostname, if Type is set to Hostname rule: '!(self.type == "Hostname" && (!has(self.hostname) || self.hostname == ""))' - message: SubjectAltName element must not contain Hostname, if Type is not set to Hostname rule: '!(self.type != "Hostname" && has(self.hostname) && self.hostname != "")' - message: SubjectAltName element must contain URI, if Type is set to URI rule: '!(self.type == "URI" && (!has(self.uri) || self.uri == ""))' - message: SubjectAltName element must not contain URI, if Type is not set to URI rule: '!(self.type != "URI" && has(self.uri) && self.uri != "")' maxItems: 5 type: array x-kubernetes-list-type: atomic wellKnownCACertificates: description: |- WellKnownCACertificates specifies whether a well-known set of CA certificates may be used in the TLS handshake between the gateway and backend pod. If WellKnownCACertificates is unspecified or empty (""), then CACertificateRefs must be specified with at least one entry for a valid configuration. Only one of CACertificateRefs or WellKnownCACertificates may be specified, not both. If an implementation does not support the WellKnownCACertificates field, or the supplied value is not recognized, the implementation MUST ensure the `Accepted` Condition on the BackendTLSPolicy is set to `status: False`, with a Reason `Invalid`. Valid values include: * "System" - indicates that well-known system CA certificates should be used. Implementations MAY define their own sets of CA certificates. Such definitions MUST use an implementation-specific, prefixed name, such as `mycompany.com/my-custom-ca-certifcates`. Support: Implementation-specific maxLength: 253 minLength: 1 pattern: ^(System|([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9]))$ type: string required: - hostname type: object x-kubernetes-validations: - message: must not contain both CACertificateRefs and WellKnownCACertificates rule: '!(has(self.caCertificateRefs) && size(self.caCertificateRefs) > 0 && has(self.wellKnownCACertificates) && self.wellKnownCACertificates != "")' - message: must specify either CACertificateRefs or WellKnownCACertificates rule: (has(self.caCertificateRefs) && size(self.caCertificateRefs) > 0 || has(self.wellKnownCACertificates) && self.wellKnownCACertificates != "") required: - targetRefs - validation type: object status: description: Status defines the current state of BackendTLSPolicy. properties: ancestors: description: |- Ancestors is a list of ancestor resources (usually Gateways) that are associated with the policy, and the status of the policy with respect to each ancestor. When this policy attaches to a parent, the controller that manages the parent and the ancestors MUST add an entry to this list when the controller first sees the policy and SHOULD update the entry as appropriate when the relevant ancestor is modified. Note that choosing the relevant ancestor is left to the Policy designers; an important part of Policy design is designing the right object level at which to namespace this status. Note also that implementations MUST ONLY populate ancestor status for the Ancestor resources they are responsible for. Implementations MUST use the ControllerName field to uniquely identify the entries in this list that they are responsible for. Note that to achieve this, the list of PolicyAncestorStatus structs MUST be treated as a map with a composite key, made up of the AncestorRef and ControllerName fields combined. A maximum of 16 ancestors will be represented in this list. An empty list means the Policy is not relevant for any ancestors. If this slice is full, implementations MUST NOT add further entries. Instead they MUST consider the policy unimplementable and signal that on any related resources such as the ancestor that would be referenced here. For example, if this list was full on BackendTLSPolicy, no additional Gateways would be able to reference the Service targeted by the BackendTLSPolicy. items: description: |- PolicyAncestorStatus describes the status of a route with respect to an associated Ancestor. Ancestors refer to objects that are either the Target of a policy or above it in terms of object hierarchy. For example, if a policy targets a Service, the Policy's Ancestors are, in order, the Service, the HTTPRoute, the Gateway, and the GatewayClass. Almost always, in this hierarchy, the Gateway will be the most useful object to place Policy status on, so we recommend that implementations SHOULD use Gateway as the PolicyAncestorStatus object unless the designers have a _very_ good reason otherwise. In the context of policy attachment, the Ancestor is used to distinguish which resource results in a distinct application of this policy. For example, if a policy targets a Service, it may have a distinct result per attached Gateway. Policies targeting the same resource may have different effects depending on the ancestors of those resources. For example, different Gateways targeting the same Service may have different capabilities, especially if they have different underlying implementations. For example, in BackendTLSPolicy, the Policy attaches to a Service that is used as a backend in a HTTPRoute that is itself attached to a Gateway. In this case, the relevant object for status is the Gateway, and that is the ancestor object referred to in this status. Note that a parent is also an ancestor, so for objects where the parent is the relevant object for status, this struct SHOULD still be used. This struct is intended to be used in a slice that's effectively a map, with a composite key made up of the AncestorRef and the ControllerName. properties: ancestorRef: description: |- AncestorRef corresponds with a ParentRef in the spec that this PolicyAncestorStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object conditions: description: Conditions describes the status of the Policy with respect to the given Ancestor. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string required: - ancestorRef - conditions - controllerName type: object maxItems: 16 type: array x-kubernetes-list-type: atomic required: - ancestors type: object required: - spec type: object served: true storage: true subresources: status: {} - deprecated: true deprecationWarning: The v1alpha3 version of BackendTLSPolicy has been deprecated and will be removed in a future release of the API. Please upgrade to v1. name: v1alpha3 schema: openAPIV3Schema: description: |- BackendTLSPolicy provides a way to configure how a Gateway connects to a Backend via TLS. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of BackendTLSPolicy. properties: options: additionalProperties: description: |- AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. maxLength: 4096 minLength: 0 type: string description: |- Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. Support: Implementation-specific maxProperties: 16 type: object targetRefs: description: |- TargetRefs identifies an API object to apply the policy to. Note that this config applies to the entire referenced resource by default, but this default may change in the future to provide a more granular application of the policy. TargetRefs must be _distinct_. This means either that: * They select different targets. If this is the case, then targetRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, and `name` must be unique across all targetRef entries in the BackendTLSPolicy. * They select different sectionNames in the same target. When more than one BackendTLSPolicy selects the same target and sectionName, implementations MUST determine precedence using the following criteria, continuing on ties: * The older policy by creation timestamp takes precedence. For example, a policy with a creation timestamp of "2021-07-15 01:02:03" MUST be given precedence over a policy with a creation timestamp of "2021-07-15 01:02:04". * The policy appearing first in alphabetical order by {namespace}/{name}. For example, a policy named `foo/bar` is given precedence over a policy named `foo/baz`. For any BackendTLSPolicy that does not take precedence, the implementation MUST ensure the `Accepted` Condition is set to `status: False`, with Reason `Conflicted`. Implementations SHOULD NOT support more than one targetRef at this time. Although the API technically allows for this, the current guidance for conflict resolution and status handling is lacking. Until that can be clarified in a future release, the safest approach is to support a single targetRef. Support Levels: * Extended: Kubernetes Service referenced by HTTPRoute backendRefs. * Implementation-Specific: Services not connected via HTTPRoute, and any other kind of backend. Implementations MAY use BackendTLSPolicy for: - Services not referenced by any Route (e.g., infrastructure services) - Gateway feature backends (e.g., ExternalAuth, rate-limiting services) - Service mesh workload-to-service communication - Other resource types beyond Service Implementations SHOULD aim to ensure that BackendTLSPolicy behavior is consistent, even outside of the extended HTTPRoute -(backendRef) -> Service path. They SHOULD clearly document how BackendTLSPolicy is interpreted in these scenarios, including: - Which resources beyond Service are supported - How the policy is discovered and applied - Any implementation-specific semantics or restrictions Note that this config applies to the entire referenced resource by default, but this default may change in the future to provide a more granular application of the policy. items: description: |- LocalPolicyTargetReferenceWithSectionName identifies an API object to apply a direct policy to. This should be used as part of Policy resources that can target single resources. For more information on how this policy attachment mode works, and a sample Policy resource, refer to the policy attachment documentation for Gateway API. Note: This should only be used for direct policy attachment when references to SectionName are actually needed. In all other cases, LocalPolicyTargetReference should be used. properties: group: description: Group is the group of the target resource. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the target resource. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the target resource. maxLength: 253 minLength: 1 type: string sectionName: description: |- SectionName is the name of a section within the target resource. When unspecified, this targetRef targets the entire resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name * HTTPRoute: HTTPRouteRule name * Service: Port name If a SectionName is specified, but does not exist on the targeted object, the Policy must fail to attach, and the policy implementation should record a `ResolvedRefs` or similar Condition in the Policy's status. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - group - kind - name type: object maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName must be specified when targetRefs includes 2 or more references to the same target rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''')) : true))' - message: sectionName must be unique when targetRefs includes 2 or more references to the same target rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)))) validation: description: Validation contains backend TLS validation configuration. properties: caCertificateRefs: description: |- CACertificateRefs contains one or more references to Kubernetes objects that contain a PEM-encoded TLS CA certificate bundle, which is used to validate a TLS handshake between the Gateway and backend Pod. If CACertificateRefs is empty or unspecified, then WellKnownCACertificates must be specified. Only one of CACertificateRefs or WellKnownCACertificates may be specified, not both. If CACertificateRefs is empty or unspecified, the configuration for WellKnownCACertificates MUST be honored instead if supported by the implementation. A CACertificateRef is invalid if: * It refers to a resource that cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured (e.g., a ConfigMap does not contain a key named `ca.crt`). In this case, the Reason must be set to `InvalidCACertificateRef` and the Message of the Condition must indicate which reference is invalid and why. * It refers to an unknown or unsupported kind of resource. In this case, the Reason must be set to `InvalidKind` and the Message of the Condition must explain which kind of resource is unknown or unsupported. * It refers to a resource in another namespace. This may change in future spec updates. Implementations MAY choose to perform further validation of the certificate content (e.g., checking expiry or enforcing specific formats). In such cases, an implementation-specific Reason and Message must be set for the invalid reference. In all cases, the implementation MUST ensure the `ResolvedRefs` Condition on the BackendTLSPolicy is set to `status: False`, with a Reason and Message that indicate the cause of the error. Connections using an invalid CACertificateRef MUST fail, and the client MUST receive an HTTP 5xx error response. If ALL CACertificateRefs are invalid, the implementation MUST also ensure the `Accepted` Condition on the BackendTLSPolicy is set to `status: False`, with a Reason `NoValidCACertificate`. A single CACertificateRef to a Kubernetes ConfigMap kind has "Core" support. Implementations MAY choose to support attaching multiple certificates to a backend, but this behavior is implementation-specific. Support: Core - An optional single reference to a Kubernetes ConfigMap, with the CA certificate in a key named `ca.crt`. Support: Implementation-specific - More than one reference, other kinds of resources, or a single reference that includes multiple certificates. items: description: |- LocalObjectReference identifies an API object within the namespace of the referrer. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "HTTPRoute" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object maxItems: 8 type: array x-kubernetes-list-type: atomic hostname: description: |- Hostname is used for two purposes in the connection between Gateways and backends: 1. Hostname MUST be used as the SNI to connect to the backend (RFC 6066). 2. Hostname MUST be used for authentication and MUST match the certificate served by the matching backend, unless SubjectAltNames is specified. 3. If SubjectAltNames are specified, Hostname can be used for certificate selection but MUST NOT be used for authentication. If you want to use the value of the Hostname field for authentication, you MUST add it to the SubjectAltNames list. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string subjectAltNames: description: |- SubjectAltNames contains one or more Subject Alternative Names. When specified the certificate served from the backend MUST have at least one Subject Alternate Name matching one of the specified SubjectAltNames. Support: Extended items: description: SubjectAltName represents Subject Alternative Name. properties: hostname: description: |- Hostname contains Subject Alternative Name specified in DNS name format. Required when Type is set to Hostname, ignored otherwise. Support: Core maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string type: description: |- Type determines the format of the Subject Alternative Name. Always required. Support: Core enum: - Hostname - URI type: string uri: description: |- URI contains Subject Alternative Name specified in a full URI format. It MUST include both a scheme (e.g., "http" or "ftp") and a scheme-specific-part. Common values include SPIFFE IDs like "spiffe://mycluster.example.com/ns/myns/sa/svc1sa". Required when Type is set to URI, ignored otherwise. Support: Core maxLength: 253 minLength: 1 pattern: ^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))? type: string required: - type type: object x-kubernetes-validations: - message: SubjectAltName element must contain Hostname, if Type is set to Hostname rule: '!(self.type == "Hostname" && (!has(self.hostname) || self.hostname == ""))' - message: SubjectAltName element must not contain Hostname, if Type is not set to Hostname rule: '!(self.type != "Hostname" && has(self.hostname) && self.hostname != "")' - message: SubjectAltName element must contain URI, if Type is set to URI rule: '!(self.type == "URI" && (!has(self.uri) || self.uri == ""))' - message: SubjectAltName element must not contain URI, if Type is not set to URI rule: '!(self.type != "URI" && has(self.uri) && self.uri != "")' maxItems: 5 type: array x-kubernetes-list-type: atomic wellKnownCACertificates: description: |- WellKnownCACertificates specifies whether a well-known set of CA certificates may be used in the TLS handshake between the gateway and backend pod. If WellKnownCACertificates is unspecified or empty (""), then CACertificateRefs must be specified with at least one entry for a valid configuration. Only one of CACertificateRefs or WellKnownCACertificates may be specified, not both. If an implementation does not support the WellKnownCACertificates field, or the supplied value is not recognized, the implementation MUST ensure the `Accepted` Condition on the BackendTLSPolicy is set to `status: False`, with a Reason `Invalid`. Valid values include: * "System" - indicates that well-known system CA certificates should be used. Implementations MAY define their own sets of CA certificates. Such definitions MUST use an implementation-specific, prefixed name, such as `mycompany.com/my-custom-ca-certifcates`. Support: Implementation-specific maxLength: 253 minLength: 1 pattern: ^(System|([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9]))$ type: string required: - hostname type: object x-kubernetes-validations: - message: must not contain both CACertificateRefs and WellKnownCACertificates rule: '!(has(self.caCertificateRefs) && size(self.caCertificateRefs) > 0 && has(self.wellKnownCACertificates) && self.wellKnownCACertificates != "")' - message: must specify either CACertificateRefs or WellKnownCACertificates rule: (has(self.caCertificateRefs) && size(self.caCertificateRefs) > 0 || has(self.wellKnownCACertificates) && self.wellKnownCACertificates != "") required: - targetRefs - validation type: object status: description: Status defines the current state of BackendTLSPolicy. properties: ancestors: description: |- Ancestors is a list of ancestor resources (usually Gateways) that are associated with the policy, and the status of the policy with respect to each ancestor. When this policy attaches to a parent, the controller that manages the parent and the ancestors MUST add an entry to this list when the controller first sees the policy and SHOULD update the entry as appropriate when the relevant ancestor is modified. Note that choosing the relevant ancestor is left to the Policy designers; an important part of Policy design is designing the right object level at which to namespace this status. Note also that implementations MUST ONLY populate ancestor status for the Ancestor resources they are responsible for. Implementations MUST use the ControllerName field to uniquely identify the entries in this list that they are responsible for. Note that to achieve this, the list of PolicyAncestorStatus structs MUST be treated as a map with a composite key, made up of the AncestorRef and ControllerName fields combined. A maximum of 16 ancestors will be represented in this list. An empty list means the Policy is not relevant for any ancestors. If this slice is full, implementations MUST NOT add further entries. Instead they MUST consider the policy unimplementable and signal that on any related resources such as the ancestor that would be referenced here. For example, if this list was full on BackendTLSPolicy, no additional Gateways would be able to reference the Service targeted by the BackendTLSPolicy. items: description: |- PolicyAncestorStatus describes the status of a route with respect to an associated Ancestor. Ancestors refer to objects that are either the Target of a policy or above it in terms of object hierarchy. For example, if a policy targets a Service, the Policy's Ancestors are, in order, the Service, the HTTPRoute, the Gateway, and the GatewayClass. Almost always, in this hierarchy, the Gateway will be the most useful object to place Policy status on, so we recommend that implementations SHOULD use Gateway as the PolicyAncestorStatus object unless the designers have a _very_ good reason otherwise. In the context of policy attachment, the Ancestor is used to distinguish which resource results in a distinct application of this policy. For example, if a policy targets a Service, it may have a distinct result per attached Gateway. Policies targeting the same resource may have different effects depending on the ancestors of those resources. For example, different Gateways targeting the same Service may have different capabilities, especially if they have different underlying implementations. For example, in BackendTLSPolicy, the Policy attaches to a Service that is used as a backend in a HTTPRoute that is itself attached to a Gateway. In this case, the relevant object for status is the Gateway, and that is the ancestor object referred to in this status. Note that a parent is also an ancestor, so for objects where the parent is the relevant object for status, this struct SHOULD still be used. This struct is intended to be used in a slice that's effectively a map, with a composite key made up of the AncestorRef and the ControllerName. properties: ancestorRef: description: |- AncestorRef corresponds with a ParentRef in the spec that this PolicyAncestorStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object conditions: description: Conditions describes the status of the Policy with respect to the given Ancestor. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string required: - ancestorRef - conditions - controllerName type: object maxItems: 16 type: array x-kubernetes-list-type: atomic required: - ancestors type: object required: - spec type: object served: false storage: false subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/standard/gateway.networking.k8s.io_gatewayclasses.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: standard name: gatewayclasses.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: GatewayClass listKind: GatewayClassList plural: gatewayclasses shortNames: - gc singular: gatewayclass scope: Cluster versions: - additionalPrinterColumns: - jsonPath: .spec.controllerName name: Controller type: string - jsonPath: .status.conditions[?(@.type=="Accepted")].status name: Accepted type: string - jsonPath: .metadata.creationTimestamp name: Age type: date - jsonPath: .spec.description name: Description priority: 1 type: string name: v1 schema: openAPIV3Schema: description: |- GatewayClass describes a class of Gateways available to the user for creating Gateway resources. It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are not propagated down to existing Gateways. This recommendation is intended to limit the blast radius of changes to GatewayClass or associated parameters. If implementations choose to propagate GatewayClass changes to existing Gateways, that MUST be clearly documented by the implementation. Whenever one or more Gateways are using a GatewayClass, implementations SHOULD add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the associated GatewayClass. This ensures that a GatewayClass associated with a Gateway is not deleted while in use. GatewayClass is a Cluster level resource. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of GatewayClass. properties: controllerName: description: |- ControllerName is the name of the controller that is managing Gateways of this class. The value of this field MUST be a domain prefixed path. Example: "example.net/gateway-controller". This field is not mutable and cannot be empty. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string x-kubernetes-validations: - message: Value is immutable rule: self == oldSelf description: description: Description helps describe a GatewayClass with more details. maxLength: 64 type: string parametersRef: description: |- ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the GatewayClass. This is optional if the controller does not require any additional configuration. ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, or an implementation-specific custom resource. The resource can be cluster-scoped or namespace-scoped. If the referent cannot be found, refers to an unsupported kind, or when the data within that resource is malformed, the GatewayClass SHOULD be rejected with the "Accepted" status condition set to "False" and an "InvalidParameters" reason. A Gateway for this GatewayClass may provide its own `parametersRef`. When both are specified, the merging behavior is implementation specific. It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. Support: Implementation-specific properties: group: description: Group is the group of the referent. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. This field is required when referring to a Namespace-scoped resource and MUST be unset when referring to a Cluster-scoped resource. maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - group - kind - name type: object required: - controllerName type: object status: default: conditions: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted description: |- Status defines the current state of GatewayClass. Implementations MUST populate status on all GatewayClass resources which specify their controller name. properties: conditions: default: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted description: |- Conditions is the current status from the controller for this GatewayClass. Controllers should prefer to publish conditions using values of GatewayClassConditionType for the type of each Condition. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map supportedFeatures: description: |- SupportedFeatures is the set of features the GatewayClass support. It MUST be sorted in ascending alphabetical order by the Name key. items: properties: name: description: |- FeatureName is used to describe distinct features that are covered by conformance tests. type: string required: - name type: object maxItems: 64 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object required: - spec type: object served: true storage: true subresources: status: {} - additionalPrinterColumns: - jsonPath: .spec.controllerName name: Controller type: string - jsonPath: .status.conditions[?(@.type=="Accepted")].status name: Accepted type: string - jsonPath: .metadata.creationTimestamp name: Age type: date - jsonPath: .spec.description name: Description priority: 1 type: string name: v1beta1 schema: openAPIV3Schema: description: |- GatewayClass describes a class of Gateways available to the user for creating Gateway resources. It is recommended that this resource be used as a template for Gateways. This means that a Gateway is based on the state of the GatewayClass at the time it was created and changes to the GatewayClass or associated parameters are not propagated down to existing Gateways. This recommendation is intended to limit the blast radius of changes to GatewayClass or associated parameters. If implementations choose to propagate GatewayClass changes to existing Gateways, that MUST be clearly documented by the implementation. Whenever one or more Gateways are using a GatewayClass, implementations SHOULD add the `gateway-exists-finalizer.gateway.networking.k8s.io` finalizer on the associated GatewayClass. This ensures that a GatewayClass associated with a Gateway is not deleted while in use. GatewayClass is a Cluster level resource. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of GatewayClass. properties: controllerName: description: |- ControllerName is the name of the controller that is managing Gateways of this class. The value of this field MUST be a domain prefixed path. Example: "example.net/gateway-controller". This field is not mutable and cannot be empty. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string x-kubernetes-validations: - message: Value is immutable rule: self == oldSelf description: description: Description helps describe a GatewayClass with more details. maxLength: 64 type: string parametersRef: description: |- ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the GatewayClass. This is optional if the controller does not require any additional configuration. ParametersRef can reference a standard Kubernetes resource, i.e. ConfigMap, or an implementation-specific custom resource. The resource can be cluster-scoped or namespace-scoped. If the referent cannot be found, refers to an unsupported kind, or when the data within that resource is malformed, the GatewayClass SHOULD be rejected with the "Accepted" status condition set to "False" and an "InvalidParameters" reason. A Gateway for this GatewayClass may provide its own `parametersRef`. When both are specified, the merging behavior is implementation specific. It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. Support: Implementation-specific properties: group: description: Group is the group of the referent. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. This field is required when referring to a Namespace-scoped resource and MUST be unset when referring to a Cluster-scoped resource. maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - group - kind - name type: object required: - controllerName type: object status: default: conditions: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted description: |- Status defines the current state of GatewayClass. Implementations MUST populate status on all GatewayClass resources which specify their controller name. properties: conditions: default: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted description: |- Conditions is the current status from the controller for this GatewayClass. Controllers should prefer to publish conditions using values of GatewayClassConditionType for the type of each Condition. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map supportedFeatures: description: |- SupportedFeatures is the set of features the GatewayClass support. It MUST be sorted in ascending alphabetical order by the Name key. items: properties: name: description: |- FeatureName is used to describe distinct features that are covered by conformance tests. type: string required: - name type: object maxItems: 64 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object required: - spec type: object served: true storage: false subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/standard/gateway.networking.k8s.io_gateways.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: standard name: gateways.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: Gateway listKind: GatewayList plural: gateways shortNames: - gtw singular: gateway scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .spec.gatewayClassName name: Class type: string - jsonPath: .status.addresses[*].value name: Address type: string - jsonPath: .status.conditions[?(@.type=="Programmed")].status name: Programmed type: string - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1 schema: openAPIV3Schema: description: |- Gateway represents an instance of a service-traffic handling infrastructure by binding Listeners to a set of IP addresses. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of Gateway. properties: addresses: description: |- Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST indicate this in an associated entry in GatewayStatus.Conditions. The Addresses field represents a request for the address(es) on the "outside of the Gateway", that traffic bound for this Gateway will use. This could be the IP address or hostname of an external load balancer or other networking infrastructure, or some other address that traffic will be sent to. If no Addresses are specified, the implementation MAY schedule the Gateway in an implementation-specific manner, assigning an appropriate set of Addresses. The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. Support: Extended items: description: GatewaySpecAddress describes an address that can be bound to a Gateway. oneOf: - properties: type: enum: - IPAddress value: anyOf: - format: ipv4 - format: ipv6 - properties: type: not: enum: - IPAddress properties: type: default: IPAddress description: Type of the address. maxLength: 253 minLength: 1 pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string value: description: |- When a value is unspecified, an implementation SHOULD automatically assign an address matching the requested type if possible. If an implementation does not support an empty value, they MUST set the "Programmed" condition in status to False with a reason of "AddressNotAssigned". Examples: `1.2.3.4`, `128::1`, `my-ip-address`. maxLength: 253 type: string type: object x-kubernetes-validations: - message: Hostname value must be empty or contain only valid characters (matching ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) rule: 'self.type == ''Hostname'' ? (!has(self.value) || self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$""")): true' maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: IPAddress values must be unique rule: 'self.all(a1, a1.type == ''IPAddress'' && has(a1.value) ? self.exists_one(a2, a2.type == a1.type && has(a2.value) && a2.value == a1.value) : true )' - message: Hostname values must be unique rule: 'self.all(a1, a1.type == ''Hostname'' && has(a1.value) ? self.exists_one(a2, a2.type == a1.type && has(a2.value) && a2.value == a1.value) : true )' allowedListeners: description: |- AllowedListeners defines which ListenerSets can be attached to this Gateway. The default value is to allow no ListenerSets. properties: namespaces: default: from: None description: |- Namespaces defines which namespaces ListenerSets can be attached to this Gateway. The default value is to allow no ListenerSets. properties: from: default: None description: |- From indicates where ListenerSets can attach to this Gateway. Possible values are: * Same: Only ListenerSets in the same namespace may be attached to this Gateway. * Selector: ListenerSets in namespaces selected by the selector may be attached to this Gateway. * All: ListenerSets in all namespaces may be attached to this Gateway. * None: Only listeners defined in the Gateway's spec are allowed The default value None enum: - All - Selector - Same - None type: string selector: description: |- Selector must be specified when From is set to "Selector". In that case, only ListenerSets in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of "From". properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic type: object type: object gatewayClassName: description: |- GatewayClassName used for this Gateway. This is the name of a GatewayClass resource. maxLength: 253 minLength: 1 type: string infrastructure: description: |- Infrastructure defines infrastructure level attributes about this Gateway instance. Support: Extended properties: annotations: additionalProperties: description: |- AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. maxLength: 4096 minLength: 0 type: string description: |- Annotations that SHOULD be applied to any resources created in response to this Gateway. For implementations creating other Kubernetes objects, this should be the `metadata.annotations` field on resources. For other implementations, this refers to any relevant (implementation specific) "annotations" concepts. An implementation may chose to add additional implementation-specific annotations as they see fit. Support: Extended maxProperties: 8 type: object x-kubernetes-validations: - message: Annotation keys must be in the form of an optional DNS subdomain prefix followed by a required name segment of up to 63 characters. rule: self.all(key, key.matches(r"""^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9]$""")) - message: If specified, the annotation key's prefix must be a DNS subdomain not longer than 253 characters in total. rule: self.all(key, key.split("/")[0].size() < 253) labels: additionalProperties: description: |- LabelValue is the value of a label in the Gateway API. This is used for validation of maps such as Gateway infrastructure labels. This matches the Kubernetes label validation rules: * must be 63 characters or less (can be empty), * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. Valid values include: * MyValue * my.name * 123-my-value maxLength: 63 minLength: 0 pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels that SHOULD be applied to any resources created in response to this Gateway. For implementations creating other Kubernetes objects, this should be the `metadata.labels` field on resources. For other implementations, this refers to any relevant (implementation specific) "labels" concepts. An implementation may chose to add additional implementation-specific labels as they see fit. If an implementation maps these labels to Pods, or any other resource that would need to be recreated when labels change, it SHOULD clearly warn about this behavior in documentation. Support: Extended maxProperties: 8 type: object x-kubernetes-validations: - message: Label keys must be in the form of an optional DNS subdomain prefix followed by a required name segment of up to 63 characters. rule: self.all(key, key.matches(r"""^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9]$""")) - message: If specified, the label key's prefix must be a DNS subdomain not longer than 253 characters in total. rule: self.all(key, key.split("/")[0].size() < 253) parametersRef: description: |- ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the Gateway. This is optional if the controller does not require any additional configuration. This follows the same semantics as GatewayClass's `parametersRef`, but on a per-Gateway basis The Gateway's GatewayClass may provide its own `parametersRef`. When both are specified, the merging behavior is implementation specific. It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. If the referent cannot be found, refers to an unsupported kind, or when the data within that resource is malformed, the Gateway SHOULD be rejected with the "Accepted" status condition set to "False" and an "InvalidParameters" reason. Support: Implementation-specific properties: group: description: Group is the group of the referent. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object type: object listeners: description: |- Listeners associated with this Gateway. Listeners define logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. ## Distinct Listeners Each Listener in a set of Listeners (for example, in a single Gateway) MUST be _distinct_, in that a traffic flow MUST be able to be assigned to exactly one listener. (This section uses "set of Listeners" rather than "Listeners in a single Gateway" because implementations MAY merge configuration from multiple Gateways onto a single data plane, and these rules _also_ apply in that case). Practically, this means that each listener in a set MUST have a unique combination of Port, Protocol, and, if supported by the protocol, Hostname. Some combinations of port, protocol, and TLS settings are considered Core support and MUST be supported by implementations based on the objects they support: HTTPRoute 1. HTTPRoute, Port: 80, Protocol: HTTP 2. HTTPRoute, Port: 443, Protocol: HTTPS, TLS Mode: Terminate, TLS keypair provided TLSRoute 1. TLSRoute, Port: 443, Protocol: TLS, TLS Mode: Passthrough "Distinct" Listeners have the following property: **The implementation can match inbound requests to a single distinct Listener**. When multiple Listeners share values for fields (for example, two Listeners with the same Port value), the implementation can match requests to only one of the Listeners using other Listener fields. When multiple listeners have the same value for the Protocol field, then each of the Listeners with matching Protocol values MUST have different values for other fields. The set of fields that MUST be different for a Listener differs per protocol. The following rules define the rules for what fields MUST be considered for Listeners to be distinct with each protocol currently defined in the Gateway API spec. The set of listeners that all share a protocol value MUST have _different_ values for _at least one_ of these fields to be distinct: * **HTTP, HTTPS, TLS**: Port, Hostname * **TCP, UDP**: Port One **very** important rule to call out involves what happens when an implementation: * Supports TCP protocol Listeners, as well as HTTP, HTTPS, or TLS protocol Listeners, and * sees HTTP, HTTPS, or TLS protocols with the same `port` as one with TCP Protocol. In this case all the Listeners that share a port with the TCP Listener are not distinct and so MUST NOT be accepted. If an implementation does not support TCP Protocol Listeners, then the previous rule does not apply, and the TCP Listeners SHOULD NOT be accepted. Note that the `tls` field is not used for determining if a listener is distinct, because Listeners that _only_ differ on TLS config will still conflict in all cases. ### Listeners that are distinct only by Hostname When the Listeners are distinct based only on Hostname, inbound request hostnames MUST match from the most specific to least specific Hostname values to choose the correct Listener and its associated set of Routes. Exact matches MUST be processed before wildcard matches, and wildcard matches MUST be processed before fallback (empty Hostname value) matches. For example, `"foo.example.com"` takes precedence over `"*.example.com"`, and `"*.example.com"` takes precedence over `""`. Additionally, if there are multiple wildcard entries, more specific wildcard entries must be processed before less specific wildcard entries. For example, `"*.foo.example.com"` takes precedence over `"*.example.com"`. The precise definition here is that the higher the number of dots in the hostname to the right of the wildcard character, the higher the precedence. The wildcard character will match any number of characters _and dots_ to the left, however, so `"*.example.com"` will match both `"foo.bar.example.com"` _and_ `"bar.example.com"`. ## Handling indistinct Listeners If a set of Listeners contains Listeners that are not distinct, then those Listeners are _Conflicted_, and the implementation MUST set the "Conflicted" condition in the Listener Status to "True". The words "indistinct" and "conflicted" are considered equivalent for the purpose of this documentation. Implementations MAY choose to accept a Gateway with some Conflicted Listeners only if they only accept the partial Listener set that contains no Conflicted Listeners. Specifically, an implementation MAY accept a partial Listener set subject to the following rules: * The implementation MUST NOT pick one conflicting Listener as the winner. ALL indistinct Listeners must not be accepted for processing. * At least one distinct Listener MUST be present, or else the Gateway effectively contains _no_ Listeners, and must be rejected from processing as a whole. The implementation MUST set a "ListenersNotValid" condition on the Gateway Status when the Gateway contains Conflicted Listeners whether or not they accept the Gateway. That Condition SHOULD clearly indicate in the Message which Listeners are conflicted, and which are Accepted. Additionally, the Listener status for those listeners SHOULD indicate which Listeners are conflicted and not Accepted. ## General Listener behavior Note that, for all distinct Listeners, requests SHOULD match at most one Listener. For example, if Listeners are defined for "foo.example.com" and "*.example.com", a request to "foo.example.com" SHOULD only be routed using routes attached to the "foo.example.com" Listener (and not the "*.example.com" Listener). This concept is known as "Listener Isolation", and it is an Extended feature of Gateway API. Implementations that do not support Listener Isolation MUST clearly document this, and MUST NOT claim support for the `GatewayHTTPListenerIsolation` feature. Implementations that _do_ support Listener Isolation SHOULD claim support for the Extended `GatewayHTTPListenerIsolation` feature and pass the associated conformance tests. ## Compatible Listeners A Gateway's Listeners are considered _compatible_ if: 1. They are distinct. 2. The implementation can serve them in compliance with the Addresses requirement that all Listeners are available on all assigned addresses. Compatible combinations in Extended support are expected to vary across implementations. A combination that is compatible for one implementation may not be compatible for another. For example, an implementation that cannot serve both TCP and UDP listeners on the same address, or cannot mix HTTPS and generic TLS listens on the same port would not consider those cases compatible, even though they are distinct. Implementations MAY merge separate Gateways onto a single set of Addresses if all Listeners across all Gateways are compatible. In a future release the MinItems=1 requirement MAY be dropped. Support: Core items: description: |- Listener embodies the concept of a logical endpoint where a Gateway accepts network connections. properties: allowedRoutes: default: namespaces: from: Same description: |- AllowedRoutes defines the types of routes that MAY be attached to a Listener and the trusted namespaces where those Route resources MAY be present. Although a client request may match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: * The most specific match as defined by the Route type. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of "2020-09-08 01:02:03" is given precedence over a Route with a creation timestamp of "2020-09-08 01:02:04". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. All valid rules within a Route attached to this Listener should be implemented. Invalid Route rules can be ignored (sometimes that will mean the full Route). If a Route rule transitions from valid to invalid, support for that Route rule should be dropped to ensure consistency. For example, even if a filter specified by a Route rule is invalid, the rest of the rules within that Route should still be supported. Support: Core properties: kinds: description: |- Kinds specifies the groups and kinds of Routes that are allowed to bind to this Gateway Listener. When unspecified or empty, the kinds of Routes selected are determined using the Listener protocol. A RouteGroupKind MUST correspond to kinds of Routes that are compatible with the application protocol specified in the Listener's Protocol field. If an implementation does not support or recognize this resource type, it MUST set the "ResolvedRefs" condition to False for this Listener with the "InvalidRouteKinds" reason. Support: Core items: description: RouteGroupKind indicates the group and kind of a Route resource. properties: group: default: gateway.networking.k8s.io description: Group is the group of the Route. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is the kind of the Route. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string required: - kind type: object maxItems: 8 type: array x-kubernetes-list-type: atomic namespaces: default: from: Same description: |- Namespaces indicates namespaces from which Routes may be attached to this Listener. This is restricted to the namespace of this Gateway by default. Support: Core properties: from: default: Same description: |- From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. Support: Core enum: - All - Selector - Same type: string selector: description: |- Selector must be specified when From is set to "Selector". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of "From". Support: Core properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic type: object type: object hostname: description: |- Hostname specifies the virtual hostname to match for protocol types that define this concept. When unspecified, all hostnames are matched. This field is ignored for protocols that don't require hostname based matching. Implementations MUST apply Hostname matching appropriately for each of the following protocols: * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. * HTTPS: The Listener Hostname SHOULD match both the SNI and Host header. Note that this does not require the SNI and Host header to be the same. The semantics of this are described in more detail below. To ensure security, Section 11.1 of RFC-6066 emphasizes that server implementations that rely on SNI hostname matching MUST also verify hostnames within the application protocol. Section 9.1.2 of RFC-7540 provides a mechanism for servers to reject the reuse of a connection by responding with the HTTP 421 Misdirected Request status code. This indicates that the origin server has rejected the request because it appears to have been misdirected. To detect misdirected requests, Gateways SHOULD match the authority of the requests with all the SNI hostname(s) configured across all the Gateway Listeners on the same port and protocol: * If another Listener has an exact match or more specific wildcard entry, the Gateway SHOULD return a 421. * If the current Listener (selected by SNI matching during ClientHello) does not match the Host: * If another Listener does match the Host, the Gateway SHOULD return a 421. * If no other Listener matches the Host, the Gateway MUST return a 404. For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, there MUST be an intersection between the values for a Route to be accepted. For more information, refer to the Route specific Hostnames documentation. Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. Support: Core maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string name: description: |- Name is the name of the Listener. This name MUST be unique within a Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string port: description: |- Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. Support: Core format: int32 maximum: 65535 minimum: 1 type: integer protocol: description: |- Protocol specifies the network protocol this listener expects to receive. Support: Core maxLength: 255 minLength: 1 pattern: ^[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ type: string tls: description: |- TLS is the TLS configuration for the Listener. This field is required if the Protocol field is "HTTPS" or "TLS". It is invalid to set this field if the Protocol field is "HTTP", "TCP", or "UDP". The association of SNIs to Certificate defined in ListenerTLSConfig is defined based on the Hostname field for this listener. The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. Support: Core properties: certificateRefs: description: |- CertificateRefs contains a series of references to Kubernetes objects that contains TLS certificates and private keys. These certificates are used to establish a TLS handshake for requests that match the hostname of the associated listener. A single CertificateRef to a Kubernetes Secret has "Core" support. Implementations MAY choose to support attaching multiple certificates to a Listener, but this behavior is implementation-specific. References to a resource in different namespace are invalid UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the "ResolvedRefs" condition MUST be set to False for this listener with the "RefNotPermitted" reason. This field is required to have at least one element when the mode is set to "Terminate" (default) and is optional otherwise. CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls Support: Implementation-specific (More than one reference or other resource types) items: description: |- SecretObjectReference identifies an API object including its namespace, defaulting to Secret. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Secret description: Kind is kind of the referent. For example "Secret". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - name type: object maxItems: 64 type: array x-kubernetes-list-type: atomic mode: default: Terminate description: |- Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificates to be specified in some way, such as populating the certificateRefs field. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. The certificateRefs field is ignored in this mode. Support: Core enum: - Terminate - Passthrough type: string options: additionalProperties: description: |- AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. maxLength: 4096 minLength: 0 type: string description: |- Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. Support: Implementation-specific maxProperties: 16 type: object type: object x-kubernetes-validations: - message: certificateRefs or options must be specified when mode is Terminate rule: 'self.mode == ''Terminate'' ? size(self.certificateRefs) > 0 || size(self.options) > 0 : true' required: - name - port - protocol type: object maxItems: 64 minItems: 1 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map x-kubernetes-validations: - message: tls must not be specified for protocols ['HTTP', 'TCP', 'UDP'] rule: 'self.all(l, l.protocol in [''HTTP'', ''TCP'', ''UDP''] ? !has(l.tls) : true)' - message: tls mode must be Terminate for protocol HTTPS rule: 'self.all(l, (l.protocol == ''HTTPS'' && has(l.tls)) ? (l.tls.mode == '''' || l.tls.mode == ''Terminate'') : true)' - message: tls mode must be set for protocol TLS rule: 'self.all(l, (l.protocol == ''TLS'' ? has(l.tls) && has(l.tls.mode) && l.tls.mode != '''' : true))' - message: hostname must not be specified for protocols ['TCP', 'UDP'] rule: 'self.all(l, l.protocol in [''TCP'', ''UDP''] ? (!has(l.hostname) || l.hostname == '''') : true)' - message: Listener name must be unique within the Gateway rule: self.all(l1, self.exists_one(l2, l1.name == l2.name)) - message: Combination of port, protocol and hostname must be unique for each listener rule: 'self.all(l1, self.exists_one(l2, l1.port == l2.port && l1.protocol == l2.protocol && (has(l1.hostname) && has(l2.hostname) ? l1.hostname == l2.hostname : !has(l1.hostname) && !has(l2.hostname))))' tls: description: |- TLS specifies frontend and backend tls configuration for entire gateway. Support: Extended properties: backend: description: |- Backend describes TLS configuration for gateway when connecting to backends. Note that this contains only details for the Gateway as a TLS client, and does _not_ imply behavior about how to choose which backend should get a TLS connection. That is determined by the presence of a BackendTLSPolicy. Support: Core properties: clientCertificateRef: description: |- ClientCertificateRef references an object that contains a client certificate and its associated private key. It can reference standard Kubernetes resources, i.e., Secret, or implementation-specific custom resources. A ClientCertificateRef is considered invalid if: * It refers to a resource that cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured (e.g., a Secret does not contain the keys named `tls.crt` and `tls.key`). In this case, the `ResolvedRefs` condition on the Gateway MUST be set to False with the Reason `InvalidClientCertificateRef` and the Message of the Condition MUST indicate why the reference is invalid. * It refers to a resource in another namespace UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the `ResolvedRefs` condition on the Gateway MUST be set to False with the Reason `RefNotPermitted`. Implementations MAY choose to perform further validation of the certificate content (e.g., checking expiry or enforcing specific formats). In such cases, an implementation-specific Reason and Message MUST be set. Support: Core - Reference to a Kubernetes TLS Secret (with the type `kubernetes.io/tls`). Support: Implementation-specific - Other resource kinds or Secrets with a different type (e.g., `Opaque`). properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Secret description: Kind is kind of the referent. For example "Secret". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - name type: object type: object frontend: description: |- Frontend describes TLS config when client connects to Gateway. Support: Core properties: default: description: |- Default specifies the default client certificate validation configuration for all Listeners handling HTTPS traffic, unless a per-port configuration is defined. support: Core properties: validation: description: |- Validation holds configuration information for validating the frontend (client). Setting this field will result in mutual authentication when connecting to the gateway. In browsers this may result in a dialog appearing that requests a user to specify the client certificate. The maximum depth of a certificate chain accepted in verification is Implementation specific. Support: Core properties: caCertificateRefs: description: |- CACertificateRefs contains one or more references to Kubernetes objects that contain a PEM-encoded TLS CA certificate bundle, which is used as a trust anchor to validate the certificates presented by the client. A CACertificateRef is invalid if: * It refers to a resource that cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured (e.g., a ConfigMap does not contain a key named `ca.crt`). In this case, the Reason on all matching HTTPS listeners must be set to `InvalidCACertificateRef` and the Message of the Condition must indicate which reference is invalid and why. * It refers to an unknown or unsupported kind of resource. In this case, the Reason on all matching HTTPS listeners must be set to `InvalidCACertificateKind` and the Message of the Condition must explain which kind of resource is unknown or unsupported. * It refers to a resource in another namespace UNLESS there is a ReferenceGrant in the target namespace that allows the CA certificate to be attached. If a ReferenceGrant does not allow this reference, the `ResolvedRefs` on all matching HTTPS listeners condition MUST be set with the Reason `RefNotPermitted`. Implementations MAY choose to perform further validation of the certificate content (e.g., checking expiry or enforcing specific formats). In such cases, an implementation-specific Reason and Message MUST be set. In all cases, the implementation MUST ensure that the `ResolvedRefs` condition is set to `status: False` on all targeted listeners (i.e., listeners serving HTTPS on a matching port). The condition MUST include a Reason and Message that indicate the cause of the error. If ALL CACertificateRefs are invalid, the implementation MUST also ensure the `Accepted` condition on the listener is set to `status: False`, with the Reason `NoValidCACertificate`. Implementations MAY choose to support attaching multiple CA certificates to a listener, but this behavior is implementation-specific. Support: Core - A single reference to a Kubernetes ConfigMap, with the CA certificate in a key named `ca.crt`. Support: Implementation-specific - More than one reference, other kinds of resources, or a single reference that includes multiple certificates. items: description: |- ObjectReference identifies an API object including its namespace. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When set to the empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "ConfigMap" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - group - kind - name type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-type: atomic mode: default: AllowValidOnly description: |- FrontendValidationMode defines the mode for validating the client certificate. There are two possible modes: - AllowValidOnly: In this mode, the gateway will accept connections only if the client presents a valid certificate. This certificate must successfully pass validation against the CA certificates specified in `CACertificateRefs`. - AllowInsecureFallback: In this mode, the gateway will accept connections even if the client certificate is not presented or fails verification. This approach delegates client authorization to the backend and introduce a significant security risk. It should be used in testing environments or on a temporary basis in non-testing environments. Defaults to AllowValidOnly. Support: Core enum: - AllowValidOnly - AllowInsecureFallback type: string required: - caCertificateRefs type: object type: object perPort: description: |- PerPort specifies tls configuration assigned per port. Per port configuration is optional. Once set this configuration overrides the default configuration for all Listeners handling HTTPS traffic that match this port. Each override port requires a unique TLS configuration. support: Core items: properties: port: description: |- The Port indicates the Port Number to which the TLS configuration will be applied. This configuration will be applied to all Listeners handling HTTPS traffic that match this port. Support: Core format: int32 maximum: 65535 minimum: 1 type: integer tls: description: |- TLS store the configuration that will be applied to all Listeners handling HTTPS traffic and matching given port. Support: Core properties: validation: description: |- Validation holds configuration information for validating the frontend (client). Setting this field will result in mutual authentication when connecting to the gateway. In browsers this may result in a dialog appearing that requests a user to specify the client certificate. The maximum depth of a certificate chain accepted in verification is Implementation specific. Support: Core properties: caCertificateRefs: description: |- CACertificateRefs contains one or more references to Kubernetes objects that contain a PEM-encoded TLS CA certificate bundle, which is used as a trust anchor to validate the certificates presented by the client. A CACertificateRef is invalid if: * It refers to a resource that cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured (e.g., a ConfigMap does not contain a key named `ca.crt`). In this case, the Reason on all matching HTTPS listeners must be set to `InvalidCACertificateRef` and the Message of the Condition must indicate which reference is invalid and why. * It refers to an unknown or unsupported kind of resource. In this case, the Reason on all matching HTTPS listeners must be set to `InvalidCACertificateKind` and the Message of the Condition must explain which kind of resource is unknown or unsupported. * It refers to a resource in another namespace UNLESS there is a ReferenceGrant in the target namespace that allows the CA certificate to be attached. If a ReferenceGrant does not allow this reference, the `ResolvedRefs` on all matching HTTPS listeners condition MUST be set with the Reason `RefNotPermitted`. Implementations MAY choose to perform further validation of the certificate content (e.g., checking expiry or enforcing specific formats). In such cases, an implementation-specific Reason and Message MUST be set. In all cases, the implementation MUST ensure that the `ResolvedRefs` condition is set to `status: False` on all targeted listeners (i.e., listeners serving HTTPS on a matching port). The condition MUST include a Reason and Message that indicate the cause of the error. If ALL CACertificateRefs are invalid, the implementation MUST also ensure the `Accepted` condition on the listener is set to `status: False`, with the Reason `NoValidCACertificate`. Implementations MAY choose to support attaching multiple CA certificates to a listener, but this behavior is implementation-specific. Support: Core - A single reference to a Kubernetes ConfigMap, with the CA certificate in a key named `ca.crt`. Support: Implementation-specific - More than one reference, other kinds of resources, or a single reference that includes multiple certificates. items: description: |- ObjectReference identifies an API object including its namespace. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When set to the empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "ConfigMap" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - group - kind - name type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-type: atomic mode: default: AllowValidOnly description: |- FrontendValidationMode defines the mode for validating the client certificate. There are two possible modes: - AllowValidOnly: In this mode, the gateway will accept connections only if the client presents a valid certificate. This certificate must successfully pass validation against the CA certificates specified in `CACertificateRefs`. - AllowInsecureFallback: In this mode, the gateway will accept connections even if the client certificate is not presented or fails verification. This approach delegates client authorization to the backend and introduce a significant security risk. It should be used in testing environments or on a temporary basis in non-testing environments. Defaults to AllowValidOnly. Support: Core enum: - AllowValidOnly - AllowInsecureFallback type: string required: - caCertificateRefs type: object type: object required: - port - tls type: object maxItems: 64 type: array x-kubernetes-list-map-keys: - port x-kubernetes-list-type: map x-kubernetes-validations: - message: Port for TLS configuration must be unique within the Gateway rule: self.all(t1, self.exists_one(t2, t1.port == t2.port)) required: - default type: object type: object required: - gatewayClassName - listeners type: object status: default: conditions: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Programmed description: Status defines the current state of Gateway. properties: addresses: description: |- Addresses lists the network addresses that have been bound to the Gateway. This list may differ from the addresses provided in the spec under some conditions: * no addresses are specified, all addresses are dynamically assigned * a combination of specified and dynamic addresses are assigned * a specified address was unusable (e.g. already in use) items: description: GatewayStatusAddress describes a network address that is bound to a Gateway. oneOf: - properties: type: enum: - IPAddress value: anyOf: - format: ipv4 - format: ipv6 - properties: type: not: enum: - IPAddress properties: type: default: IPAddress description: Type of the address. maxLength: 253 minLength: 1 pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string value: description: |- Value of the address. The validity of the values will depend on the type and support by the controller. Examples: `1.2.3.4`, `128::1`, `my-ip-address`. maxLength: 253 minLength: 1 type: string required: - value type: object x-kubernetes-validations: - message: Hostname value must only contain valid characters (matching ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) rule: 'self.type == ''Hostname'' ? self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"""): true' maxItems: 16 type: array x-kubernetes-list-type: atomic attachedListenerSets: description: |- AttachedListenerSets represents the total number of ListenerSets that have been successfully attached to this Gateway. A ListenerSet is successfully attached to a Gateway when all the following conditions are met: - The ListenerSet is selected by the Gateway's AllowedListeners field - The ListenerSet has a valid ParentRef selecting the Gateway - The ListenerSet's status has the condition "Accepted: true" Uses for this field include troubleshooting AttachedListenerSets attachment and measuring blast radius/impact of changes to a Gateway. format: int32 type: integer conditions: default: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Programmed description: |- Conditions describe the current conditions of the Gateway. Implementations should prefer to express Gateway conditions using the `GatewayConditionType` and `GatewayConditionReason` constants so that operators and tools can converge on a common vocabulary to describe Gateway state. Known condition types are: * "Accepted" * "Programmed" * "Ready" items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map listeners: description: Listeners provide status for each unique listener port defined in the Spec. items: description: ListenerStatus is the status associated with a Listener. properties: attachedRoutes: description: |- AttachedRoutes represents the total number of Routes that have been successfully attached to this Listener. Successful attachment of a Route to a Listener is based solely on the combination of the AllowedRoutes field on the corresponding Listener and the Route's ParentRefs field. A Route is successfully attached to a Listener when it is selected by the Listener's AllowedRoutes field AND the Route has a valid ParentRef selecting the whole Gateway resource or a specific Listener as a parent resource (more detail on attachment semantics can be found in the documentation on the various Route kinds ParentRefs fields). Listener or Route status does not impact successful attachment, i.e. the AttachedRoutes field count MUST be set for Listeners, even if the Accepted condition of an individual Listener is set to "False". The AttachedRoutes number represents the number of Routes with the Accepted condition set to "True" that have been attached to this Listener. Routes with any other value for the Accepted condition MUST NOT be included in this count. Uses for this field include troubleshooting Route attachment and measuring blast radius/impact of changes to a Listener. format: int32 type: integer conditions: description: Conditions describe the current condition of this listener. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map name: description: Name is the name of the Listener that this status corresponds to. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string supportedKinds: description: |- SupportedKinds is the list indicating the Kinds supported by this listener. This MUST represent the kinds supported by an implementation for that Listener configuration. If kinds are specified in Spec that are not supported, they MUST NOT appear in this list and an implementation MUST set the "ResolvedRefs" condition to "False" with the "InvalidRouteKinds" reason. If both valid and invalid Route kinds are specified, the implementation MUST reference the valid Route kinds that have been specified. items: description: RouteGroupKind indicates the group and kind of a Route resource. properties: group: default: gateway.networking.k8s.io description: Group is the group of the Route. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is the kind of the Route. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string required: - kind type: object maxItems: 8 type: array x-kubernetes-list-type: atomic required: - attachedRoutes - conditions - name type: object maxItems: 64 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object required: - spec type: object served: true storage: true subresources: status: {} - additionalPrinterColumns: - jsonPath: .spec.gatewayClassName name: Class type: string - jsonPath: .status.addresses[*].value name: Address type: string - jsonPath: .status.conditions[?(@.type=="Programmed")].status name: Programmed type: string - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1beta1 schema: openAPIV3Schema: description: |- Gateway represents an instance of a service-traffic handling infrastructure by binding Listeners to a set of IP addresses. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of Gateway. properties: addresses: description: |- Addresses requested for this Gateway. This is optional and behavior can depend on the implementation. If a value is set in the spec and the requested address is invalid or unavailable, the implementation MUST indicate this in an associated entry in GatewayStatus.Conditions. The Addresses field represents a request for the address(es) on the "outside of the Gateway", that traffic bound for this Gateway will use. This could be the IP address or hostname of an external load balancer or other networking infrastructure, or some other address that traffic will be sent to. If no Addresses are specified, the implementation MAY schedule the Gateway in an implementation-specific manner, assigning an appropriate set of Addresses. The implementation MUST bind all Listeners to every GatewayAddress that it assigns to the Gateway and add a corresponding entry in GatewayStatus.Addresses. Support: Extended items: description: GatewaySpecAddress describes an address that can be bound to a Gateway. oneOf: - properties: type: enum: - IPAddress value: anyOf: - format: ipv4 - format: ipv6 - properties: type: not: enum: - IPAddress properties: type: default: IPAddress description: Type of the address. maxLength: 253 minLength: 1 pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string value: description: |- When a value is unspecified, an implementation SHOULD automatically assign an address matching the requested type if possible. If an implementation does not support an empty value, they MUST set the "Programmed" condition in status to False with a reason of "AddressNotAssigned". Examples: `1.2.3.4`, `128::1`, `my-ip-address`. maxLength: 253 type: string type: object x-kubernetes-validations: - message: Hostname value must be empty or contain only valid characters (matching ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) rule: 'self.type == ''Hostname'' ? (!has(self.value) || self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$""")): true' maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: IPAddress values must be unique rule: 'self.all(a1, a1.type == ''IPAddress'' && has(a1.value) ? self.exists_one(a2, a2.type == a1.type && has(a2.value) && a2.value == a1.value) : true )' - message: Hostname values must be unique rule: 'self.all(a1, a1.type == ''Hostname'' && has(a1.value) ? self.exists_one(a2, a2.type == a1.type && has(a2.value) && a2.value == a1.value) : true )' allowedListeners: description: |- AllowedListeners defines which ListenerSets can be attached to this Gateway. The default value is to allow no ListenerSets. properties: namespaces: default: from: None description: |- Namespaces defines which namespaces ListenerSets can be attached to this Gateway. The default value is to allow no ListenerSets. properties: from: default: None description: |- From indicates where ListenerSets can attach to this Gateway. Possible values are: * Same: Only ListenerSets in the same namespace may be attached to this Gateway. * Selector: ListenerSets in namespaces selected by the selector may be attached to this Gateway. * All: ListenerSets in all namespaces may be attached to this Gateway. * None: Only listeners defined in the Gateway's spec are allowed The default value None enum: - All - Selector - Same - None type: string selector: description: |- Selector must be specified when From is set to "Selector". In that case, only ListenerSets in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of "From". properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic type: object type: object gatewayClassName: description: |- GatewayClassName used for this Gateway. This is the name of a GatewayClass resource. maxLength: 253 minLength: 1 type: string infrastructure: description: |- Infrastructure defines infrastructure level attributes about this Gateway instance. Support: Extended properties: annotations: additionalProperties: description: |- AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. maxLength: 4096 minLength: 0 type: string description: |- Annotations that SHOULD be applied to any resources created in response to this Gateway. For implementations creating other Kubernetes objects, this should be the `metadata.annotations` field on resources. For other implementations, this refers to any relevant (implementation specific) "annotations" concepts. An implementation may chose to add additional implementation-specific annotations as they see fit. Support: Extended maxProperties: 8 type: object x-kubernetes-validations: - message: Annotation keys must be in the form of an optional DNS subdomain prefix followed by a required name segment of up to 63 characters. rule: self.all(key, key.matches(r"""^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9]$""")) - message: If specified, the annotation key's prefix must be a DNS subdomain not longer than 253 characters in total. rule: self.all(key, key.split("/")[0].size() < 253) labels: additionalProperties: description: |- LabelValue is the value of a label in the Gateway API. This is used for validation of maps such as Gateway infrastructure labels. This matches the Kubernetes label validation rules: * must be 63 characters or less (can be empty), * unless empty, must begin and end with an alphanumeric character ([a-z0-9A-Z]), * could contain dashes (-), underscores (_), dots (.), and alphanumerics between. Valid values include: * MyValue * my.name * 123-my-value maxLength: 63 minLength: 0 pattern: ^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])?$ type: string description: |- Labels that SHOULD be applied to any resources created in response to this Gateway. For implementations creating other Kubernetes objects, this should be the `metadata.labels` field on resources. For other implementations, this refers to any relevant (implementation specific) "labels" concepts. An implementation may chose to add additional implementation-specific labels as they see fit. If an implementation maps these labels to Pods, or any other resource that would need to be recreated when labels change, it SHOULD clearly warn about this behavior in documentation. Support: Extended maxProperties: 8 type: object x-kubernetes-validations: - message: Label keys must be in the form of an optional DNS subdomain prefix followed by a required name segment of up to 63 characters. rule: self.all(key, key.matches(r"""^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?([A-Za-z0-9][-A-Za-z0-9_.]{0,61})?[A-Za-z0-9]$""")) - message: If specified, the label key's prefix must be a DNS subdomain not longer than 253 characters in total. rule: self.all(key, key.split("/")[0].size() < 253) parametersRef: description: |- ParametersRef is a reference to a resource that contains the configuration parameters corresponding to the Gateway. This is optional if the controller does not require any additional configuration. This follows the same semantics as GatewayClass's `parametersRef`, but on a per-Gateway basis The Gateway's GatewayClass may provide its own `parametersRef`. When both are specified, the merging behavior is implementation specific. It is generally recommended that GatewayClass provides defaults that can be overridden by a Gateway. If the referent cannot be found, refers to an unsupported kind, or when the data within that resource is malformed, the Gateway SHOULD be rejected with the "Accepted" status condition set to "False" and an "InvalidParameters" reason. Support: Implementation-specific properties: group: description: Group is the group of the referent. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object type: object listeners: description: |- Listeners associated with this Gateway. Listeners define logical endpoints that are bound on this Gateway's addresses. At least one Listener MUST be specified. ## Distinct Listeners Each Listener in a set of Listeners (for example, in a single Gateway) MUST be _distinct_, in that a traffic flow MUST be able to be assigned to exactly one listener. (This section uses "set of Listeners" rather than "Listeners in a single Gateway" because implementations MAY merge configuration from multiple Gateways onto a single data plane, and these rules _also_ apply in that case). Practically, this means that each listener in a set MUST have a unique combination of Port, Protocol, and, if supported by the protocol, Hostname. Some combinations of port, protocol, and TLS settings are considered Core support and MUST be supported by implementations based on the objects they support: HTTPRoute 1. HTTPRoute, Port: 80, Protocol: HTTP 2. HTTPRoute, Port: 443, Protocol: HTTPS, TLS Mode: Terminate, TLS keypair provided TLSRoute 1. TLSRoute, Port: 443, Protocol: TLS, TLS Mode: Passthrough "Distinct" Listeners have the following property: **The implementation can match inbound requests to a single distinct Listener**. When multiple Listeners share values for fields (for example, two Listeners with the same Port value), the implementation can match requests to only one of the Listeners using other Listener fields. When multiple listeners have the same value for the Protocol field, then each of the Listeners with matching Protocol values MUST have different values for other fields. The set of fields that MUST be different for a Listener differs per protocol. The following rules define the rules for what fields MUST be considered for Listeners to be distinct with each protocol currently defined in the Gateway API spec. The set of listeners that all share a protocol value MUST have _different_ values for _at least one_ of these fields to be distinct: * **HTTP, HTTPS, TLS**: Port, Hostname * **TCP, UDP**: Port One **very** important rule to call out involves what happens when an implementation: * Supports TCP protocol Listeners, as well as HTTP, HTTPS, or TLS protocol Listeners, and * sees HTTP, HTTPS, or TLS protocols with the same `port` as one with TCP Protocol. In this case all the Listeners that share a port with the TCP Listener are not distinct and so MUST NOT be accepted. If an implementation does not support TCP Protocol Listeners, then the previous rule does not apply, and the TCP Listeners SHOULD NOT be accepted. Note that the `tls` field is not used for determining if a listener is distinct, because Listeners that _only_ differ on TLS config will still conflict in all cases. ### Listeners that are distinct only by Hostname When the Listeners are distinct based only on Hostname, inbound request hostnames MUST match from the most specific to least specific Hostname values to choose the correct Listener and its associated set of Routes. Exact matches MUST be processed before wildcard matches, and wildcard matches MUST be processed before fallback (empty Hostname value) matches. For example, `"foo.example.com"` takes precedence over `"*.example.com"`, and `"*.example.com"` takes precedence over `""`. Additionally, if there are multiple wildcard entries, more specific wildcard entries must be processed before less specific wildcard entries. For example, `"*.foo.example.com"` takes precedence over `"*.example.com"`. The precise definition here is that the higher the number of dots in the hostname to the right of the wildcard character, the higher the precedence. The wildcard character will match any number of characters _and dots_ to the left, however, so `"*.example.com"` will match both `"foo.bar.example.com"` _and_ `"bar.example.com"`. ## Handling indistinct Listeners If a set of Listeners contains Listeners that are not distinct, then those Listeners are _Conflicted_, and the implementation MUST set the "Conflicted" condition in the Listener Status to "True". The words "indistinct" and "conflicted" are considered equivalent for the purpose of this documentation. Implementations MAY choose to accept a Gateway with some Conflicted Listeners only if they only accept the partial Listener set that contains no Conflicted Listeners. Specifically, an implementation MAY accept a partial Listener set subject to the following rules: * The implementation MUST NOT pick one conflicting Listener as the winner. ALL indistinct Listeners must not be accepted for processing. * At least one distinct Listener MUST be present, or else the Gateway effectively contains _no_ Listeners, and must be rejected from processing as a whole. The implementation MUST set a "ListenersNotValid" condition on the Gateway Status when the Gateway contains Conflicted Listeners whether or not they accept the Gateway. That Condition SHOULD clearly indicate in the Message which Listeners are conflicted, and which are Accepted. Additionally, the Listener status for those listeners SHOULD indicate which Listeners are conflicted and not Accepted. ## General Listener behavior Note that, for all distinct Listeners, requests SHOULD match at most one Listener. For example, if Listeners are defined for "foo.example.com" and "*.example.com", a request to "foo.example.com" SHOULD only be routed using routes attached to the "foo.example.com" Listener (and not the "*.example.com" Listener). This concept is known as "Listener Isolation", and it is an Extended feature of Gateway API. Implementations that do not support Listener Isolation MUST clearly document this, and MUST NOT claim support for the `GatewayHTTPListenerIsolation` feature. Implementations that _do_ support Listener Isolation SHOULD claim support for the Extended `GatewayHTTPListenerIsolation` feature and pass the associated conformance tests. ## Compatible Listeners A Gateway's Listeners are considered _compatible_ if: 1. They are distinct. 2. The implementation can serve them in compliance with the Addresses requirement that all Listeners are available on all assigned addresses. Compatible combinations in Extended support are expected to vary across implementations. A combination that is compatible for one implementation may not be compatible for another. For example, an implementation that cannot serve both TCP and UDP listeners on the same address, or cannot mix HTTPS and generic TLS listens on the same port would not consider those cases compatible, even though they are distinct. Implementations MAY merge separate Gateways onto a single set of Addresses if all Listeners across all Gateways are compatible. In a future release the MinItems=1 requirement MAY be dropped. Support: Core items: description: |- Listener embodies the concept of a logical endpoint where a Gateway accepts network connections. properties: allowedRoutes: default: namespaces: from: Same description: |- AllowedRoutes defines the types of routes that MAY be attached to a Listener and the trusted namespaces where those Route resources MAY be present. Although a client request may match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: * The most specific match as defined by the Route type. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of "2020-09-08 01:02:03" is given precedence over a Route with a creation timestamp of "2020-09-08 01:02:04". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. All valid rules within a Route attached to this Listener should be implemented. Invalid Route rules can be ignored (sometimes that will mean the full Route). If a Route rule transitions from valid to invalid, support for that Route rule should be dropped to ensure consistency. For example, even if a filter specified by a Route rule is invalid, the rest of the rules within that Route should still be supported. Support: Core properties: kinds: description: |- Kinds specifies the groups and kinds of Routes that are allowed to bind to this Gateway Listener. When unspecified or empty, the kinds of Routes selected are determined using the Listener protocol. A RouteGroupKind MUST correspond to kinds of Routes that are compatible with the application protocol specified in the Listener's Protocol field. If an implementation does not support or recognize this resource type, it MUST set the "ResolvedRefs" condition to False for this Listener with the "InvalidRouteKinds" reason. Support: Core items: description: RouteGroupKind indicates the group and kind of a Route resource. properties: group: default: gateway.networking.k8s.io description: Group is the group of the Route. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is the kind of the Route. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string required: - kind type: object maxItems: 8 type: array x-kubernetes-list-type: atomic namespaces: default: from: Same description: |- Namespaces indicates namespaces from which Routes may be attached to this Listener. This is restricted to the namespace of this Gateway by default. Support: Core properties: from: default: Same description: |- From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. Support: Core enum: - All - Selector - Same type: string selector: description: |- Selector must be specified when From is set to "Selector". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of "From". Support: Core properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic type: object type: object hostname: description: |- Hostname specifies the virtual hostname to match for protocol types that define this concept. When unspecified, all hostnames are matched. This field is ignored for protocols that don't require hostname based matching. Implementations MUST apply Hostname matching appropriately for each of the following protocols: * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. * HTTPS: The Listener Hostname SHOULD match both the SNI and Host header. Note that this does not require the SNI and Host header to be the same. The semantics of this are described in more detail below. To ensure security, Section 11.1 of RFC-6066 emphasizes that server implementations that rely on SNI hostname matching MUST also verify hostnames within the application protocol. Section 9.1.2 of RFC-7540 provides a mechanism for servers to reject the reuse of a connection by responding with the HTTP 421 Misdirected Request status code. This indicates that the origin server has rejected the request because it appears to have been misdirected. To detect misdirected requests, Gateways SHOULD match the authority of the requests with all the SNI hostname(s) configured across all the Gateway Listeners on the same port and protocol: * If another Listener has an exact match or more specific wildcard entry, the Gateway SHOULD return a 421. * If the current Listener (selected by SNI matching during ClientHello) does not match the Host: * If another Listener does match the Host, the Gateway SHOULD return a 421. * If no other Listener matches the Host, the Gateway MUST return a 404. For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, there MUST be an intersection between the values for a Route to be accepted. For more information, refer to the Route specific Hostnames documentation. Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. Support: Core maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string name: description: |- Name is the name of the Listener. This name MUST be unique within a Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string port: description: |- Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. Support: Core format: int32 maximum: 65535 minimum: 1 type: integer protocol: description: |- Protocol specifies the network protocol this listener expects to receive. Support: Core maxLength: 255 minLength: 1 pattern: ^[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ type: string tls: description: |- TLS is the TLS configuration for the Listener. This field is required if the Protocol field is "HTTPS" or "TLS". It is invalid to set this field if the Protocol field is "HTTP", "TCP", or "UDP". The association of SNIs to Certificate defined in ListenerTLSConfig is defined based on the Hostname field for this listener. The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. Support: Core properties: certificateRefs: description: |- CertificateRefs contains a series of references to Kubernetes objects that contains TLS certificates and private keys. These certificates are used to establish a TLS handshake for requests that match the hostname of the associated listener. A single CertificateRef to a Kubernetes Secret has "Core" support. Implementations MAY choose to support attaching multiple certificates to a Listener, but this behavior is implementation-specific. References to a resource in different namespace are invalid UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the "ResolvedRefs" condition MUST be set to False for this listener with the "RefNotPermitted" reason. This field is required to have at least one element when the mode is set to "Terminate" (default) and is optional otherwise. CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls Support: Implementation-specific (More than one reference or other resource types) items: description: |- SecretObjectReference identifies an API object including its namespace, defaulting to Secret. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Secret description: Kind is kind of the referent. For example "Secret". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - name type: object maxItems: 64 type: array x-kubernetes-list-type: atomic mode: default: Terminate description: |- Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificates to be specified in some way, such as populating the certificateRefs field. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. The certificateRefs field is ignored in this mode. Support: Core enum: - Terminate - Passthrough type: string options: additionalProperties: description: |- AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. maxLength: 4096 minLength: 0 type: string description: |- Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. Support: Implementation-specific maxProperties: 16 type: object type: object x-kubernetes-validations: - message: certificateRefs or options must be specified when mode is Terminate rule: 'self.mode == ''Terminate'' ? size(self.certificateRefs) > 0 || size(self.options) > 0 : true' required: - name - port - protocol type: object maxItems: 64 minItems: 1 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map x-kubernetes-validations: - message: tls must not be specified for protocols ['HTTP', 'TCP', 'UDP'] rule: 'self.all(l, l.protocol in [''HTTP'', ''TCP'', ''UDP''] ? !has(l.tls) : true)' - message: tls mode must be Terminate for protocol HTTPS rule: 'self.all(l, (l.protocol == ''HTTPS'' && has(l.tls)) ? (l.tls.mode == '''' || l.tls.mode == ''Terminate'') : true)' - message: tls mode must be set for protocol TLS rule: 'self.all(l, (l.protocol == ''TLS'' ? has(l.tls) && has(l.tls.mode) && l.tls.mode != '''' : true))' - message: hostname must not be specified for protocols ['TCP', 'UDP'] rule: 'self.all(l, l.protocol in [''TCP'', ''UDP''] ? (!has(l.hostname) || l.hostname == '''') : true)' - message: Listener name must be unique within the Gateway rule: self.all(l1, self.exists_one(l2, l1.name == l2.name)) - message: Combination of port, protocol and hostname must be unique for each listener rule: 'self.all(l1, self.exists_one(l2, l1.port == l2.port && l1.protocol == l2.protocol && (has(l1.hostname) && has(l2.hostname) ? l1.hostname == l2.hostname : !has(l1.hostname) && !has(l2.hostname))))' tls: description: |- TLS specifies frontend and backend tls configuration for entire gateway. Support: Extended properties: backend: description: |- Backend describes TLS configuration for gateway when connecting to backends. Note that this contains only details for the Gateway as a TLS client, and does _not_ imply behavior about how to choose which backend should get a TLS connection. That is determined by the presence of a BackendTLSPolicy. Support: Core properties: clientCertificateRef: description: |- ClientCertificateRef references an object that contains a client certificate and its associated private key. It can reference standard Kubernetes resources, i.e., Secret, or implementation-specific custom resources. A ClientCertificateRef is considered invalid if: * It refers to a resource that cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured (e.g., a Secret does not contain the keys named `tls.crt` and `tls.key`). In this case, the `ResolvedRefs` condition on the Gateway MUST be set to False with the Reason `InvalidClientCertificateRef` and the Message of the Condition MUST indicate why the reference is invalid. * It refers to a resource in another namespace UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the `ResolvedRefs` condition on the Gateway MUST be set to False with the Reason `RefNotPermitted`. Implementations MAY choose to perform further validation of the certificate content (e.g., checking expiry or enforcing specific formats). In such cases, an implementation-specific Reason and Message MUST be set. Support: Core - Reference to a Kubernetes TLS Secret (with the type `kubernetes.io/tls`). Support: Implementation-specific - Other resource kinds or Secrets with a different type (e.g., `Opaque`). properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Secret description: Kind is kind of the referent. For example "Secret". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - name type: object type: object frontend: description: |- Frontend describes TLS config when client connects to Gateway. Support: Core properties: default: description: |- Default specifies the default client certificate validation configuration for all Listeners handling HTTPS traffic, unless a per-port configuration is defined. support: Core properties: validation: description: |- Validation holds configuration information for validating the frontend (client). Setting this field will result in mutual authentication when connecting to the gateway. In browsers this may result in a dialog appearing that requests a user to specify the client certificate. The maximum depth of a certificate chain accepted in verification is Implementation specific. Support: Core properties: caCertificateRefs: description: |- CACertificateRefs contains one or more references to Kubernetes objects that contain a PEM-encoded TLS CA certificate bundle, which is used as a trust anchor to validate the certificates presented by the client. A CACertificateRef is invalid if: * It refers to a resource that cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured (e.g., a ConfigMap does not contain a key named `ca.crt`). In this case, the Reason on all matching HTTPS listeners must be set to `InvalidCACertificateRef` and the Message of the Condition must indicate which reference is invalid and why. * It refers to an unknown or unsupported kind of resource. In this case, the Reason on all matching HTTPS listeners must be set to `InvalidCACertificateKind` and the Message of the Condition must explain which kind of resource is unknown or unsupported. * It refers to a resource in another namespace UNLESS there is a ReferenceGrant in the target namespace that allows the CA certificate to be attached. If a ReferenceGrant does not allow this reference, the `ResolvedRefs` on all matching HTTPS listeners condition MUST be set with the Reason `RefNotPermitted`. Implementations MAY choose to perform further validation of the certificate content (e.g., checking expiry or enforcing specific formats). In such cases, an implementation-specific Reason and Message MUST be set. In all cases, the implementation MUST ensure that the `ResolvedRefs` condition is set to `status: False` on all targeted listeners (i.e., listeners serving HTTPS on a matching port). The condition MUST include a Reason and Message that indicate the cause of the error. If ALL CACertificateRefs are invalid, the implementation MUST also ensure the `Accepted` condition on the listener is set to `status: False`, with the Reason `NoValidCACertificate`. Implementations MAY choose to support attaching multiple CA certificates to a listener, but this behavior is implementation-specific. Support: Core - A single reference to a Kubernetes ConfigMap, with the CA certificate in a key named `ca.crt`. Support: Implementation-specific - More than one reference, other kinds of resources, or a single reference that includes multiple certificates. items: description: |- ObjectReference identifies an API object including its namespace. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When set to the empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "ConfigMap" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - group - kind - name type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-type: atomic mode: default: AllowValidOnly description: |- FrontendValidationMode defines the mode for validating the client certificate. There are two possible modes: - AllowValidOnly: In this mode, the gateway will accept connections only if the client presents a valid certificate. This certificate must successfully pass validation against the CA certificates specified in `CACertificateRefs`. - AllowInsecureFallback: In this mode, the gateway will accept connections even if the client certificate is not presented or fails verification. This approach delegates client authorization to the backend and introduce a significant security risk. It should be used in testing environments or on a temporary basis in non-testing environments. Defaults to AllowValidOnly. Support: Core enum: - AllowValidOnly - AllowInsecureFallback type: string required: - caCertificateRefs type: object type: object perPort: description: |- PerPort specifies tls configuration assigned per port. Per port configuration is optional. Once set this configuration overrides the default configuration for all Listeners handling HTTPS traffic that match this port. Each override port requires a unique TLS configuration. support: Core items: properties: port: description: |- The Port indicates the Port Number to which the TLS configuration will be applied. This configuration will be applied to all Listeners handling HTTPS traffic that match this port. Support: Core format: int32 maximum: 65535 minimum: 1 type: integer tls: description: |- TLS store the configuration that will be applied to all Listeners handling HTTPS traffic and matching given port. Support: Core properties: validation: description: |- Validation holds configuration information for validating the frontend (client). Setting this field will result in mutual authentication when connecting to the gateway. In browsers this may result in a dialog appearing that requests a user to specify the client certificate. The maximum depth of a certificate chain accepted in verification is Implementation specific. Support: Core properties: caCertificateRefs: description: |- CACertificateRefs contains one or more references to Kubernetes objects that contain a PEM-encoded TLS CA certificate bundle, which is used as a trust anchor to validate the certificates presented by the client. A CACertificateRef is invalid if: * It refers to a resource that cannot be resolved (e.g., the referenced resource does not exist) or is misconfigured (e.g., a ConfigMap does not contain a key named `ca.crt`). In this case, the Reason on all matching HTTPS listeners must be set to `InvalidCACertificateRef` and the Message of the Condition must indicate which reference is invalid and why. * It refers to an unknown or unsupported kind of resource. In this case, the Reason on all matching HTTPS listeners must be set to `InvalidCACertificateKind` and the Message of the Condition must explain which kind of resource is unknown or unsupported. * It refers to a resource in another namespace UNLESS there is a ReferenceGrant in the target namespace that allows the CA certificate to be attached. If a ReferenceGrant does not allow this reference, the `ResolvedRefs` on all matching HTTPS listeners condition MUST be set with the Reason `RefNotPermitted`. Implementations MAY choose to perform further validation of the certificate content (e.g., checking expiry or enforcing specific formats). In such cases, an implementation-specific Reason and Message MUST be set. In all cases, the implementation MUST ensure that the `ResolvedRefs` condition is set to `status: False` on all targeted listeners (i.e., listeners serving HTTPS on a matching port). The condition MUST include a Reason and Message that indicate the cause of the error. If ALL CACertificateRefs are invalid, the implementation MUST also ensure the `Accepted` condition on the listener is set to `status: False`, with the Reason `NoValidCACertificate`. Implementations MAY choose to support attaching multiple CA certificates to a listener, but this behavior is implementation-specific. Support: Core - A single reference to a Kubernetes ConfigMap, with the CA certificate in a key named `ca.crt`. Support: Implementation-specific - More than one reference, other kinds of resources, or a single reference that includes multiple certificates. items: description: |- ObjectReference identifies an API object including its namespace. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When set to the empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "ConfigMap" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - group - kind - name type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-type: atomic mode: default: AllowValidOnly description: |- FrontendValidationMode defines the mode for validating the client certificate. There are two possible modes: - AllowValidOnly: In this mode, the gateway will accept connections only if the client presents a valid certificate. This certificate must successfully pass validation against the CA certificates specified in `CACertificateRefs`. - AllowInsecureFallback: In this mode, the gateway will accept connections even if the client certificate is not presented or fails verification. This approach delegates client authorization to the backend and introduce a significant security risk. It should be used in testing environments or on a temporary basis in non-testing environments. Defaults to AllowValidOnly. Support: Core enum: - AllowValidOnly - AllowInsecureFallback type: string required: - caCertificateRefs type: object type: object required: - port - tls type: object maxItems: 64 type: array x-kubernetes-list-map-keys: - port x-kubernetes-list-type: map x-kubernetes-validations: - message: Port for TLS configuration must be unique within the Gateway rule: self.all(t1, self.exists_one(t2, t1.port == t2.port)) required: - default type: object type: object required: - gatewayClassName - listeners type: object status: default: conditions: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Programmed description: Status defines the current state of Gateway. properties: addresses: description: |- Addresses lists the network addresses that have been bound to the Gateway. This list may differ from the addresses provided in the spec under some conditions: * no addresses are specified, all addresses are dynamically assigned * a combination of specified and dynamic addresses are assigned * a specified address was unusable (e.g. already in use) items: description: GatewayStatusAddress describes a network address that is bound to a Gateway. oneOf: - properties: type: enum: - IPAddress value: anyOf: - format: ipv4 - format: ipv6 - properties: type: not: enum: - IPAddress properties: type: default: IPAddress description: Type of the address. maxLength: 253 minLength: 1 pattern: ^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string value: description: |- Value of the address. The validity of the values will depend on the type and support by the controller. Examples: `1.2.3.4`, `128::1`, `my-ip-address`. maxLength: 253 minLength: 1 type: string required: - value type: object x-kubernetes-validations: - message: Hostname value must only contain valid characters (matching ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$) rule: 'self.type == ''Hostname'' ? self.value.matches(r"""^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"""): true' maxItems: 16 type: array x-kubernetes-list-type: atomic attachedListenerSets: description: |- AttachedListenerSets represents the total number of ListenerSets that have been successfully attached to this Gateway. A ListenerSet is successfully attached to a Gateway when all the following conditions are met: - The ListenerSet is selected by the Gateway's AllowedListeners field - The ListenerSet has a valid ParentRef selecting the Gateway - The ListenerSet's status has the condition "Accepted: true" Uses for this field include troubleshooting AttachedListenerSets attachment and measuring blast radius/impact of changes to a Gateway. format: int32 type: integer conditions: default: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Programmed description: |- Conditions describe the current conditions of the Gateway. Implementations should prefer to express Gateway conditions using the `GatewayConditionType` and `GatewayConditionReason` constants so that operators and tools can converge on a common vocabulary to describe Gateway state. Known condition types are: * "Accepted" * "Programmed" * "Ready" items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map listeners: description: Listeners provide status for each unique listener port defined in the Spec. items: description: ListenerStatus is the status associated with a Listener. properties: attachedRoutes: description: |- AttachedRoutes represents the total number of Routes that have been successfully attached to this Listener. Successful attachment of a Route to a Listener is based solely on the combination of the AllowedRoutes field on the corresponding Listener and the Route's ParentRefs field. A Route is successfully attached to a Listener when it is selected by the Listener's AllowedRoutes field AND the Route has a valid ParentRef selecting the whole Gateway resource or a specific Listener as a parent resource (more detail on attachment semantics can be found in the documentation on the various Route kinds ParentRefs fields). Listener or Route status does not impact successful attachment, i.e. the AttachedRoutes field count MUST be set for Listeners, even if the Accepted condition of an individual Listener is set to "False". The AttachedRoutes number represents the number of Routes with the Accepted condition set to "True" that have been attached to this Listener. Routes with any other value for the Accepted condition MUST NOT be included in this count. Uses for this field include troubleshooting Route attachment and measuring blast radius/impact of changes to a Listener. format: int32 type: integer conditions: description: Conditions describe the current condition of this listener. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map name: description: Name is the name of the Listener that this status corresponds to. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string supportedKinds: description: |- SupportedKinds is the list indicating the Kinds supported by this listener. This MUST represent the kinds supported by an implementation for that Listener configuration. If kinds are specified in Spec that are not supported, they MUST NOT appear in this list and an implementation MUST set the "ResolvedRefs" condition to "False" with the "InvalidRouteKinds" reason. If both valid and invalid Route kinds are specified, the implementation MUST reference the valid Route kinds that have been specified. items: description: RouteGroupKind indicates the group and kind of a Route resource. properties: group: default: gateway.networking.k8s.io description: Group is the group of the Route. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is the kind of the Route. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string required: - kind type: object maxItems: 8 type: array x-kubernetes-list-type: atomic required: - attachedRoutes - conditions - name type: object maxItems: 64 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object required: - spec type: object served: true storage: false subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/standard/gateway.networking.k8s.io_grpcroutes.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: standard name: grpcroutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: GRPCRoute listKind: GRPCRouteList plural: grpcroutes singular: grpcroute scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .spec.hostnames name: Hostnames type: string - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1 schema: openAPIV3Schema: description: |- GRPCRoute provides a way to route gRPC requests. This includes the capability to match requests by hostname, gRPC service, gRPC method, or HTTP/2 header. Filters can be used to specify additional processing steps. Backends specify where matching requests will be routed. GRPCRoute falls under extended support within the Gateway API. Within the following specification, the word "MUST" indicates that an implementation supporting GRPCRoute must conform to the indicated requirement, but an implementation not supporting this route type need not follow the requirement unless explicitly indicated. Implementations supporting `GRPCRoute` with the `HTTPS` `ProtocolType` MUST accept HTTP/2 connections without an initial upgrade from HTTP/1.1, i.e. via ALPN. If the implementation does not support this, then it MUST set the "Accepted" condition to "False" for the affected listener with a reason of "UnsupportedProtocol". Implementations MAY also accept HTTP/2 connections with an upgrade from HTTP/1. Implementations supporting `GRPCRoute` with the `HTTP` `ProtocolType` MUST support HTTP/2 over cleartext TCP (h2c, https://www.rfc-editor.org/rfc/rfc7540#section-3.1) without an initial upgrade from HTTP/1.1, i.e. with prior knowledge (https://www.rfc-editor.org/rfc/rfc7540#section-3.4). If the implementation does not support this, then it MUST set the "Accepted" condition to "False" for the affected listener with a reason of "UnsupportedProtocol". Implementations MAY also accept HTTP/2 connections with an upgrade from HTTP/1, i.e. without prior knowledge. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of GRPCRoute. properties: hostnames: description: |- Hostnames defines a set of hostnames to match against the GRPC Host header to select a GRPCRoute to process the request. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label MUST appear by itself as the first label. If a hostname is specified by both the Listener and GRPCRoute, there MUST be at least one intersecting hostname for the GRPCRoute to be attached to the Listener. For example: * A Listener with `test.example.com` as the hostname matches GRPCRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches GRPCRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `test.example.com` and `*.example.com` would both match. On the other hand, `example.com` and `test.example.net` would not match. Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. If both the Listener and GRPCRoute have specified hostnames, any GRPCRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the GRPCRoute specified `test.example.com` and `test.example.net`, `test.example.net` MUST NOT be considered for a match. If both the Listener and GRPCRoute have specified hostnames, and none match with the criteria above, then the GRPCRoute MUST NOT be accepted by the implementation. The implementation MUST raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. If a Route (A) of type HTTPRoute or GRPCRoute is attached to a Listener and that listener already has another Route (B) of the other type attached and the intersection of the hostnames of A and B is non-empty, then the implementation MUST accept exactly one of these two routes, determined by the following criteria, in order: * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by "{namespace}/{name}". The rejected Route MUST raise an 'Accepted' condition with a status of 'False' in the corresponding RouteParentStatus. Support: Core items: description: |- Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. Hostname can be "precise" which is a domain name without the terminating dot of a network host (e.g. "foo.example.com") or "wildcard", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed. maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string maxItems: 16 type: array x-kubernetes-list-type: atomic parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. For Services, that means the Service must either be in the same namespace for a "producer" route, or the mesh implementation must support and allow "consumer" routes for the referenced Service. ReferenceGrant is not applicable for governing ParentRefs to Services - it is not possible to create a "producer" route for a Service in a different namespace from the Route. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. ParentRefs must be _distinct_. This means either that: * They select different objects. If this is the case, then parentRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, `namespace`, and `name` must be unique across all parentRef entries in the Route. * They do not select different objects, but for each optional field used, each ParentRef that selects the same object must set the same set of optional fields to different values. If one ParentRef sets a combination of optional fields, all must set the same combination. Some examples: * If one ParentRef sets `sectionName`, all ParentRefs referencing the same object must also set `sectionName`. * If one ParentRef sets `port`, all ParentRefs referencing the same object must also set `port`. * If one ParentRef sets `sectionName` and `port`, all ParentRefs referencing the same object must also set `sectionName` and `port`. It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable other kinds of cross-namespace reference. items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object maxItems: 32 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''')) : true))' - message: sectionName must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)))) rules: description: Rules are a list of GRPC matchers, filters and actions. items: description: |- GRPCRouteRule defines the semantics for matching a gRPC request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be sent. Failure behavior here depends on how many BackendRefs are specified and how many are invalid. If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive an `UNAVAILABLE` status. See the GRPCBackendRef definition for the rules about what makes a single GRPCBackendRef invalid. When a GRPCBackendRef is invalid, `UNAVAILABLE` statuses MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive an `UNAVAILABLE` status. For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic MUST receive an `UNAVAILABLE` status. Implementations may choose how that 50 percent is determined. Support: Core for Kubernetes Service Support: Implementation-specific for any other resource Support for weight: Core items: description: |- GRPCBackendRef defines how a GRPCRoute forwards a gRPC request. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. properties: filters: description: |- Filters defined at this level MUST be executed if and only if the request is being forwarded to the backend defined here. Support: Implementation-specific (For broader support of filters, use the Filters field in GRPCRouteRule.) items: description: |- GRPCRouteFilter defines processing steps that must be completed during the request or response lifecycle. GRPCRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the "filter" behavior. For example, resource "myroutefilter" in group "networking.example.net"). ExtensionRef MUST NOT be used for core and extended filters. Support: Implementation-specific This filter can be used multiple times within the same rule. properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "HTTPRoute" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request headers. Support: Core properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object requestMirror: description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. This filter can be used multiple times within the same rule. Note that not all implementations will be able to support mirroring to multiple backends. Support: Extended properties: backendRef: description: |- BackendRef references a resource where mirrored requests are sent. Mirrored requests must be sent only to a single destination endpoint within this BackendRef, irrespective of how many endpoints are present within this BackendRef. If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the "ResolvedRefs" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the "ResolvedRefs" condition on the Route is set to `status: False`, with the "RefNotPermitted" reason and not configure this backend in the underlying implementation. In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. Support: Extended for Kubernetes Service Support: Implementation-specific for any other resource properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: description: |- Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. properties: denominator: default: 100 format: int32 minimum: 1 type: integer numerator: format: int32 minimum: 0 type: integer required: - numerator type: object x-kubernetes-validations: - message: numerator must be less than or equal to denominator rule: self.numerator <= self.denominator percent: description: |- Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. format: int32 maximum: 100 minimum: 0 type: integer required: - backendRef type: object x-kubernetes-validations: - message: Only one of percent or fraction may be specified in HTTPRequestMirrorFilter rule: '!(has(self.percent) && has(self.fraction))' responseHeaderModifier: description: |- ResponseHeaderModifier defines a schema for a filter that modifies response headers. Support: Extended properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object type: description: |- Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: - Core: Filter types and their corresponding configuration defined by "Support: Core" in this package, e.g. "RequestHeaderModifier". All implementations supporting GRPCRoute MUST support core filters. - Extended: Filter types and their corresponding configuration defined by "Support: Extended" in this package, e.g. "RequestMirror". Implementers are encouraged to support extended filters. - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` MUST be set to "ExtensionRef" for custom filters. Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. enum: - ResponseHeaderModifier - RequestHeaderModifier - RequestMirror - ExtensionRef type: string required: - type type: object x-kubernetes-validations: - message: filter.requestHeaderModifier must be nil if the filter.type is not RequestHeaderModifier rule: '!(has(self.requestHeaderModifier) && self.type != ''RequestHeaderModifier'')' - message: filter.requestHeaderModifier must be specified for RequestHeaderModifier filter.type rule: '!(!has(self.requestHeaderModifier) && self.type == ''RequestHeaderModifier'')' - message: filter.responseHeaderModifier must be nil if the filter.type is not ResponseHeaderModifier rule: '!(has(self.responseHeaderModifier) && self.type != ''ResponseHeaderModifier'')' - message: filter.responseHeaderModifier must be specified for ResponseHeaderModifier filter.type rule: '!(!has(self.responseHeaderModifier) && self.type == ''ResponseHeaderModifier'')' - message: filter.requestMirror must be nil if the filter.type is not RequestMirror rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' - message: filter.requestMirror must be specified for RequestMirror filter.type rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' - message: filter.extensionRef must be nil if the filter.type is not ExtensionRef rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: RequestHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1 - message: ResponseHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() <= 1 group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer weight: default: 1 description: |- Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. Support for this field varies based on the context where used. format: int32 maximum: 1000000 minimum: 0 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' maxItems: 16 type: array x-kubernetes-list-type: atomic filters: description: |- Filters define the filters that are applied to requests that match this rule. The effects of ordering of multiple behaviors are currently unspecified. This can change in the future based on feedback during the alpha stage. Conformance-levels at this level are defined based on the type of filter: - ALL core filters MUST be supported by all implementations that support GRPCRoute. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. Specifying the same filter multiple times is not supported unless explicitly indicated in the filter. If an implementation cannot support a combination of filters, it must clearly document that limitation. In cases where incompatible or unsupported filters are specified and cause the `Accepted` condition to be set to status `False`, implementations may use the `IncompatibleFilters` reason to specify this configuration error. Support: Core items: description: |- GRPCRouteFilter defines processing steps that must be completed during the request or response lifecycle. GRPCRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the "filter" behavior. For example, resource "myroutefilter" in group "networking.example.net"). ExtensionRef MUST NOT be used for core and extended filters. Support: Implementation-specific This filter can be used multiple times within the same rule. properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "HTTPRoute" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request headers. Support: Core properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object requestMirror: description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. This filter can be used multiple times within the same rule. Note that not all implementations will be able to support mirroring to multiple backends. Support: Extended properties: backendRef: description: |- BackendRef references a resource where mirrored requests are sent. Mirrored requests must be sent only to a single destination endpoint within this BackendRef, irrespective of how many endpoints are present within this BackendRef. If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the "ResolvedRefs" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the "ResolvedRefs" condition on the Route is set to `status: False`, with the "RefNotPermitted" reason and not configure this backend in the underlying implementation. In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. Support: Extended for Kubernetes Service Support: Implementation-specific for any other resource properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: description: |- Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. properties: denominator: default: 100 format: int32 minimum: 1 type: integer numerator: format: int32 minimum: 0 type: integer required: - numerator type: object x-kubernetes-validations: - message: numerator must be less than or equal to denominator rule: self.numerator <= self.denominator percent: description: |- Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. format: int32 maximum: 100 minimum: 0 type: integer required: - backendRef type: object x-kubernetes-validations: - message: Only one of percent or fraction may be specified in HTTPRequestMirrorFilter rule: '!(has(self.percent) && has(self.fraction))' responseHeaderModifier: description: |- ResponseHeaderModifier defines a schema for a filter that modifies response headers. Support: Extended properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object type: description: |- Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: - Core: Filter types and their corresponding configuration defined by "Support: Core" in this package, e.g. "RequestHeaderModifier". All implementations supporting GRPCRoute MUST support core filters. - Extended: Filter types and their corresponding configuration defined by "Support: Extended" in this package, e.g. "RequestMirror". Implementers are encouraged to support extended filters. - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` MUST be set to "ExtensionRef" for custom filters. Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. enum: - ResponseHeaderModifier - RequestHeaderModifier - RequestMirror - ExtensionRef type: string required: - type type: object x-kubernetes-validations: - message: filter.requestHeaderModifier must be nil if the filter.type is not RequestHeaderModifier rule: '!(has(self.requestHeaderModifier) && self.type != ''RequestHeaderModifier'')' - message: filter.requestHeaderModifier must be specified for RequestHeaderModifier filter.type rule: '!(!has(self.requestHeaderModifier) && self.type == ''RequestHeaderModifier'')' - message: filter.responseHeaderModifier must be nil if the filter.type is not ResponseHeaderModifier rule: '!(has(self.responseHeaderModifier) && self.type != ''ResponseHeaderModifier'')' - message: filter.responseHeaderModifier must be specified for ResponseHeaderModifier filter.type rule: '!(!has(self.responseHeaderModifier) && self.type == ''ResponseHeaderModifier'')' - message: filter.requestMirror must be nil if the filter.type is not RequestMirror rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' - message: filter.requestMirror must be specified for RequestMirror filter.type rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' - message: filter.extensionRef must be nil if the filter.type is not ExtensionRef rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: RequestHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1 - message: ResponseHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() <= 1 matches: description: |- Matches define conditions used for matching the rule against incoming gRPC requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. For example, take the following matches configuration: ``` matches: - method: service: foo.bar headers: values: version: 2 - method: service: foo.bar.v2 ``` For a request to match against this rule, it MUST satisfy EITHER of the two conditions: - service of foo.bar AND contains the header `version: 2` - service of foo.bar.v2 See the documentation for GRPCRouteMatch on how to specify multiple match conditions to be ANDed together. If no matches are specified, the implementation MUST match every gRPC request. Proxy or Load Balancer routing configuration generated from GRPCRoutes MUST prioritize rules based on the following criteria, continuing on ties. Merging MUST not be done between GRPCRoutes and HTTPRoutes. Precedence MUST be given to the rule with the largest number of: * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. * Characters in a matching service. * Characters in a matching method. * Header matches. If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by "{namespace}/{name}". If ties still exist within the Route that has been given precedence, matching precedence MUST be granted to the first matching rule meeting the above criteria. items: description: |- GRPCRouteMatch defines the predicate used to match requests to a given action. Multiple match types are ANDed together, i.e. the match will evaluate to true only if all conditions are satisfied. For example, the match below will match a gRPC request only if its service is `foo` AND it contains the `version: v1` header: ``` matches: - method: type: Exact service: "foo" - headers: name: "version" value "v1" ``` properties: headers: description: |- Headers specifies gRPC request header matchers. Multiple match values are ANDed together, meaning, a request MUST match all the specified headers to select the route. items: description: |- GRPCHeaderMatch describes how to select a gRPC route by matching gRPC request headers. properties: name: description: |- Name is the name of the gRPC Header to be matched. If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string type: default: Exact description: Type specifies how to match against the value of the header. enum: - Exact - RegularExpression type: string value: description: Value is the value of the gRPC Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map method: description: |- Method specifies a gRPC request service/method matcher. If this field is not specified, all services and methods will match. properties: method: description: |- Value of the method to match against. If left empty or omitted, will match all services. At least one of Service and Method MUST be a non-empty string. maxLength: 1024 type: string service: description: |- Value of the service to match against. If left empty or omitted, will match any service. At least one of Service and Method MUST be a non-empty string. maxLength: 1024 type: string type: default: Exact description: |- Type specifies how to match against the service and/or method. Support: Core (Exact with service and method specified) Support: Implementation-specific (Exact with method specified but no service specified) Support: Implementation-specific (RegularExpression) enum: - Exact - RegularExpression type: string type: object x-kubernetes-validations: - message: One or both of 'service' or 'method' must be specified rule: 'has(self.type) ? has(self.service) || has(self.method) : true' - message: service must only contain valid characters (matching ^(?i)\.?[a-z_][a-z_0-9]*(\.[a-z_][a-z_0-9]*)*$) rule: '(!has(self.type) || self.type == ''Exact'') && has(self.service) ? self.service.matches(r"""^(?i)\.?[a-z_][a-z_0-9]*(\.[a-z_][a-z_0-9]*)*$"""): true' - message: method must only contain valid characters (matching ^[A-Za-z_][A-Za-z_0-9]*$) rule: '(!has(self.type) || self.type == ''Exact'') && has(self.method) ? self.method.matches(r"""^[A-Za-z_][A-Za-z_0-9]*$"""): true' type: object maxItems: 64 type: array x-kubernetes-list-type: atomic name: description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. Support: Extended maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string type: object maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: While 16 rules and 64 matches per rule are allowed, the total number of matches across all rules in a route must be less than 128 rule: '(self.size() > 0 ? (has(self[0].matches) ? self[0].matches.size() : 0) : 0) + (self.size() > 1 ? (has(self[1].matches) ? self[1].matches.size() : 0) : 0) + (self.size() > 2 ? (has(self[2].matches) ? self[2].matches.size() : 0) : 0) + (self.size() > 3 ? (has(self[3].matches) ? self[3].matches.size() : 0) : 0) + (self.size() > 4 ? (has(self[4].matches) ? self[4].matches.size() : 0) : 0) + (self.size() > 5 ? (has(self[5].matches) ? self[5].matches.size() : 0) : 0) + (self.size() > 6 ? (has(self[6].matches) ? self[6].matches.size() : 0) : 0) + (self.size() > 7 ? (has(self[7].matches) ? self[7].matches.size() : 0) : 0) + (self.size() > 8 ? (has(self[8].matches) ? self[8].matches.size() : 0) : 0) + (self.size() > 9 ? (has(self[9].matches) ? self[9].matches.size() : 0) : 0) + (self.size() > 10 ? (has(self[10].matches) ? self[10].matches.size() : 0) : 0) + (self.size() > 11 ? (has(self[11].matches) ? self[11].matches.size() : 0) : 0) + (self.size() > 12 ? (has(self[12].matches) ? self[12].matches.size() : 0) : 0) + (self.size() > 13 ? (has(self[13].matches) ? self[13].matches.size() : 0) : 0) + (self.size() > 14 ? (has(self[14].matches) ? self[14].matches.size() : 0) : 0) + (self.size() > 15 ? (has(self[15].matches) ? self[15].matches.size() : 0) : 0) <= 128' type: object status: description: Status defines the current state of GRPCRoute. properties: parents: description: |- Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway. items: description: |- RouteParentStatus describes the status of a route with respect to an associated Parent. properties: conditions: description: |- Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the "Accepted" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. A Route MUST be considered "Accepted" if at least one of the Route's rules is implemented by the Gateway. There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: * The Route refers to a nonexistent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace to which the controller does not have access. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string parentRef: description: |- ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object required: - conditions - controllerName - parentRef type: object maxItems: 32 type: array x-kubernetes-list-type: atomic required: - parents type: object required: - spec type: object served: true storage: true subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/standard/gateway.networking.k8s.io_httproutes.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: standard name: httproutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: HTTPRoute listKind: HTTPRouteList plural: httproutes singular: httproute scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .spec.hostnames name: Hostnames type: string - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1 schema: openAPIV3Schema: description: |- HTTPRoute provides a way to route HTTP requests. This includes the capability to match requests by hostname, path, header, or query param. Filters can be used to specify additional processing steps. Backends specify where matching requests should be routed. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of HTTPRoute. properties: hostnames: description: |- Hostnames defines a set of hostnames that should match against the HTTP Host header to select a HTTPRoute used to process the request. Implementations MUST ignore any port value specified in the HTTP Host header while performing a match and (absent of any applicable header modification configuration) MUST forward this header unmodified to the backend. Valid values for Hostnames are determined by RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. If a hostname is specified by both the Listener and HTTPRoute, there must be at least one intersecting hostname for the HTTPRoute to be attached to the Listener. For example: * A Listener with `test.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `*.example.com`, `test.example.com`, and `foo.test.example.com` would all match. On the other hand, `example.com` and `test.example.net` would not match. Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. If both the Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the HTTPRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. If both the Listener and HTTPRoute have specified hostnames, and none match with the criteria above, then the HTTPRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. overlapping wildcard matching and exact matching hostnames), precedence must be given to rules from the HTTPRoute with the largest number of: * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. Support: Core items: description: |- Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. Hostname can be "precise" which is a domain name without the terminating dot of a network host (e.g. "foo.example.com") or "wildcard", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed. maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string maxItems: 16 type: array x-kubernetes-list-type: atomic parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. For Services, that means the Service must either be in the same namespace for a "producer" route, or the mesh implementation must support and allow "consumer" routes for the referenced Service. ReferenceGrant is not applicable for governing ParentRefs to Services - it is not possible to create a "producer" route for a Service in a different namespace from the Route. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. ParentRefs must be _distinct_. This means either that: * They select different objects. If this is the case, then parentRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, `namespace`, and `name` must be unique across all parentRef entries in the Route. * They do not select different objects, but for each optional field used, each ParentRef that selects the same object must set the same set of optional fields to different values. If one ParentRef sets a combination of optional fields, all must set the same combination. Some examples: * If one ParentRef sets `sectionName`, all ParentRefs referencing the same object must also set `sectionName`. * If one ParentRef sets `port`, all ParentRefs referencing the same object must also set `port`. * If one ParentRef sets `sectionName` and `port`, all ParentRefs referencing the same object must also set `sectionName` and `port`. It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable other kinds of cross-namespace reference. items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object maxItems: 32 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''')) : true))' - message: sectionName must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)))) rules: default: - matches: - path: type: PathPrefix value: / description: Rules are a list of HTTP matchers, filters and actions. items: description: |- HTTPRouteRule defines semantics for matching an HTTP request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be sent. Failure behavior here depends on how many BackendRefs are specified and how many are invalid. If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive a 500 status code. See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. When a HTTPBackendRef is invalid, 500 status codes MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive a 500 status code. For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic must receive a 500. Implementations may choose how that 50 percent is determined. When a HTTPBackendRef refers to a Service that has no ready endpoints, implementations SHOULD return a 503 for requests to that backend instead. If an implementation chooses to do this, all of the above rules for 500 responses MUST also apply for responses that return a 503. Support: Core for Kubernetes Service Support: Extended for Kubernetes ServiceImport Support: Implementation-specific for any other resource Support for weight: Core items: description: |- HTTPBackendRef defines how a HTTPRoute forwards a HTTP request. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. properties: filters: description: |- Filters defined at this level should be executed if and only if the request is being forwarded to the backend defined here. Support: Implementation-specific (For broader support of filters, use the Filters field in HTTPRouteRule.) items: description: |- HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: cors: description: |- CORS defines a schema for a filter that responds to the cross-origin request based on HTTP response header. Support: Extended properties: allowCredentials: description: |- AllowCredentials indicates whether the actual cross-origin request allows to include credentials. When set to true, the gateway will include the `Access-Control-Allow-Credentials` response header with value true (case-sensitive). When set to false or omitted the gateway will omit the header `Access-Control-Allow-Credentials` entirely (this is the standard CORS behavior). Support: Extended type: boolean allowHeaders: description: |- AllowHeaders indicates which HTTP request headers are supported for accessing the requested resource. Header names are not case-sensitive. Multiple header names in the value of the `Access-Control-Allow-Headers` response header are separated by a comma (","). When the `AllowHeaders` field is configured with one or more headers, the gateway must return the `Access-Control-Allow-Headers` response header which value is present in the `AllowHeaders` field. If any header name in the `Access-Control-Request-Headers` request header is not included in the list of header names specified by the response header `Access-Control-Allow-Headers`, it will present an error on the client side. If any header name in the `Access-Control-Allow-Headers` response header does not recognize by the client, it will also occur an error on the client side. A wildcard indicates that the requests with all HTTP headers are allowed. If config contains the wildcard "*" in allowHeaders and the request is not credentialed, the `Access-Control-Allow-Headers` response header can either use the `*` wildcard or the value of Access-Control-Request-Headers from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Headers` response header. When also the `AllowCredentials` field is true and `AllowHeaders` field is specified with the `*` wildcard, the gateway must specify one or more HTTP headers in the value of the `Access-Control-Allow-Headers` response header. The value of the header `Access-Control-Allow-Headers` is same as the `Access-Control-Request-Headers` header provided by the client. If the header `Access-Control-Request-Headers` is not included in the request, the gateway will omit the `Access-Control-Allow-Headers` response header, instead of specifying the `*` wildcard. Support: Extended items: description: |- HTTPHeaderName is the name of an HTTP header. Valid values include: * "Authorization" * "Set-Cookie" Invalid values include: - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo headers are not currently supported by this type. - "/invalid" - "/ " is an invalid character maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string maxItems: 64 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowHeaders cannot contain '*' alongside other methods rule: '!(''*'' in self && self.size() > 1)' allowMethods: description: |- AllowMethods indicates which HTTP methods are supported for accessing the requested resource. Valid values are any method defined by RFC9110, along with the special value `*`, which represents all HTTP methods are allowed. Method names are case-sensitive, so these values are also case-sensitive. (See https://www.rfc-editor.org/rfc/rfc2616#section-5.1.1) Multiple method names in the value of the `Access-Control-Allow-Methods` response header are separated by a comma (","). A CORS-safelisted method is a method that is `GET`, `HEAD`, or `POST`. (See https://fetch.spec.whatwg.org/#cors-safelisted-method) The CORS-safelisted methods are always allowed, regardless of whether they are specified in the `AllowMethods` field. When the `AllowMethods` field is configured with one or more methods, the gateway must return the `Access-Control-Allow-Methods` response header which value is present in the `AllowMethods` field. If the HTTP method of the `Access-Control-Request-Method` request header is not included in the list of methods specified by the response header `Access-Control-Allow-Methods`, it will present an error on the client side. If config contains the wildcard "*" in allowMethods and the request is not credentialed, the `Access-Control-Allow-Methods` response header can either use the `*` wildcard or the value of Access-Control-Request-Method from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Methods` response header. When also the `AllowCredentials` field is true and `AllowMethods` field specified with the `*` wildcard, the gateway must specify one HTTP method in the value of the Access-Control-Allow-Methods response header. The value of the header `Access-Control-Allow-Methods` is same as the `Access-Control-Request-Method` header provided by the client. If the header `Access-Control-Request-Method` is not included in the request, the gateway will omit the `Access-Control-Allow-Methods` response header, instead of specifying the `*` wildcard. Support: Extended items: enum: - GET - HEAD - POST - PUT - DELETE - CONNECT - OPTIONS - TRACE - PATCH - '*' type: string maxItems: 9 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowMethods cannot contain '*' alongside other methods rule: '!(''*'' in self && self.size() > 1)' allowOrigins: description: |- AllowOrigins indicates whether the response can be shared with requested resource from the given `Origin`. The `Origin` consists of a scheme and a host, with an optional port, and takes the form `://(:)`. Valid values for scheme are: `http` and `https`. Valid values for port are any integer between 1 and 65535 (the list of available TCP/UDP ports). Note that, if not included, port `80` is assumed for `http` scheme origins, and port `443` is assumed for `https` origins. This may affect origin matching. The host part of the origin may contain the wildcard character `*`. These wildcard characters behave as follows: * `*` is a greedy match to the _left_, including any number of DNS labels to the left of its position. This also means that `*` will include any number of period `.` characters to the left of its position. * A wildcard by itself matches all hosts. An origin value that includes _only_ the `*` character indicates requests from all `Origin`s are allowed. When the `AllowOrigins` field is configured with multiple origins, it means the server supports clients from multiple origins. If the request `Origin` matches the configured allowed origins, the gateway must return the given `Origin` and sets value of the header `Access-Control-Allow-Origin` same as the `Origin` header provided by the client. The status code of a successful response to a "preflight" request is always an OK status (i.e., 204 or 200). If the request `Origin` does not match the configured allowed origins, the gateway returns 204/200 response but doesn't set the relevant cross-origin response headers. Alternatively, the gateway responds with 403 status to the "preflight" request is denied, coupled with omitting the CORS headers. The cross-origin request fails on the client side. Therefore, the client doesn't attempt the actual cross-origin request. Conversely, if the request `Origin` matches one of the configured allowed origins, the gateway sets the response header `Access-Control-Allow-Origin` to the same value as the `Origin` header provided by the client. When config has the wildcard ("*") in allowOrigins, and the request is not credentialed (e.g., it is a preflight request), the `Access-Control-Allow-Origin` response header either contains the wildcard as well or the Origin from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Origin` response header. When also the `AllowCredentials` field is true and `AllowOrigins` field specified with the `*` wildcard, the gateway must return a single origin in the value of the `Access-Control-Allow-Origin` response header, instead of specifying the `*` wildcard. The value of the header `Access-Control-Allow-Origin` is same as the `Origin` header provided by the client. Support: Extended items: description: |- The CORSOrigin MUST NOT be a relative URI, and it MUST follow the URI syntax and encoding rules specified in RFC3986. The CORSOrigin MUST include both a scheme (e.g., "http" or "spiffe") and a scheme-specific-part, or it should be a single '*' character. URIs that include an authority MUST include a fully qualified domain name or IP address as the host. maxLength: 253 minLength: 1 pattern: (^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$) type: string maxItems: 64 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowOrigins cannot contain '*' alongside other origins rule: '!(''*'' in self && self.size() > 1)' exposeHeaders: description: |- ExposeHeaders indicates which HTTP response headers can be exposed to client-side scripts in response to a cross-origin request. A CORS-safelisted response header is an HTTP header in a CORS response that it is considered safe to expose to the client scripts. The CORS-safelisted response headers include the following headers: `Cache-Control` `Content-Language` `Content-Length` `Content-Type` `Expires` `Last-Modified` `Pragma` (See https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name) The CORS-safelisted response headers are exposed to client by default. When an HTTP header name is specified using the `ExposeHeaders` field, this additional header will be exposed as part of the response to the client. Header names are not case-sensitive. Multiple header names in the value of the `Access-Control-Expose-Headers` response header are separated by a comma (","). A wildcard indicates that the responses with all HTTP headers are exposed to clients. The `Access-Control-Expose-Headers` response header can only use `*` wildcard as value when the request is not credentialed. When the `exposeHeaders` config field contains the "*" wildcard and the request is credentialed, the gateway cannot use the `*` wildcard in the `Access-Control-Expose-Headers` response header. Support: Extended items: description: |- HTTPHeaderName is the name of an HTTP header. Valid values include: * "Authorization" * "Set-Cookie" Invalid values include: - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo headers are not currently supported by this type. - "/invalid" - "/ " is an invalid character maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string maxItems: 64 type: array x-kubernetes-list-type: set maxAge: default: 5 description: |- MaxAge indicates the duration (in seconds) for the client to cache the results of a "preflight" request. The information provided by the `Access-Control-Allow-Methods` and `Access-Control-Allow-Headers` response headers can be cached by the client until the time specified by `Access-Control-Max-Age` elapses. The default value of `Access-Control-Max-Age` response header is 5 (seconds). When the `MaxAge` field is unspecified, the gateway sets the response header "Access-Control-Max-Age: 5" by default. format: int32 minimum: 1 type: integer type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the "filter" behavior. For example, resource "myroutefilter" in group "networking.example.net"). ExtensionRef MUST NOT be used for core and extended filters. This filter can be used multiple times within the same rule. Support: Implementation-specific properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "HTTPRoute" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request headers. Support: Core properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object requestMirror: description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. This filter can be used multiple times within the same rule. Note that not all implementations will be able to support mirroring to multiple backends. Support: Extended properties: backendRef: description: |- BackendRef references a resource where mirrored requests are sent. Mirrored requests must be sent only to a single destination endpoint within this BackendRef, irrespective of how many endpoints are present within this BackendRef. If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the "ResolvedRefs" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the "ResolvedRefs" condition on the Route is set to `status: False`, with the "RefNotPermitted" reason and not configure this backend in the underlying implementation. In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. Support: Extended for Kubernetes Service Support: Implementation-specific for any other resource properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: description: |- Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. properties: denominator: default: 100 format: int32 minimum: 1 type: integer numerator: format: int32 minimum: 0 type: integer required: - numerator type: object x-kubernetes-validations: - message: numerator must be less than or equal to denominator rule: self.numerator <= self.denominator percent: description: |- Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. format: int32 maximum: 100 minimum: 0 type: integer required: - backendRef type: object x-kubernetes-validations: - message: Only one of percent or fraction may be specified in HTTPRequestMirrorFilter rule: '!(has(self.percent) && has(self.fraction))' requestRedirect: description: |- RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. Support: Core properties: hostname: description: |- Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname in the `Host` header of the request is used. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string path: description: |- Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. Support: Extended properties: replaceFullPath: description: |- ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. maxLength: 1024 type: string replacePrefixMatch: description: |- ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch of "/xyz" would be modified to "/xyz/bar". Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in the implementation setting the Accepted Condition for the Route to `status: False`. Request Path | Prefix Match | Replace Prefix | Modified Path maxLength: 1024 type: string type: description: |- Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - ReplaceFullPath - ReplacePrefixMatch type: string required: - type type: object x-kubernetes-validations: - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) : true' - message: type must be 'ReplaceFullPath' when replaceFullPath is set rule: 'has(self.replaceFullPath) ? self.type == ''ReplaceFullPath'' : true' - message: replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch' rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) : true' - message: type must be 'ReplacePrefixMatch' when replacePrefixMatch is set rule: 'has(self.replacePrefixMatch) ? self.type == ''ReplacePrefixMatch'' : true' port: description: |- Port is the port to be used in the value of the `Location` header in the response. If no port is specified, the redirect port MUST be derived using the following rules: * If redirect scheme is not-empty, the redirect port MUST be the well-known port associated with the redirect scheme. Specifically "http" to port 80 and "https" to port 443. If the redirect scheme does not have a well-known port, the listener port of the Gateway SHOULD be used. * If redirect scheme is empty, the redirect port MUST be the Gateway Listener port. Implementations SHOULD NOT add the port number in the 'Location' header in the following cases: * A Location header that will use HTTP (whether that is determined via the Listener protocol or the Scheme field) _and_ use port 80. * A Location header that will use HTTPS (whether that is determined via the Listener protocol or the Scheme field) _and_ use port 443. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer scheme: description: |- Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. Scheme redirects can affect the port of the redirect, for more information, refer to the documentation for the port field of this filter. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. Support: Extended enum: - http - https type: string statusCode: default: 302 description: |- StatusCode is the HTTP status code to be used in response. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. Support: Core enum: - 301 - 302 - 303 - 307 - 308 type: integer type: object responseHeaderModifier: description: |- ResponseHeaderModifier defines a schema for a filter that modifies response headers. Support: Extended properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object type: description: |- Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: - Core: Filter types and their corresponding configuration defined by "Support: Core" in this package, e.g. "RequestHeaderModifier". All implementations must support core filters. - Extended: Filter types and their corresponding configuration defined by "Support: Extended" in this package, e.g. "RequestMirror". Implementers are encouraged to support extended filters. - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to "ExtensionRef" for custom filters. Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - RequestHeaderModifier - ResponseHeaderModifier - RequestMirror - RequestRedirect - URLRewrite - ExtensionRef - CORS type: string urlRewrite: description: |- URLRewrite defines a schema for a filter that modifies a request during forwarding. Support: Extended properties: hostname: description: |- Hostname is the value to be used to replace the Host header value during forwarding. Support: Extended maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string path: description: |- Path defines a path rewrite. Support: Extended properties: replaceFullPath: description: |- ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. maxLength: 1024 type: string replacePrefixMatch: description: |- ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch of "/xyz" would be modified to "/xyz/bar". Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in the implementation setting the Accepted Condition for the Route to `status: False`. Request Path | Prefix Match | Replace Prefix | Modified Path maxLength: 1024 type: string type: description: |- Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - ReplaceFullPath - ReplacePrefixMatch type: string required: - type type: object x-kubernetes-validations: - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) : true' - message: type must be 'ReplaceFullPath' when replaceFullPath is set rule: 'has(self.replaceFullPath) ? self.type == ''ReplaceFullPath'' : true' - message: replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch' rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) : true' - message: type must be 'ReplacePrefixMatch' when replacePrefixMatch is set rule: 'has(self.replacePrefixMatch) ? self.type == ''ReplacePrefixMatch'' : true' type: object required: - type type: object x-kubernetes-validations: - message: filter.cors must be nil if the filter.type is not CORS rule: '!(has(self.cors) && self.type != ''CORS'')' - message: filter.cors must be specified for CORS filter.type rule: '!(!has(self.cors) && self.type == ''CORS'')' - message: filter.requestHeaderModifier must be nil if the filter.type is not RequestHeaderModifier rule: '!(has(self.requestHeaderModifier) && self.type != ''RequestHeaderModifier'')' - message: filter.requestHeaderModifier must be specified for RequestHeaderModifier filter.type rule: '!(!has(self.requestHeaderModifier) && self.type == ''RequestHeaderModifier'')' - message: filter.responseHeaderModifier must be nil if the filter.type is not ResponseHeaderModifier rule: '!(has(self.responseHeaderModifier) && self.type != ''ResponseHeaderModifier'')' - message: filter.responseHeaderModifier must be specified for ResponseHeaderModifier filter.type rule: '!(!has(self.responseHeaderModifier) && self.type == ''ResponseHeaderModifier'')' - message: filter.requestMirror must be nil if the filter.type is not RequestMirror rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' - message: filter.requestMirror must be specified for RequestMirror filter.type rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' - message: filter.requestRedirect must be nil if the filter.type is not RequestRedirect rule: '!(has(self.requestRedirect) && self.type != ''RequestRedirect'')' - message: filter.requestRedirect must be specified for RequestRedirect filter.type rule: '!(!has(self.requestRedirect) && self.type == ''RequestRedirect'')' - message: filter.urlRewrite must be nil if the filter.type is not URLRewrite rule: '!(has(self.urlRewrite) && self.type != ''URLRewrite'')' - message: filter.urlRewrite must be specified for URLRewrite filter.type rule: '!(!has(self.urlRewrite) && self.type == ''URLRewrite'')' - message: filter.extensionRef must be nil if the filter.type is not ExtensionRef rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both rule: '!(self.exists(f, f.type == ''RequestRedirect'') && self.exists(f, f.type == ''URLRewrite''))' - message: RequestHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1 - message: ResponseHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() <= 1 - message: RequestRedirect filter cannot be repeated rule: self.filter(f, f.type == 'RequestRedirect').size() <= 1 - message: URLRewrite filter cannot be repeated rule: self.filter(f, f.type == 'URLRewrite').size() <= 1 group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer weight: default: 1 description: |- Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. Support for this field varies based on the context where used. format: int32 maximum: 1000000 minimum: 0 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' maxItems: 16 type: array x-kubernetes-list-type: atomic filters: description: |- Filters define the filters that are applied to requests that match this rule. Wherever possible, implementations SHOULD implement filters in the order they are specified. Implementations MAY choose to implement this ordering strictly, rejecting any combination or order of filters that cannot be supported. If implementations choose a strict interpretation of filter ordering, they MUST clearly document that behavior. To reject an invalid combination or order of filters, implementations SHOULD consider the Route Rules with this configuration invalid. If all Route Rules in a Route are invalid, the entire Route would be considered invalid. If only a portion of Route Rules are invalid, implementations MUST set the "PartiallyInvalid" condition for the Route. Conformance-levels at this level are defined based on the type of filter: - ALL core filters MUST be supported by all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. Specifying the same filter multiple times is not supported unless explicitly indicated in the filter. All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation cannot support other combinations of filters, they must clearly document that limitation. In cases where incompatible or unsupported filters are specified and cause the `Accepted` condition to be set to status `False`, implementations may use the `IncompatibleFilters` reason to specify this configuration error. Support: Core items: description: |- HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: cors: description: |- CORS defines a schema for a filter that responds to the cross-origin request based on HTTP response header. Support: Extended properties: allowCredentials: description: |- AllowCredentials indicates whether the actual cross-origin request allows to include credentials. When set to true, the gateway will include the `Access-Control-Allow-Credentials` response header with value true (case-sensitive). When set to false or omitted the gateway will omit the header `Access-Control-Allow-Credentials` entirely (this is the standard CORS behavior). Support: Extended type: boolean allowHeaders: description: |- AllowHeaders indicates which HTTP request headers are supported for accessing the requested resource. Header names are not case-sensitive. Multiple header names in the value of the `Access-Control-Allow-Headers` response header are separated by a comma (","). When the `AllowHeaders` field is configured with one or more headers, the gateway must return the `Access-Control-Allow-Headers` response header which value is present in the `AllowHeaders` field. If any header name in the `Access-Control-Request-Headers` request header is not included in the list of header names specified by the response header `Access-Control-Allow-Headers`, it will present an error on the client side. If any header name in the `Access-Control-Allow-Headers` response header does not recognize by the client, it will also occur an error on the client side. A wildcard indicates that the requests with all HTTP headers are allowed. If config contains the wildcard "*" in allowHeaders and the request is not credentialed, the `Access-Control-Allow-Headers` response header can either use the `*` wildcard or the value of Access-Control-Request-Headers from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Headers` response header. When also the `AllowCredentials` field is true and `AllowHeaders` field is specified with the `*` wildcard, the gateway must specify one or more HTTP headers in the value of the `Access-Control-Allow-Headers` response header. The value of the header `Access-Control-Allow-Headers` is same as the `Access-Control-Request-Headers` header provided by the client. If the header `Access-Control-Request-Headers` is not included in the request, the gateway will omit the `Access-Control-Allow-Headers` response header, instead of specifying the `*` wildcard. Support: Extended items: description: |- HTTPHeaderName is the name of an HTTP header. Valid values include: * "Authorization" * "Set-Cookie" Invalid values include: - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo headers are not currently supported by this type. - "/invalid" - "/ " is an invalid character maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string maxItems: 64 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowHeaders cannot contain '*' alongside other methods rule: '!(''*'' in self && self.size() > 1)' allowMethods: description: |- AllowMethods indicates which HTTP methods are supported for accessing the requested resource. Valid values are any method defined by RFC9110, along with the special value `*`, which represents all HTTP methods are allowed. Method names are case-sensitive, so these values are also case-sensitive. (See https://www.rfc-editor.org/rfc/rfc2616#section-5.1.1) Multiple method names in the value of the `Access-Control-Allow-Methods` response header are separated by a comma (","). A CORS-safelisted method is a method that is `GET`, `HEAD`, or `POST`. (See https://fetch.spec.whatwg.org/#cors-safelisted-method) The CORS-safelisted methods are always allowed, regardless of whether they are specified in the `AllowMethods` field. When the `AllowMethods` field is configured with one or more methods, the gateway must return the `Access-Control-Allow-Methods` response header which value is present in the `AllowMethods` field. If the HTTP method of the `Access-Control-Request-Method` request header is not included in the list of methods specified by the response header `Access-Control-Allow-Methods`, it will present an error on the client side. If config contains the wildcard "*" in allowMethods and the request is not credentialed, the `Access-Control-Allow-Methods` response header can either use the `*` wildcard or the value of Access-Control-Request-Method from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Methods` response header. When also the `AllowCredentials` field is true and `AllowMethods` field specified with the `*` wildcard, the gateway must specify one HTTP method in the value of the Access-Control-Allow-Methods response header. The value of the header `Access-Control-Allow-Methods` is same as the `Access-Control-Request-Method` header provided by the client. If the header `Access-Control-Request-Method` is not included in the request, the gateway will omit the `Access-Control-Allow-Methods` response header, instead of specifying the `*` wildcard. Support: Extended items: enum: - GET - HEAD - POST - PUT - DELETE - CONNECT - OPTIONS - TRACE - PATCH - '*' type: string maxItems: 9 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowMethods cannot contain '*' alongside other methods rule: '!(''*'' in self && self.size() > 1)' allowOrigins: description: |- AllowOrigins indicates whether the response can be shared with requested resource from the given `Origin`. The `Origin` consists of a scheme and a host, with an optional port, and takes the form `://(:)`. Valid values for scheme are: `http` and `https`. Valid values for port are any integer between 1 and 65535 (the list of available TCP/UDP ports). Note that, if not included, port `80` is assumed for `http` scheme origins, and port `443` is assumed for `https` origins. This may affect origin matching. The host part of the origin may contain the wildcard character `*`. These wildcard characters behave as follows: * `*` is a greedy match to the _left_, including any number of DNS labels to the left of its position. This also means that `*` will include any number of period `.` characters to the left of its position. * A wildcard by itself matches all hosts. An origin value that includes _only_ the `*` character indicates requests from all `Origin`s are allowed. When the `AllowOrigins` field is configured with multiple origins, it means the server supports clients from multiple origins. If the request `Origin` matches the configured allowed origins, the gateway must return the given `Origin` and sets value of the header `Access-Control-Allow-Origin` same as the `Origin` header provided by the client. The status code of a successful response to a "preflight" request is always an OK status (i.e., 204 or 200). If the request `Origin` does not match the configured allowed origins, the gateway returns 204/200 response but doesn't set the relevant cross-origin response headers. Alternatively, the gateway responds with 403 status to the "preflight" request is denied, coupled with omitting the CORS headers. The cross-origin request fails on the client side. Therefore, the client doesn't attempt the actual cross-origin request. Conversely, if the request `Origin` matches one of the configured allowed origins, the gateway sets the response header `Access-Control-Allow-Origin` to the same value as the `Origin` header provided by the client. When config has the wildcard ("*") in allowOrigins, and the request is not credentialed (e.g., it is a preflight request), the `Access-Control-Allow-Origin` response header either contains the wildcard as well or the Origin from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Origin` response header. When also the `AllowCredentials` field is true and `AllowOrigins` field specified with the `*` wildcard, the gateway must return a single origin in the value of the `Access-Control-Allow-Origin` response header, instead of specifying the `*` wildcard. The value of the header `Access-Control-Allow-Origin` is same as the `Origin` header provided by the client. Support: Extended items: description: |- The CORSOrigin MUST NOT be a relative URI, and it MUST follow the URI syntax and encoding rules specified in RFC3986. The CORSOrigin MUST include both a scheme (e.g., "http" or "spiffe") and a scheme-specific-part, or it should be a single '*' character. URIs that include an authority MUST include a fully qualified domain name or IP address as the host. maxLength: 253 minLength: 1 pattern: (^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$) type: string maxItems: 64 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowOrigins cannot contain '*' alongside other origins rule: '!(''*'' in self && self.size() > 1)' exposeHeaders: description: |- ExposeHeaders indicates which HTTP response headers can be exposed to client-side scripts in response to a cross-origin request. A CORS-safelisted response header is an HTTP header in a CORS response that it is considered safe to expose to the client scripts. The CORS-safelisted response headers include the following headers: `Cache-Control` `Content-Language` `Content-Length` `Content-Type` `Expires` `Last-Modified` `Pragma` (See https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name) The CORS-safelisted response headers are exposed to client by default. When an HTTP header name is specified using the `ExposeHeaders` field, this additional header will be exposed as part of the response to the client. Header names are not case-sensitive. Multiple header names in the value of the `Access-Control-Expose-Headers` response header are separated by a comma (","). A wildcard indicates that the responses with all HTTP headers are exposed to clients. The `Access-Control-Expose-Headers` response header can only use `*` wildcard as value when the request is not credentialed. When the `exposeHeaders` config field contains the "*" wildcard and the request is credentialed, the gateway cannot use the `*` wildcard in the `Access-Control-Expose-Headers` response header. Support: Extended items: description: |- HTTPHeaderName is the name of an HTTP header. Valid values include: * "Authorization" * "Set-Cookie" Invalid values include: - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo headers are not currently supported by this type. - "/invalid" - "/ " is an invalid character maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string maxItems: 64 type: array x-kubernetes-list-type: set maxAge: default: 5 description: |- MaxAge indicates the duration (in seconds) for the client to cache the results of a "preflight" request. The information provided by the `Access-Control-Allow-Methods` and `Access-Control-Allow-Headers` response headers can be cached by the client until the time specified by `Access-Control-Max-Age` elapses. The default value of `Access-Control-Max-Age` response header is 5 (seconds). When the `MaxAge` field is unspecified, the gateway sets the response header "Access-Control-Max-Age: 5" by default. format: int32 minimum: 1 type: integer type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the "filter" behavior. For example, resource "myroutefilter" in group "networking.example.net"). ExtensionRef MUST NOT be used for core and extended filters. This filter can be used multiple times within the same rule. Support: Implementation-specific properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "HTTPRoute" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request headers. Support: Core properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object requestMirror: description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. This filter can be used multiple times within the same rule. Note that not all implementations will be able to support mirroring to multiple backends. Support: Extended properties: backendRef: description: |- BackendRef references a resource where mirrored requests are sent. Mirrored requests must be sent only to a single destination endpoint within this BackendRef, irrespective of how many endpoints are present within this BackendRef. If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the "ResolvedRefs" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the "ResolvedRefs" condition on the Route is set to `status: False`, with the "RefNotPermitted" reason and not configure this backend in the underlying implementation. In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. Support: Extended for Kubernetes Service Support: Implementation-specific for any other resource properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: description: |- Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. properties: denominator: default: 100 format: int32 minimum: 1 type: integer numerator: format: int32 minimum: 0 type: integer required: - numerator type: object x-kubernetes-validations: - message: numerator must be less than or equal to denominator rule: self.numerator <= self.denominator percent: description: |- Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. format: int32 maximum: 100 minimum: 0 type: integer required: - backendRef type: object x-kubernetes-validations: - message: Only one of percent or fraction may be specified in HTTPRequestMirrorFilter rule: '!(has(self.percent) && has(self.fraction))' requestRedirect: description: |- RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. Support: Core properties: hostname: description: |- Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname in the `Host` header of the request is used. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string path: description: |- Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. Support: Extended properties: replaceFullPath: description: |- ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. maxLength: 1024 type: string replacePrefixMatch: description: |- ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch of "/xyz" would be modified to "/xyz/bar". Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in the implementation setting the Accepted Condition for the Route to `status: False`. Request Path | Prefix Match | Replace Prefix | Modified Path maxLength: 1024 type: string type: description: |- Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - ReplaceFullPath - ReplacePrefixMatch type: string required: - type type: object x-kubernetes-validations: - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) : true' - message: type must be 'ReplaceFullPath' when replaceFullPath is set rule: 'has(self.replaceFullPath) ? self.type == ''ReplaceFullPath'' : true' - message: replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch' rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) : true' - message: type must be 'ReplacePrefixMatch' when replacePrefixMatch is set rule: 'has(self.replacePrefixMatch) ? self.type == ''ReplacePrefixMatch'' : true' port: description: |- Port is the port to be used in the value of the `Location` header in the response. If no port is specified, the redirect port MUST be derived using the following rules: * If redirect scheme is not-empty, the redirect port MUST be the well-known port associated with the redirect scheme. Specifically "http" to port 80 and "https" to port 443. If the redirect scheme does not have a well-known port, the listener port of the Gateway SHOULD be used. * If redirect scheme is empty, the redirect port MUST be the Gateway Listener port. Implementations SHOULD NOT add the port number in the 'Location' header in the following cases: * A Location header that will use HTTP (whether that is determined via the Listener protocol or the Scheme field) _and_ use port 80. * A Location header that will use HTTPS (whether that is determined via the Listener protocol or the Scheme field) _and_ use port 443. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer scheme: description: |- Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. Scheme redirects can affect the port of the redirect, for more information, refer to the documentation for the port field of this filter. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. Support: Extended enum: - http - https type: string statusCode: default: 302 description: |- StatusCode is the HTTP status code to be used in response. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. Support: Core enum: - 301 - 302 - 303 - 307 - 308 type: integer type: object responseHeaderModifier: description: |- ResponseHeaderModifier defines a schema for a filter that modifies response headers. Support: Extended properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object type: description: |- Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: - Core: Filter types and their corresponding configuration defined by "Support: Core" in this package, e.g. "RequestHeaderModifier". All implementations must support core filters. - Extended: Filter types and their corresponding configuration defined by "Support: Extended" in this package, e.g. "RequestMirror". Implementers are encouraged to support extended filters. - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to "ExtensionRef" for custom filters. Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - RequestHeaderModifier - ResponseHeaderModifier - RequestMirror - RequestRedirect - URLRewrite - ExtensionRef - CORS type: string urlRewrite: description: |- URLRewrite defines a schema for a filter that modifies a request during forwarding. Support: Extended properties: hostname: description: |- Hostname is the value to be used to replace the Host header value during forwarding. Support: Extended maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string path: description: |- Path defines a path rewrite. Support: Extended properties: replaceFullPath: description: |- ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. maxLength: 1024 type: string replacePrefixMatch: description: |- ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch of "/xyz" would be modified to "/xyz/bar". Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in the implementation setting the Accepted Condition for the Route to `status: False`. Request Path | Prefix Match | Replace Prefix | Modified Path maxLength: 1024 type: string type: description: |- Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - ReplaceFullPath - ReplacePrefixMatch type: string required: - type type: object x-kubernetes-validations: - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) : true' - message: type must be 'ReplaceFullPath' when replaceFullPath is set rule: 'has(self.replaceFullPath) ? self.type == ''ReplaceFullPath'' : true' - message: replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch' rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) : true' - message: type must be 'ReplacePrefixMatch' when replacePrefixMatch is set rule: 'has(self.replacePrefixMatch) ? self.type == ''ReplacePrefixMatch'' : true' type: object required: - type type: object x-kubernetes-validations: - message: filter.cors must be nil if the filter.type is not CORS rule: '!(has(self.cors) && self.type != ''CORS'')' - message: filter.cors must be specified for CORS filter.type rule: '!(!has(self.cors) && self.type == ''CORS'')' - message: filter.requestHeaderModifier must be nil if the filter.type is not RequestHeaderModifier rule: '!(has(self.requestHeaderModifier) && self.type != ''RequestHeaderModifier'')' - message: filter.requestHeaderModifier must be specified for RequestHeaderModifier filter.type rule: '!(!has(self.requestHeaderModifier) && self.type == ''RequestHeaderModifier'')' - message: filter.responseHeaderModifier must be nil if the filter.type is not ResponseHeaderModifier rule: '!(has(self.responseHeaderModifier) && self.type != ''ResponseHeaderModifier'')' - message: filter.responseHeaderModifier must be specified for ResponseHeaderModifier filter.type rule: '!(!has(self.responseHeaderModifier) && self.type == ''ResponseHeaderModifier'')' - message: filter.requestMirror must be nil if the filter.type is not RequestMirror rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' - message: filter.requestMirror must be specified for RequestMirror filter.type rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' - message: filter.requestRedirect must be nil if the filter.type is not RequestRedirect rule: '!(has(self.requestRedirect) && self.type != ''RequestRedirect'')' - message: filter.requestRedirect must be specified for RequestRedirect filter.type rule: '!(!has(self.requestRedirect) && self.type == ''RequestRedirect'')' - message: filter.urlRewrite must be nil if the filter.type is not URLRewrite rule: '!(has(self.urlRewrite) && self.type != ''URLRewrite'')' - message: filter.urlRewrite must be specified for URLRewrite filter.type rule: '!(!has(self.urlRewrite) && self.type == ''URLRewrite'')' - message: filter.extensionRef must be nil if the filter.type is not ExtensionRef rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both rule: '!(self.exists(f, f.type == ''RequestRedirect'') && self.exists(f, f.type == ''URLRewrite''))' - message: RequestHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1 - message: ResponseHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() <= 1 - message: RequestRedirect filter cannot be repeated rule: self.filter(f, f.type == 'RequestRedirect').size() <= 1 - message: URLRewrite filter cannot be repeated rule: self.filter(f, f.type == 'URLRewrite').size() <= 1 matches: default: - path: type: PathPrefix value: / description: |- Matches define conditions used for matching the rule against incoming HTTP requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. For example, take the following matches configuration: ``` matches: - path: value: "/foo" headers: - name: "version" value: "v2" - path: value: "/v2/foo" ``` For a request to match against this rule, a request must satisfy EITHER of the two conditions: - path prefixed with `/foo` AND contains the header `version: v2` - path prefix of `/v2/foo` See the documentation for HTTPRouteMatch on how to specify multiple match conditions that should be ANDed together. If no matches are specified, the default is a prefix path match on "/", which has the effect of matching every HTTP request. Proxy or Load Balancer routing configuration generated from HTTPRoutes MUST prioritize matches based on the following criteria, continuing on ties. Across all rules specified on applicable Routes, precedence must be given to the match having: * "Exact" path match. * "Prefix" path match with largest number of characters. * Method match. * Largest number of header matches. * Largest number of query param matches. Note: The precedence of RegularExpression path matches are implementation-specific. If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by "{namespace}/{name}". If ties still exist within an HTTPRoute, matching precedence MUST be granted to the FIRST matching rule (in list order) with a match meeting the above criteria. When no rules matching a request have been successfully attached to the parent a request is coming from, a HTTP 404 status code MUST be returned. items: description: "HTTPRouteMatch defines the predicate used to match requests to a given\naction. Multiple match types are ANDed together, i.e. the match will\nevaluate to true only if all conditions are satisfied.\n\nFor example, the match below will match a HTTP request only if its path\nstarts with `/foo` AND it contains the `version: v1` header:\n\n```\nmatch:\n\n\tpath:\n\t \ value: \"/foo\"\n\theaders:\n\t- name: \"version\"\n\t \ value \"v1\"\n\n```" properties: headers: description: |- Headers specifies HTTP request header matchers. Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route. items: description: |- HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. When a header is repeated in an HTTP request, it is implementation-specific behavior as to how this is represented. Generally, proxies should follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding processing a repeated header, with special handling for "Set-Cookie". maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string type: default: Exact description: |- Type specifies how to match against the value of the header. Support: Core (Exact) Support: Implementation-specific (RegularExpression) Since RegularExpression HeaderMatchType has implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect. enum: - Exact - RegularExpression type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map method: description: |- Method specifies HTTP method matcher. When specified, this route will be matched only if the request has the specified method. Support: Extended enum: - GET - HEAD - POST - PUT - DELETE - CONNECT - OPTIONS - TRACE - PATCH type: string path: default: type: PathPrefix value: / description: |- Path specifies a HTTP request path matcher. If this field is not specified, a default prefix match on the "/" path is provided. properties: type: default: PathPrefix description: |- Type specifies how to match against the path Value. Support: Core (Exact, PathPrefix) Support: Implementation-specific (RegularExpression) enum: - Exact - PathPrefix - RegularExpression type: string value: default: / description: Value of the HTTP path to match against. maxLength: 1024 type: string type: object x-kubernetes-validations: - message: value must be an absolute path and start with '/' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? self.value.startsWith(''/'') : true' - message: must not contain '//' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''//'') : true' - message: must not contain '/./' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''/./'') : true' - message: must not contain '/../' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''/../'') : true' - message: must not contain '%2f' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''%2f'') : true' - message: must not contain '%2F' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''%2F'') : true' - message: must not contain '#' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''#'') : true' - message: must not end with '/..' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.endsWith(''/..'') : true' - message: must not end with '/.' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.endsWith(''/.'') : true' - message: type must be one of ['Exact', 'PathPrefix', 'RegularExpression'] rule: self.type in ['Exact','PathPrefix'] || self.type == 'RegularExpression' - message: must only contain valid characters (matching ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$) for types ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? self.value.matches(r"""^(?:[-A-Za-z0-9/._~!$&''()*+,;=:@]|[%][0-9a-fA-F]{2})+$""") : true' queryParams: description: |- QueryParams specifies HTTP query parameter matchers. Multiple match values are ANDed together, meaning, a request must match all the specified query parameters to select the route. Support: Extended items: description: |- HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP query parameters. properties: name: description: |- Name is the name of the HTTP query param to be matched. This must be an exact string match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). If multiple entries specify equivalent query param names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent query param name MUST be ignored. If a query param is repeated in an HTTP request, the behavior is purposely left undefined, since different data planes have different capabilities. However, it is *recommended* that implementations should match against the first value of the param if the data plane supports it, as this behavior is expected in other load balancing contexts outside of the Gateway API. Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string type: default: Exact description: |- Type specifies how to match against the value of the query parameter. Support: Extended (Exact) Support: Implementation-specific (RegularExpression) Since RegularExpression QueryParamMatchType has Implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect. enum: - Exact - RegularExpression type: string value: description: Value is the value of HTTP query param to be matched. maxLength: 1024 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object maxItems: 64 type: array x-kubernetes-list-type: atomic name: description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. Support: Extended maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string timeouts: description: |- Timeouts defines the timeouts that can be configured for an HTTP request. Support: Extended properties: backendRequest: description: |- BackendRequest specifies a timeout for an individual request from the gateway to a backend. This covers the time from when the request first starts being sent from the gateway to when the full response has been received from the backend. Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout completely. Implementations that cannot completely disable the timeout MUST instead interpret the zero duration as the longest possible value to which the timeout can be set. An entire client HTTP transaction with a gateway, covered by the Request timeout, may result in more than one call from the gateway to the destination backend, for example, if automatic retries are supported. The value of BackendRequest must be a Gateway API Duration string as defined by GEP-2257. When this field is unspecified, its behavior is implementation-specific; when specified, the value of BackendRequest must be no more than the value of the Request timeout (since the Request timeout encompasses the BackendRequest timeout). Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string request: description: |- Request specifies the maximum duration for a gateway to respond to an HTTP request. If the gateway has not been able to respond before this deadline is met, the gateway MUST return a timeout error. For example, setting the `rules.timeouts.request` field to the value `10s` in an `HTTPRoute` will cause a timeout if a client request is taking longer than 10 seconds to complete. Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout completely. Implementations that cannot completely disable the timeout MUST instead interpret the zero duration as the longest possible value to which the timeout can be set. This timeout is intended to cover as close to the whole request-response transaction as possible although an implementation MAY choose to start the timeout after the entire request stream has been received instead of immediately after the transaction is initiated by the client. The value of Request is a Gateway API Duration string as defined by GEP-2257. When this field is unspecified, request timeout behavior is implementation-specific. Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string type: object x-kubernetes-validations: - message: backendRequest timeout cannot be longer than request timeout rule: '!(has(self.request) && has(self.backendRequest) && duration(self.request) != duration(''0s'') && duration(self.backendRequest) > duration(self.request))' type: object x-kubernetes-validations: - message: RequestRedirect filter must not be used together with backendRefs rule: '(has(self.backendRefs) && size(self.backendRefs) > 0) ? (!has(self.filters) || self.filters.all(f, !has(f.requestRedirect))): true' - message: When using RequestRedirect filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified rule: '(has(self.filters) && self.filters.exists_one(f, has(f.requestRedirect) && has(f.requestRedirect.path) && f.requestRedirect.path.type == ''ReplacePrefixMatch'' && has(f.requestRedirect.path.replacePrefixMatch))) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != ''PathPrefix'') ? false : true) : true' - message: When using URLRewrite filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified rule: '(has(self.filters) && self.filters.exists_one(f, has(f.urlRewrite) && has(f.urlRewrite.path) && f.urlRewrite.path.type == ''ReplacePrefixMatch'' && has(f.urlRewrite.path.replacePrefixMatch))) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != ''PathPrefix'') ? false : true) : true' - message: Within backendRefs, when using RequestRedirect filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified rule: '(has(self.backendRefs) && self.backendRefs.exists_one(b, (has(b.filters) && b.filters.exists_one(f, has(f.requestRedirect) && has(f.requestRedirect.path) && f.requestRedirect.path.type == ''ReplacePrefixMatch'' && has(f.requestRedirect.path.replacePrefixMatch))) )) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != ''PathPrefix'') ? false : true) : true' - message: Within backendRefs, When using URLRewrite filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified rule: '(has(self.backendRefs) && self.backendRefs.exists_one(b, (has(b.filters) && b.filters.exists_one(f, has(f.urlRewrite) && has(f.urlRewrite.path) && f.urlRewrite.path.type == ''ReplacePrefixMatch'' && has(f.urlRewrite.path.replacePrefixMatch))) )) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != ''PathPrefix'') ? false : true) : true' maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: While 16 rules and 64 matches per rule are allowed, the total number of matches across all rules in a route must be less than 128 rule: '(self.size() > 0 ? self[0].matches.size() : 0) + (self.size() > 1 ? self[1].matches.size() : 0) + (self.size() > 2 ? self[2].matches.size() : 0) + (self.size() > 3 ? self[3].matches.size() : 0) + (self.size() > 4 ? self[4].matches.size() : 0) + (self.size() > 5 ? self[5].matches.size() : 0) + (self.size() > 6 ? self[6].matches.size() : 0) + (self.size() > 7 ? self[7].matches.size() : 0) + (self.size() > 8 ? self[8].matches.size() : 0) + (self.size() > 9 ? self[9].matches.size() : 0) + (self.size() > 10 ? self[10].matches.size() : 0) + (self.size() > 11 ? self[11].matches.size() : 0) + (self.size() > 12 ? self[12].matches.size() : 0) + (self.size() > 13 ? self[13].matches.size() : 0) + (self.size() > 14 ? self[14].matches.size() : 0) + (self.size() > 15 ? self[15].matches.size() : 0) <= 128' type: object status: description: Status defines the current state of HTTPRoute. properties: parents: description: |- Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway. items: description: |- RouteParentStatus describes the status of a route with respect to an associated Parent. properties: conditions: description: |- Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the "Accepted" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. A Route MUST be considered "Accepted" if at least one of the Route's rules is implemented by the Gateway. There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: * The Route refers to a nonexistent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace to which the controller does not have access. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string parentRef: description: |- ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object required: - conditions - controllerName - parentRef type: object maxItems: 32 type: array x-kubernetes-list-type: atomic required: - parents type: object required: - spec type: object served: true storage: true subresources: status: {} - additionalPrinterColumns: - jsonPath: .spec.hostnames name: Hostnames type: string - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1beta1 schema: openAPIV3Schema: description: |- HTTPRoute provides a way to route HTTP requests. This includes the capability to match requests by hostname, path, header, or query param. Filters can be used to specify additional processing steps. Backends specify where matching requests should be routed. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of HTTPRoute. properties: hostnames: description: |- Hostnames defines a set of hostnames that should match against the HTTP Host header to select a HTTPRoute used to process the request. Implementations MUST ignore any port value specified in the HTTP Host header while performing a match and (absent of any applicable header modification configuration) MUST forward this header unmodified to the backend. Valid values for Hostnames are determined by RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. If a hostname is specified by both the Listener and HTTPRoute, there must be at least one intersecting hostname for the HTTPRoute to be attached to the Listener. For example: * A Listener with `test.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches HTTPRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `*.example.com`, `test.example.com`, and `foo.test.example.com` would all match. On the other hand, `example.com` and `test.example.net` would not match. Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. If both the Listener and HTTPRoute have specified hostnames, any HTTPRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the HTTPRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. If both the Listener and HTTPRoute have specified hostnames, and none match with the criteria above, then the HTTPRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. In the event that multiple HTTPRoutes specify intersecting hostnames (e.g. overlapping wildcard matching and exact matching hostnames), precedence must be given to rules from the HTTPRoute with the largest number of: * Characters in a matching non-wildcard hostname. * Characters in a matching hostname. If ties exist across multiple Routes, the matching precedence rules for HTTPRouteMatches takes over. Support: Core items: description: |- Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. Hostname can be "precise" which is a domain name without the terminating dot of a network host (e.g. "foo.example.com") or "wildcard", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed. maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string maxItems: 16 type: array x-kubernetes-list-type: atomic parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. For Services, that means the Service must either be in the same namespace for a "producer" route, or the mesh implementation must support and allow "consumer" routes for the referenced Service. ReferenceGrant is not applicable for governing ParentRefs to Services - it is not possible to create a "producer" route for a Service in a different namespace from the Route. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. ParentRefs must be _distinct_. This means either that: * They select different objects. If this is the case, then parentRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, `namespace`, and `name` must be unique across all parentRef entries in the Route. * They do not select different objects, but for each optional field used, each ParentRef that selects the same object must set the same set of optional fields to different values. If one ParentRef sets a combination of optional fields, all must set the same combination. Some examples: * If one ParentRef sets `sectionName`, all ParentRefs referencing the same object must also set `sectionName`. * If one ParentRef sets `port`, all ParentRefs referencing the same object must also set `port`. * If one ParentRef sets `sectionName` and `port`, all ParentRefs referencing the same object must also set `sectionName` and `port`. It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable other kinds of cross-namespace reference. items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object maxItems: 32 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''')) : true))' - message: sectionName must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)))) rules: default: - matches: - path: type: PathPrefix value: / description: Rules are a list of HTTP matchers, filters and actions. items: description: |- HTTPRouteRule defines semantics for matching an HTTP request based on conditions (matches), processing it (filters), and forwarding the request to an API object (backendRefs). properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be sent. Failure behavior here depends on how many BackendRefs are specified and how many are invalid. If *all* entries in BackendRefs are invalid, and there are also no filters specified in this route rule, *all* traffic which matches this rule MUST receive a 500 status code. See the HTTPBackendRef definition for the rules about what makes a single HTTPBackendRef invalid. When a HTTPBackendRef is invalid, 500 status codes MUST be returned for requests that would have otherwise been routed to an invalid backend. If multiple backends are specified, and some are invalid, the proportion of requests that would otherwise have been routed to an invalid backend MUST receive a 500 status code. For example, if two backends are specified with equal weights, and one is invalid, 50 percent of traffic must receive a 500. Implementations may choose how that 50 percent is determined. When a HTTPBackendRef refers to a Service that has no ready endpoints, implementations SHOULD return a 503 for requests to that backend instead. If an implementation chooses to do this, all of the above rules for 500 responses MUST also apply for responses that return a 503. Support: Core for Kubernetes Service Support: Extended for Kubernetes ServiceImport Support: Implementation-specific for any other resource Support for weight: Core items: description: |- HTTPBackendRef defines how a HTTPRoute forwards a HTTP request. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. properties: filters: description: |- Filters defined at this level should be executed if and only if the request is being forwarded to the backend defined here. Support: Implementation-specific (For broader support of filters, use the Filters field in HTTPRouteRule.) items: description: |- HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: cors: description: |- CORS defines a schema for a filter that responds to the cross-origin request based on HTTP response header. Support: Extended properties: allowCredentials: description: |- AllowCredentials indicates whether the actual cross-origin request allows to include credentials. When set to true, the gateway will include the `Access-Control-Allow-Credentials` response header with value true (case-sensitive). When set to false or omitted the gateway will omit the header `Access-Control-Allow-Credentials` entirely (this is the standard CORS behavior). Support: Extended type: boolean allowHeaders: description: |- AllowHeaders indicates which HTTP request headers are supported for accessing the requested resource. Header names are not case-sensitive. Multiple header names in the value of the `Access-Control-Allow-Headers` response header are separated by a comma (","). When the `AllowHeaders` field is configured with one or more headers, the gateway must return the `Access-Control-Allow-Headers` response header which value is present in the `AllowHeaders` field. If any header name in the `Access-Control-Request-Headers` request header is not included in the list of header names specified by the response header `Access-Control-Allow-Headers`, it will present an error on the client side. If any header name in the `Access-Control-Allow-Headers` response header does not recognize by the client, it will also occur an error on the client side. A wildcard indicates that the requests with all HTTP headers are allowed. If config contains the wildcard "*" in allowHeaders and the request is not credentialed, the `Access-Control-Allow-Headers` response header can either use the `*` wildcard or the value of Access-Control-Request-Headers from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Headers` response header. When also the `AllowCredentials` field is true and `AllowHeaders` field is specified with the `*` wildcard, the gateway must specify one or more HTTP headers in the value of the `Access-Control-Allow-Headers` response header. The value of the header `Access-Control-Allow-Headers` is same as the `Access-Control-Request-Headers` header provided by the client. If the header `Access-Control-Request-Headers` is not included in the request, the gateway will omit the `Access-Control-Allow-Headers` response header, instead of specifying the `*` wildcard. Support: Extended items: description: |- HTTPHeaderName is the name of an HTTP header. Valid values include: * "Authorization" * "Set-Cookie" Invalid values include: - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo headers are not currently supported by this type. - "/invalid" - "/ " is an invalid character maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string maxItems: 64 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowHeaders cannot contain '*' alongside other methods rule: '!(''*'' in self && self.size() > 1)' allowMethods: description: |- AllowMethods indicates which HTTP methods are supported for accessing the requested resource. Valid values are any method defined by RFC9110, along with the special value `*`, which represents all HTTP methods are allowed. Method names are case-sensitive, so these values are also case-sensitive. (See https://www.rfc-editor.org/rfc/rfc2616#section-5.1.1) Multiple method names in the value of the `Access-Control-Allow-Methods` response header are separated by a comma (","). A CORS-safelisted method is a method that is `GET`, `HEAD`, or `POST`. (See https://fetch.spec.whatwg.org/#cors-safelisted-method) The CORS-safelisted methods are always allowed, regardless of whether they are specified in the `AllowMethods` field. When the `AllowMethods` field is configured with one or more methods, the gateway must return the `Access-Control-Allow-Methods` response header which value is present in the `AllowMethods` field. If the HTTP method of the `Access-Control-Request-Method` request header is not included in the list of methods specified by the response header `Access-Control-Allow-Methods`, it will present an error on the client side. If config contains the wildcard "*" in allowMethods and the request is not credentialed, the `Access-Control-Allow-Methods` response header can either use the `*` wildcard or the value of Access-Control-Request-Method from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Methods` response header. When also the `AllowCredentials` field is true and `AllowMethods` field specified with the `*` wildcard, the gateway must specify one HTTP method in the value of the Access-Control-Allow-Methods response header. The value of the header `Access-Control-Allow-Methods` is same as the `Access-Control-Request-Method` header provided by the client. If the header `Access-Control-Request-Method` is not included in the request, the gateway will omit the `Access-Control-Allow-Methods` response header, instead of specifying the `*` wildcard. Support: Extended items: enum: - GET - HEAD - POST - PUT - DELETE - CONNECT - OPTIONS - TRACE - PATCH - '*' type: string maxItems: 9 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowMethods cannot contain '*' alongside other methods rule: '!(''*'' in self && self.size() > 1)' allowOrigins: description: |- AllowOrigins indicates whether the response can be shared with requested resource from the given `Origin`. The `Origin` consists of a scheme and a host, with an optional port, and takes the form `://(:)`. Valid values for scheme are: `http` and `https`. Valid values for port are any integer between 1 and 65535 (the list of available TCP/UDP ports). Note that, if not included, port `80` is assumed for `http` scheme origins, and port `443` is assumed for `https` origins. This may affect origin matching. The host part of the origin may contain the wildcard character `*`. These wildcard characters behave as follows: * `*` is a greedy match to the _left_, including any number of DNS labels to the left of its position. This also means that `*` will include any number of period `.` characters to the left of its position. * A wildcard by itself matches all hosts. An origin value that includes _only_ the `*` character indicates requests from all `Origin`s are allowed. When the `AllowOrigins` field is configured with multiple origins, it means the server supports clients from multiple origins. If the request `Origin` matches the configured allowed origins, the gateway must return the given `Origin` and sets value of the header `Access-Control-Allow-Origin` same as the `Origin` header provided by the client. The status code of a successful response to a "preflight" request is always an OK status (i.e., 204 or 200). If the request `Origin` does not match the configured allowed origins, the gateway returns 204/200 response but doesn't set the relevant cross-origin response headers. Alternatively, the gateway responds with 403 status to the "preflight" request is denied, coupled with omitting the CORS headers. The cross-origin request fails on the client side. Therefore, the client doesn't attempt the actual cross-origin request. Conversely, if the request `Origin` matches one of the configured allowed origins, the gateway sets the response header `Access-Control-Allow-Origin` to the same value as the `Origin` header provided by the client. When config has the wildcard ("*") in allowOrigins, and the request is not credentialed (e.g., it is a preflight request), the `Access-Control-Allow-Origin` response header either contains the wildcard as well or the Origin from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Origin` response header. When also the `AllowCredentials` field is true and `AllowOrigins` field specified with the `*` wildcard, the gateway must return a single origin in the value of the `Access-Control-Allow-Origin` response header, instead of specifying the `*` wildcard. The value of the header `Access-Control-Allow-Origin` is same as the `Origin` header provided by the client. Support: Extended items: description: |- The CORSOrigin MUST NOT be a relative URI, and it MUST follow the URI syntax and encoding rules specified in RFC3986. The CORSOrigin MUST include both a scheme (e.g., "http" or "spiffe") and a scheme-specific-part, or it should be a single '*' character. URIs that include an authority MUST include a fully qualified domain name or IP address as the host. maxLength: 253 minLength: 1 pattern: (^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$) type: string maxItems: 64 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowOrigins cannot contain '*' alongside other origins rule: '!(''*'' in self && self.size() > 1)' exposeHeaders: description: |- ExposeHeaders indicates which HTTP response headers can be exposed to client-side scripts in response to a cross-origin request. A CORS-safelisted response header is an HTTP header in a CORS response that it is considered safe to expose to the client scripts. The CORS-safelisted response headers include the following headers: `Cache-Control` `Content-Language` `Content-Length` `Content-Type` `Expires` `Last-Modified` `Pragma` (See https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name) The CORS-safelisted response headers are exposed to client by default. When an HTTP header name is specified using the `ExposeHeaders` field, this additional header will be exposed as part of the response to the client. Header names are not case-sensitive. Multiple header names in the value of the `Access-Control-Expose-Headers` response header are separated by a comma (","). A wildcard indicates that the responses with all HTTP headers are exposed to clients. The `Access-Control-Expose-Headers` response header can only use `*` wildcard as value when the request is not credentialed. When the `exposeHeaders` config field contains the "*" wildcard and the request is credentialed, the gateway cannot use the `*` wildcard in the `Access-Control-Expose-Headers` response header. Support: Extended items: description: |- HTTPHeaderName is the name of an HTTP header. Valid values include: * "Authorization" * "Set-Cookie" Invalid values include: - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo headers are not currently supported by this type. - "/invalid" - "/ " is an invalid character maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string maxItems: 64 type: array x-kubernetes-list-type: set maxAge: default: 5 description: |- MaxAge indicates the duration (in seconds) for the client to cache the results of a "preflight" request. The information provided by the `Access-Control-Allow-Methods` and `Access-Control-Allow-Headers` response headers can be cached by the client until the time specified by `Access-Control-Max-Age` elapses. The default value of `Access-Control-Max-Age` response header is 5 (seconds). When the `MaxAge` field is unspecified, the gateway sets the response header "Access-Control-Max-Age: 5" by default. format: int32 minimum: 1 type: integer type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the "filter" behavior. For example, resource "myroutefilter" in group "networking.example.net"). ExtensionRef MUST NOT be used for core and extended filters. This filter can be used multiple times within the same rule. Support: Implementation-specific properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "HTTPRoute" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request headers. Support: Core properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object requestMirror: description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. This filter can be used multiple times within the same rule. Note that not all implementations will be able to support mirroring to multiple backends. Support: Extended properties: backendRef: description: |- BackendRef references a resource where mirrored requests are sent. Mirrored requests must be sent only to a single destination endpoint within this BackendRef, irrespective of how many endpoints are present within this BackendRef. If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the "ResolvedRefs" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the "ResolvedRefs" condition on the Route is set to `status: False`, with the "RefNotPermitted" reason and not configure this backend in the underlying implementation. In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. Support: Extended for Kubernetes Service Support: Implementation-specific for any other resource properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: description: |- Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. properties: denominator: default: 100 format: int32 minimum: 1 type: integer numerator: format: int32 minimum: 0 type: integer required: - numerator type: object x-kubernetes-validations: - message: numerator must be less than or equal to denominator rule: self.numerator <= self.denominator percent: description: |- Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. format: int32 maximum: 100 minimum: 0 type: integer required: - backendRef type: object x-kubernetes-validations: - message: Only one of percent or fraction may be specified in HTTPRequestMirrorFilter rule: '!(has(self.percent) && has(self.fraction))' requestRedirect: description: |- RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. Support: Core properties: hostname: description: |- Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname in the `Host` header of the request is used. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string path: description: |- Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. Support: Extended properties: replaceFullPath: description: |- ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. maxLength: 1024 type: string replacePrefixMatch: description: |- ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch of "/xyz" would be modified to "/xyz/bar". Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in the implementation setting the Accepted Condition for the Route to `status: False`. Request Path | Prefix Match | Replace Prefix | Modified Path maxLength: 1024 type: string type: description: |- Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - ReplaceFullPath - ReplacePrefixMatch type: string required: - type type: object x-kubernetes-validations: - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) : true' - message: type must be 'ReplaceFullPath' when replaceFullPath is set rule: 'has(self.replaceFullPath) ? self.type == ''ReplaceFullPath'' : true' - message: replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch' rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) : true' - message: type must be 'ReplacePrefixMatch' when replacePrefixMatch is set rule: 'has(self.replacePrefixMatch) ? self.type == ''ReplacePrefixMatch'' : true' port: description: |- Port is the port to be used in the value of the `Location` header in the response. If no port is specified, the redirect port MUST be derived using the following rules: * If redirect scheme is not-empty, the redirect port MUST be the well-known port associated with the redirect scheme. Specifically "http" to port 80 and "https" to port 443. If the redirect scheme does not have a well-known port, the listener port of the Gateway SHOULD be used. * If redirect scheme is empty, the redirect port MUST be the Gateway Listener port. Implementations SHOULD NOT add the port number in the 'Location' header in the following cases: * A Location header that will use HTTP (whether that is determined via the Listener protocol or the Scheme field) _and_ use port 80. * A Location header that will use HTTPS (whether that is determined via the Listener protocol or the Scheme field) _and_ use port 443. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer scheme: description: |- Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. Scheme redirects can affect the port of the redirect, for more information, refer to the documentation for the port field of this filter. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. Support: Extended enum: - http - https type: string statusCode: default: 302 description: |- StatusCode is the HTTP status code to be used in response. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. Support: Core enum: - 301 - 302 - 303 - 307 - 308 type: integer type: object responseHeaderModifier: description: |- ResponseHeaderModifier defines a schema for a filter that modifies response headers. Support: Extended properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object type: description: |- Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: - Core: Filter types and their corresponding configuration defined by "Support: Core" in this package, e.g. "RequestHeaderModifier". All implementations must support core filters. - Extended: Filter types and their corresponding configuration defined by "Support: Extended" in this package, e.g. "RequestMirror". Implementers are encouraged to support extended filters. - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to "ExtensionRef" for custom filters. Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - RequestHeaderModifier - ResponseHeaderModifier - RequestMirror - RequestRedirect - URLRewrite - ExtensionRef - CORS type: string urlRewrite: description: |- URLRewrite defines a schema for a filter that modifies a request during forwarding. Support: Extended properties: hostname: description: |- Hostname is the value to be used to replace the Host header value during forwarding. Support: Extended maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string path: description: |- Path defines a path rewrite. Support: Extended properties: replaceFullPath: description: |- ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. maxLength: 1024 type: string replacePrefixMatch: description: |- ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch of "/xyz" would be modified to "/xyz/bar". Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in the implementation setting the Accepted Condition for the Route to `status: False`. Request Path | Prefix Match | Replace Prefix | Modified Path maxLength: 1024 type: string type: description: |- Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - ReplaceFullPath - ReplacePrefixMatch type: string required: - type type: object x-kubernetes-validations: - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) : true' - message: type must be 'ReplaceFullPath' when replaceFullPath is set rule: 'has(self.replaceFullPath) ? self.type == ''ReplaceFullPath'' : true' - message: replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch' rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) : true' - message: type must be 'ReplacePrefixMatch' when replacePrefixMatch is set rule: 'has(self.replacePrefixMatch) ? self.type == ''ReplacePrefixMatch'' : true' type: object required: - type type: object x-kubernetes-validations: - message: filter.cors must be nil if the filter.type is not CORS rule: '!(has(self.cors) && self.type != ''CORS'')' - message: filter.cors must be specified for CORS filter.type rule: '!(!has(self.cors) && self.type == ''CORS'')' - message: filter.requestHeaderModifier must be nil if the filter.type is not RequestHeaderModifier rule: '!(has(self.requestHeaderModifier) && self.type != ''RequestHeaderModifier'')' - message: filter.requestHeaderModifier must be specified for RequestHeaderModifier filter.type rule: '!(!has(self.requestHeaderModifier) && self.type == ''RequestHeaderModifier'')' - message: filter.responseHeaderModifier must be nil if the filter.type is not ResponseHeaderModifier rule: '!(has(self.responseHeaderModifier) && self.type != ''ResponseHeaderModifier'')' - message: filter.responseHeaderModifier must be specified for ResponseHeaderModifier filter.type rule: '!(!has(self.responseHeaderModifier) && self.type == ''ResponseHeaderModifier'')' - message: filter.requestMirror must be nil if the filter.type is not RequestMirror rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' - message: filter.requestMirror must be specified for RequestMirror filter.type rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' - message: filter.requestRedirect must be nil if the filter.type is not RequestRedirect rule: '!(has(self.requestRedirect) && self.type != ''RequestRedirect'')' - message: filter.requestRedirect must be specified for RequestRedirect filter.type rule: '!(!has(self.requestRedirect) && self.type == ''RequestRedirect'')' - message: filter.urlRewrite must be nil if the filter.type is not URLRewrite rule: '!(has(self.urlRewrite) && self.type != ''URLRewrite'')' - message: filter.urlRewrite must be specified for URLRewrite filter.type rule: '!(!has(self.urlRewrite) && self.type == ''URLRewrite'')' - message: filter.extensionRef must be nil if the filter.type is not ExtensionRef rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both rule: '!(self.exists(f, f.type == ''RequestRedirect'') && self.exists(f, f.type == ''URLRewrite''))' - message: RequestHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1 - message: ResponseHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() <= 1 - message: RequestRedirect filter cannot be repeated rule: self.filter(f, f.type == 'RequestRedirect').size() <= 1 - message: URLRewrite filter cannot be repeated rule: self.filter(f, f.type == 'URLRewrite').size() <= 1 group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer weight: default: 1 description: |- Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. Support for this field varies based on the context where used. format: int32 maximum: 1000000 minimum: 0 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' maxItems: 16 type: array x-kubernetes-list-type: atomic filters: description: |- Filters define the filters that are applied to requests that match this rule. Wherever possible, implementations SHOULD implement filters in the order they are specified. Implementations MAY choose to implement this ordering strictly, rejecting any combination or order of filters that cannot be supported. If implementations choose a strict interpretation of filter ordering, they MUST clearly document that behavior. To reject an invalid combination or order of filters, implementations SHOULD consider the Route Rules with this configuration invalid. If all Route Rules in a Route are invalid, the entire Route would be considered invalid. If only a portion of Route Rules are invalid, implementations MUST set the "PartiallyInvalid" condition for the Route. Conformance-levels at this level are defined based on the type of filter: - ALL core filters MUST be supported by all implementations. - Implementers are encouraged to support extended filters. - Implementation-specific custom filters have no API guarantees across implementations. Specifying the same filter multiple times is not supported unless explicitly indicated in the filter. All filters are expected to be compatible with each other except for the URLRewrite and RequestRedirect filters, which may not be combined. If an implementation cannot support other combinations of filters, they must clearly document that limitation. In cases where incompatible or unsupported filters are specified and cause the `Accepted` condition to be set to status `False`, implementations may use the `IncompatibleFilters` reason to specify this configuration error. Support: Core items: description: |- HTTPRouteFilter defines processing steps that must be completed during the request or response lifecycle. HTTPRouteFilters are meant as an extension point to express processing that may be done in Gateway implementations. Some examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. properties: cors: description: |- CORS defines a schema for a filter that responds to the cross-origin request based on HTTP response header. Support: Extended properties: allowCredentials: description: |- AllowCredentials indicates whether the actual cross-origin request allows to include credentials. When set to true, the gateway will include the `Access-Control-Allow-Credentials` response header with value true (case-sensitive). When set to false or omitted the gateway will omit the header `Access-Control-Allow-Credentials` entirely (this is the standard CORS behavior). Support: Extended type: boolean allowHeaders: description: |- AllowHeaders indicates which HTTP request headers are supported for accessing the requested resource. Header names are not case-sensitive. Multiple header names in the value of the `Access-Control-Allow-Headers` response header are separated by a comma (","). When the `AllowHeaders` field is configured with one or more headers, the gateway must return the `Access-Control-Allow-Headers` response header which value is present in the `AllowHeaders` field. If any header name in the `Access-Control-Request-Headers` request header is not included in the list of header names specified by the response header `Access-Control-Allow-Headers`, it will present an error on the client side. If any header name in the `Access-Control-Allow-Headers` response header does not recognize by the client, it will also occur an error on the client side. A wildcard indicates that the requests with all HTTP headers are allowed. If config contains the wildcard "*" in allowHeaders and the request is not credentialed, the `Access-Control-Allow-Headers` response header can either use the `*` wildcard or the value of Access-Control-Request-Headers from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Headers` response header. When also the `AllowCredentials` field is true and `AllowHeaders` field is specified with the `*` wildcard, the gateway must specify one or more HTTP headers in the value of the `Access-Control-Allow-Headers` response header. The value of the header `Access-Control-Allow-Headers` is same as the `Access-Control-Request-Headers` header provided by the client. If the header `Access-Control-Request-Headers` is not included in the request, the gateway will omit the `Access-Control-Allow-Headers` response header, instead of specifying the `*` wildcard. Support: Extended items: description: |- HTTPHeaderName is the name of an HTTP header. Valid values include: * "Authorization" * "Set-Cookie" Invalid values include: - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo headers are not currently supported by this type. - "/invalid" - "/ " is an invalid character maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string maxItems: 64 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowHeaders cannot contain '*' alongside other methods rule: '!(''*'' in self && self.size() > 1)' allowMethods: description: |- AllowMethods indicates which HTTP methods are supported for accessing the requested resource. Valid values are any method defined by RFC9110, along with the special value `*`, which represents all HTTP methods are allowed. Method names are case-sensitive, so these values are also case-sensitive. (See https://www.rfc-editor.org/rfc/rfc2616#section-5.1.1) Multiple method names in the value of the `Access-Control-Allow-Methods` response header are separated by a comma (","). A CORS-safelisted method is a method that is `GET`, `HEAD`, or `POST`. (See https://fetch.spec.whatwg.org/#cors-safelisted-method) The CORS-safelisted methods are always allowed, regardless of whether they are specified in the `AllowMethods` field. When the `AllowMethods` field is configured with one or more methods, the gateway must return the `Access-Control-Allow-Methods` response header which value is present in the `AllowMethods` field. If the HTTP method of the `Access-Control-Request-Method` request header is not included in the list of methods specified by the response header `Access-Control-Allow-Methods`, it will present an error on the client side. If config contains the wildcard "*" in allowMethods and the request is not credentialed, the `Access-Control-Allow-Methods` response header can either use the `*` wildcard or the value of Access-Control-Request-Method from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Methods` response header. When also the `AllowCredentials` field is true and `AllowMethods` field specified with the `*` wildcard, the gateway must specify one HTTP method in the value of the Access-Control-Allow-Methods response header. The value of the header `Access-Control-Allow-Methods` is same as the `Access-Control-Request-Method` header provided by the client. If the header `Access-Control-Request-Method` is not included in the request, the gateway will omit the `Access-Control-Allow-Methods` response header, instead of specifying the `*` wildcard. Support: Extended items: enum: - GET - HEAD - POST - PUT - DELETE - CONNECT - OPTIONS - TRACE - PATCH - '*' type: string maxItems: 9 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowMethods cannot contain '*' alongside other methods rule: '!(''*'' in self && self.size() > 1)' allowOrigins: description: |- AllowOrigins indicates whether the response can be shared with requested resource from the given `Origin`. The `Origin` consists of a scheme and a host, with an optional port, and takes the form `://(:)`. Valid values for scheme are: `http` and `https`. Valid values for port are any integer between 1 and 65535 (the list of available TCP/UDP ports). Note that, if not included, port `80` is assumed for `http` scheme origins, and port `443` is assumed for `https` origins. This may affect origin matching. The host part of the origin may contain the wildcard character `*`. These wildcard characters behave as follows: * `*` is a greedy match to the _left_, including any number of DNS labels to the left of its position. This also means that `*` will include any number of period `.` characters to the left of its position. * A wildcard by itself matches all hosts. An origin value that includes _only_ the `*` character indicates requests from all `Origin`s are allowed. When the `AllowOrigins` field is configured with multiple origins, it means the server supports clients from multiple origins. If the request `Origin` matches the configured allowed origins, the gateway must return the given `Origin` and sets value of the header `Access-Control-Allow-Origin` same as the `Origin` header provided by the client. The status code of a successful response to a "preflight" request is always an OK status (i.e., 204 or 200). If the request `Origin` does not match the configured allowed origins, the gateway returns 204/200 response but doesn't set the relevant cross-origin response headers. Alternatively, the gateway responds with 403 status to the "preflight" request is denied, coupled with omitting the CORS headers. The cross-origin request fails on the client side. Therefore, the client doesn't attempt the actual cross-origin request. Conversely, if the request `Origin` matches one of the configured allowed origins, the gateway sets the response header `Access-Control-Allow-Origin` to the same value as the `Origin` header provided by the client. When config has the wildcard ("*") in allowOrigins, and the request is not credentialed (e.g., it is a preflight request), the `Access-Control-Allow-Origin` response header either contains the wildcard as well or the Origin from the request. When the request is credentialed, the gateway must not specify the `*` wildcard in the `Access-Control-Allow-Origin` response header. When also the `AllowCredentials` field is true and `AllowOrigins` field specified with the `*` wildcard, the gateway must return a single origin in the value of the `Access-Control-Allow-Origin` response header, instead of specifying the `*` wildcard. The value of the header `Access-Control-Allow-Origin` is same as the `Origin` header provided by the client. Support: Extended items: description: |- The CORSOrigin MUST NOT be a relative URI, and it MUST follow the URI syntax and encoding rules specified in RFC3986. The CORSOrigin MUST include both a scheme (e.g., "http" or "spiffe") and a scheme-specific-part, or it should be a single '*' character. URIs that include an authority MUST include a fully qualified domain name or IP address as the host. maxLength: 253 minLength: 1 pattern: (^\*$)|(^([a-zA-Z][a-zA-Z0-9+\-.]+):\/\/([^:/?#]+)(:([0-9]{1,5}))?$) type: string maxItems: 64 type: array x-kubernetes-list-type: set x-kubernetes-validations: - message: AllowOrigins cannot contain '*' alongside other origins rule: '!(''*'' in self && self.size() > 1)' exposeHeaders: description: |- ExposeHeaders indicates which HTTP response headers can be exposed to client-side scripts in response to a cross-origin request. A CORS-safelisted response header is an HTTP header in a CORS response that it is considered safe to expose to the client scripts. The CORS-safelisted response headers include the following headers: `Cache-Control` `Content-Language` `Content-Length` `Content-Type` `Expires` `Last-Modified` `Pragma` (See https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name) The CORS-safelisted response headers are exposed to client by default. When an HTTP header name is specified using the `ExposeHeaders` field, this additional header will be exposed as part of the response to the client. Header names are not case-sensitive. Multiple header names in the value of the `Access-Control-Expose-Headers` response header are separated by a comma (","). A wildcard indicates that the responses with all HTTP headers are exposed to clients. The `Access-Control-Expose-Headers` response header can only use `*` wildcard as value when the request is not credentialed. When the `exposeHeaders` config field contains the "*" wildcard and the request is credentialed, the gateway cannot use the `*` wildcard in the `Access-Control-Expose-Headers` response header. Support: Extended items: description: |- HTTPHeaderName is the name of an HTTP header. Valid values include: * "Authorization" * "Set-Cookie" Invalid values include: - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo headers are not currently supported by this type. - "/invalid" - "/ " is an invalid character maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string maxItems: 64 type: array x-kubernetes-list-type: set maxAge: default: 5 description: |- MaxAge indicates the duration (in seconds) for the client to cache the results of a "preflight" request. The information provided by the `Access-Control-Allow-Methods` and `Access-Control-Allow-Headers` response headers can be cached by the client until the time specified by `Access-Control-Max-Age` elapses. The default value of `Access-Control-Max-Age` response header is 5 (seconds). When the `MaxAge` field is unspecified, the gateway sets the response header "Access-Control-Max-Age: 5" by default. format: int32 minimum: 1 type: integer type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the "filter" behavior. For example, resource "myroutefilter" in group "networking.example.net"). ExtensionRef MUST NOT be used for core and extended filters. This filter can be used multiple times within the same rule. Support: Implementation-specific properties: group: description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is kind of the referent. For example "HTTPRoute" or "Service". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string required: - group - kind - name type: object requestHeaderModifier: description: |- RequestHeaderModifier defines a schema for a filter that modifies request headers. Support: Core properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object requestMirror: description: |- RequestMirror defines a schema for a filter that mirrors requests. Requests are sent to the specified destination, but responses from that destination are ignored. This filter can be used multiple times within the same rule. Note that not all implementations will be able to support mirroring to multiple backends. Support: Extended properties: backendRef: description: |- BackendRef references a resource where mirrored requests are sent. Mirrored requests must be sent only to a single destination endpoint within this BackendRef, irrespective of how many endpoints are present within this BackendRef. If the referent cannot be found, this BackendRef is invalid and must be dropped from the Gateway. The controller must ensure the "ResolvedRefs" condition on the Route status is set to `status: False` and not configure this backend in the underlying implementation. If there is a cross-namespace reference to an *existing* object that is not allowed by a ReferenceGrant, the controller must ensure the "ResolvedRefs" condition on the Route is set to `status: False`, with the "RefNotPermitted" reason and not configure this backend in the underlying implementation. In either error case, the Message of the `ResolvedRefs` Condition should be used to provide more detail about the problem. Support: Extended for Kubernetes Service Support: Implementation-specific for any other resource properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' fraction: description: |- Fraction represents the fraction of requests that should be mirrored to BackendRef. Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. properties: denominator: default: 100 format: int32 minimum: 1 type: integer numerator: format: int32 minimum: 0 type: integer required: - numerator type: object x-kubernetes-validations: - message: numerator must be less than or equal to denominator rule: self.numerator <= self.denominator percent: description: |- Percent represents the percentage of requests that should be mirrored to BackendRef. Its minimum value is 0 (indicating 0% of requests) and its maximum value is 100 (indicating 100% of requests). Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. format: int32 maximum: 100 minimum: 0 type: integer required: - backendRef type: object x-kubernetes-validations: - message: Only one of percent or fraction may be specified in HTTPRequestMirrorFilter rule: '!(has(self.percent) && has(self.fraction))' requestRedirect: description: |- RequestRedirect defines a schema for a filter that responds to the request with an HTTP redirection. Support: Core properties: hostname: description: |- Hostname is the hostname to be used in the value of the `Location` header in the response. When empty, the hostname in the `Host` header of the request is used. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string path: description: |- Path defines parameters used to modify the path of the incoming request. The modified path is then used to construct the `Location` header. When empty, the request path is used as-is. Support: Extended properties: replaceFullPath: description: |- ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. maxLength: 1024 type: string replacePrefixMatch: description: |- ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch of "/xyz" would be modified to "/xyz/bar". Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in the implementation setting the Accepted Condition for the Route to `status: False`. Request Path | Prefix Match | Replace Prefix | Modified Path maxLength: 1024 type: string type: description: |- Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - ReplaceFullPath - ReplacePrefixMatch type: string required: - type type: object x-kubernetes-validations: - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) : true' - message: type must be 'ReplaceFullPath' when replaceFullPath is set rule: 'has(self.replaceFullPath) ? self.type == ''ReplaceFullPath'' : true' - message: replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch' rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) : true' - message: type must be 'ReplacePrefixMatch' when replacePrefixMatch is set rule: 'has(self.replacePrefixMatch) ? self.type == ''ReplacePrefixMatch'' : true' port: description: |- Port is the port to be used in the value of the `Location` header in the response. If no port is specified, the redirect port MUST be derived using the following rules: * If redirect scheme is not-empty, the redirect port MUST be the well-known port associated with the redirect scheme. Specifically "http" to port 80 and "https" to port 443. If the redirect scheme does not have a well-known port, the listener port of the Gateway SHOULD be used. * If redirect scheme is empty, the redirect port MUST be the Gateway Listener port. Implementations SHOULD NOT add the port number in the 'Location' header in the following cases: * A Location header that will use HTTP (whether that is determined via the Listener protocol or the Scheme field) _and_ use port 80. * A Location header that will use HTTPS (whether that is determined via the Listener protocol or the Scheme field) _and_ use port 443. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer scheme: description: |- Scheme is the scheme to be used in the value of the `Location` header in the response. When empty, the scheme of the request is used. Scheme redirects can affect the port of the redirect, for more information, refer to the documentation for the port field of this filter. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. Support: Extended enum: - http - https type: string statusCode: default: 302 description: |- StatusCode is the HTTP status code to be used in response. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. Support: Core enum: - 301 - 302 - 303 - 307 - 308 type: integer type: object responseHeaderModifier: description: |- ResponseHeaderModifier defines a schema for a filter that modifies response headers. Support: Extended properties: add: description: |- Add adds the given header(s) (name, value) to the request before the action. It appends to any existing values associated with the header name. Input: GET /foo HTTP/1.1 my-header: foo Config: add: - name: "my-header" value: "bar,baz" Output: GET /foo HTTP/1.1 my-header: foo,bar,baz items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map remove: description: |- Remove the given header(s) from the HTTP request before the action. The value of Remove is a list of HTTP header names. Note that the header names are case-insensitive (see https://datatracker.ietf.org/doc/html/rfc2616#section-4.2). Input: GET /foo HTTP/1.1 my-header1: foo my-header2: bar my-header3: baz Config: remove: ["my-header1", "my-header3"] Output: GET /foo HTTP/1.1 my-header2: bar items: type: string maxItems: 16 type: array x-kubernetes-list-type: set set: description: |- Set overwrites the request with the given header (name, value) before the action. Input: GET /foo HTTP/1.1 my-header: foo Config: set: - name: "my-header" value: "bar" Output: GET /foo HTTP/1.1 my-header: bar items: description: HTTPHeader represents an HTTP Header name and value as defined by RFC 7230. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object type: description: |- Type identifies the type of filter to apply. As with other API fields, types are classified into three conformance levels: - Core: Filter types and their corresponding configuration defined by "Support: Core" in this package, e.g. "RequestHeaderModifier". All implementations must support core filters. - Extended: Filter types and their corresponding configuration defined by "Support: Extended" in this package, e.g. "RequestMirror". Implementers are encouraged to support extended filters. - Implementation-specific: Filters that are defined and supported by specific vendors. In the future, filters showing convergence in behavior across multiple implementations will be considered for inclusion in extended or core conformance levels. Filter-specific configuration for such filters is specified using the ExtensionRef field. `Type` should be set to "ExtensionRef" for custom filters. Implementers are encouraged to define custom implementation types to extend the core API with implementation-specific behavior. If a reference to a custom filter type cannot be resolved, the filter MUST NOT be skipped. Instead, requests that would have been processed by that filter MUST receive a HTTP error response. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - RequestHeaderModifier - ResponseHeaderModifier - RequestMirror - RequestRedirect - URLRewrite - ExtensionRef - CORS type: string urlRewrite: description: |- URLRewrite defines a schema for a filter that modifies a request during forwarding. Support: Extended properties: hostname: description: |- Hostname is the value to be used to replace the Host header value during forwarding. Support: Extended maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string path: description: |- Path defines a path rewrite. Support: Extended properties: replaceFullPath: description: |- ReplaceFullPath specifies the value with which to replace the full path of a request during a rewrite or redirect. maxLength: 1024 type: string replacePrefixMatch: description: |- ReplacePrefixMatch specifies the value with which to replace the prefix match of a request during a rewrite or redirect. For example, a request to "/foo/bar" with a prefix match of "/foo" and a ReplacePrefixMatch of "/xyz" would be modified to "/xyz/bar". Note that this matches the behavior of the PathPrefix match type. This matches full path elements. A path element refers to the list of labels in the path split by the `/` separator. When specified, a trailing `/` is ignored. For example, the paths `/abc`, `/abc/`, and `/abc/def` would all match the prefix `/abc`, but the path `/abcd` would not. ReplacePrefixMatch is only compatible with a `PathPrefix` HTTPRouteMatch. Using any other HTTPRouteMatch type on the same HTTPRouteRule will result in the implementation setting the Accepted Condition for the Route to `status: False`. Request Path | Prefix Match | Replace Prefix | Modified Path maxLength: 1024 type: string type: description: |- Type defines the type of path modifier. Additional types may be added in a future release of the API. Note that values may be added to this enum, implementations must ensure that unknown values will not cause a crash. Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. enum: - ReplaceFullPath - ReplacePrefixMatch type: string required: - type type: object x-kubernetes-validations: - message: replaceFullPath must be specified when type is set to 'ReplaceFullPath' rule: 'self.type == ''ReplaceFullPath'' ? has(self.replaceFullPath) : true' - message: type must be 'ReplaceFullPath' when replaceFullPath is set rule: 'has(self.replaceFullPath) ? self.type == ''ReplaceFullPath'' : true' - message: replacePrefixMatch must be specified when type is set to 'ReplacePrefixMatch' rule: 'self.type == ''ReplacePrefixMatch'' ? has(self.replacePrefixMatch) : true' - message: type must be 'ReplacePrefixMatch' when replacePrefixMatch is set rule: 'has(self.replacePrefixMatch) ? self.type == ''ReplacePrefixMatch'' : true' type: object required: - type type: object x-kubernetes-validations: - message: filter.cors must be nil if the filter.type is not CORS rule: '!(has(self.cors) && self.type != ''CORS'')' - message: filter.cors must be specified for CORS filter.type rule: '!(!has(self.cors) && self.type == ''CORS'')' - message: filter.requestHeaderModifier must be nil if the filter.type is not RequestHeaderModifier rule: '!(has(self.requestHeaderModifier) && self.type != ''RequestHeaderModifier'')' - message: filter.requestHeaderModifier must be specified for RequestHeaderModifier filter.type rule: '!(!has(self.requestHeaderModifier) && self.type == ''RequestHeaderModifier'')' - message: filter.responseHeaderModifier must be nil if the filter.type is not ResponseHeaderModifier rule: '!(has(self.responseHeaderModifier) && self.type != ''ResponseHeaderModifier'')' - message: filter.responseHeaderModifier must be specified for ResponseHeaderModifier filter.type rule: '!(!has(self.responseHeaderModifier) && self.type == ''ResponseHeaderModifier'')' - message: filter.requestMirror must be nil if the filter.type is not RequestMirror rule: '!(has(self.requestMirror) && self.type != ''RequestMirror'')' - message: filter.requestMirror must be specified for RequestMirror filter.type rule: '!(!has(self.requestMirror) && self.type == ''RequestMirror'')' - message: filter.requestRedirect must be nil if the filter.type is not RequestRedirect rule: '!(has(self.requestRedirect) && self.type != ''RequestRedirect'')' - message: filter.requestRedirect must be specified for RequestRedirect filter.type rule: '!(!has(self.requestRedirect) && self.type == ''RequestRedirect'')' - message: filter.urlRewrite must be nil if the filter.type is not URLRewrite rule: '!(has(self.urlRewrite) && self.type != ''URLRewrite'')' - message: filter.urlRewrite must be specified for URLRewrite filter.type rule: '!(!has(self.urlRewrite) && self.type == ''URLRewrite'')' - message: filter.extensionRef must be nil if the filter.type is not ExtensionRef rule: '!(has(self.extensionRef) && self.type != ''ExtensionRef'')' - message: filter.extensionRef must be specified for ExtensionRef filter.type rule: '!(!has(self.extensionRef) && self.type == ''ExtensionRef'')' maxItems: 16 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: May specify either httpRouteFilterRequestRedirect or httpRouteFilterRequestRewrite, but not both rule: '!(self.exists(f, f.type == ''RequestRedirect'') && self.exists(f, f.type == ''URLRewrite''))' - message: RequestHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'RequestHeaderModifier').size() <= 1 - message: ResponseHeaderModifier filter cannot be repeated rule: self.filter(f, f.type == 'ResponseHeaderModifier').size() <= 1 - message: RequestRedirect filter cannot be repeated rule: self.filter(f, f.type == 'RequestRedirect').size() <= 1 - message: URLRewrite filter cannot be repeated rule: self.filter(f, f.type == 'URLRewrite').size() <= 1 matches: default: - path: type: PathPrefix value: / description: |- Matches define conditions used for matching the rule against incoming HTTP requests. Each match is independent, i.e. this rule will be matched if **any** one of the matches is satisfied. For example, take the following matches configuration: ``` matches: - path: value: "/foo" headers: - name: "version" value: "v2" - path: value: "/v2/foo" ``` For a request to match against this rule, a request must satisfy EITHER of the two conditions: - path prefixed with `/foo` AND contains the header `version: v2` - path prefix of `/v2/foo` See the documentation for HTTPRouteMatch on how to specify multiple match conditions that should be ANDed together. If no matches are specified, the default is a prefix path match on "/", which has the effect of matching every HTTP request. Proxy or Load Balancer routing configuration generated from HTTPRoutes MUST prioritize matches based on the following criteria, continuing on ties. Across all rules specified on applicable Routes, precedence must be given to the match having: * "Exact" path match. * "Prefix" path match with largest number of characters. * Method match. * Largest number of header matches. * Largest number of query param matches. Note: The precedence of RegularExpression path matches are implementation-specific. If ties still exist across multiple Routes, matching precedence MUST be determined in order of the following criteria, continuing on ties: * The oldest Route based on creation timestamp. * The Route appearing first in alphabetical order by "{namespace}/{name}". If ties still exist within an HTTPRoute, matching precedence MUST be granted to the FIRST matching rule (in list order) with a match meeting the above criteria. When no rules matching a request have been successfully attached to the parent a request is coming from, a HTTP 404 status code MUST be returned. items: description: "HTTPRouteMatch defines the predicate used to match requests to a given\naction. Multiple match types are ANDed together, i.e. the match will\nevaluate to true only if all conditions are satisfied.\n\nFor example, the match below will match a HTTP request only if its path\nstarts with `/foo` AND it contains the `version: v1` header:\n\n```\nmatch:\n\n\tpath:\n\t \ value: \"/foo\"\n\theaders:\n\t- name: \"version\"\n\t \ value \"v1\"\n\n```" properties: headers: description: |- Headers specifies HTTP request header matchers. Multiple match values are ANDed together, meaning, a request must match all the specified headers to select the route. items: description: |- HTTPHeaderMatch describes how to select a HTTP route by matching HTTP request headers. properties: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent header name MUST be ignored. Due to the case-insensitivity of header names, "foo" and "Foo" are considered equivalent. When a header is repeated in an HTTP request, it is implementation-specific behavior as to how this is represented. Generally, proxies should follow the guidance from the RFC: https://www.rfc-editor.org/rfc/rfc7230.html#section-3.2.2 regarding processing a repeated header, with special handling for "Set-Cookie". maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string type: default: Exact description: |- Type specifies how to match against the value of the header. Support: Core (Exact) Support: Implementation-specific (RegularExpression) Since RegularExpression HeaderMatchType has implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect. enum: - Exact - RegularExpression type: string value: description: Value is the value of HTTP Header to be matched. maxLength: 4096 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map method: description: |- Method specifies HTTP method matcher. When specified, this route will be matched only if the request has the specified method. Support: Extended enum: - GET - HEAD - POST - PUT - DELETE - CONNECT - OPTIONS - TRACE - PATCH type: string path: default: type: PathPrefix value: / description: |- Path specifies a HTTP request path matcher. If this field is not specified, a default prefix match on the "/" path is provided. properties: type: default: PathPrefix description: |- Type specifies how to match against the path Value. Support: Core (Exact, PathPrefix) Support: Implementation-specific (RegularExpression) enum: - Exact - PathPrefix - RegularExpression type: string value: default: / description: Value of the HTTP path to match against. maxLength: 1024 type: string type: object x-kubernetes-validations: - message: value must be an absolute path and start with '/' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? self.value.startsWith(''/'') : true' - message: must not contain '//' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''//'') : true' - message: must not contain '/./' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''/./'') : true' - message: must not contain '/../' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''/../'') : true' - message: must not contain '%2f' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''%2f'') : true' - message: must not contain '%2F' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''%2F'') : true' - message: must not contain '#' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.contains(''#'') : true' - message: must not end with '/..' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.endsWith(''/..'') : true' - message: must not end with '/.' when type one of ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? !self.value.endsWith(''/.'') : true' - message: type must be one of ['Exact', 'PathPrefix', 'RegularExpression'] rule: self.type in ['Exact','PathPrefix'] || self.type == 'RegularExpression' - message: must only contain valid characters (matching ^(?:[-A-Za-z0-9/._~!$&'()*+,;=:@]|[%][0-9a-fA-F]{2})+$) for types ['Exact', 'PathPrefix'] rule: '(self.type in [''Exact'',''PathPrefix'']) ? self.value.matches(r"""^(?:[-A-Za-z0-9/._~!$&''()*+,;=:@]|[%][0-9a-fA-F]{2})+$""") : true' queryParams: description: |- QueryParams specifies HTTP query parameter matchers. Multiple match values are ANDed together, meaning, a request must match all the specified query parameters to select the route. Support: Extended items: description: |- HTTPQueryParamMatch describes how to select a HTTP route by matching HTTP query parameters. properties: name: description: |- Name is the name of the HTTP query param to be matched. This must be an exact string match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3). If multiple entries specify equivalent query param names, only the first entry with an equivalent name MUST be considered for a match. Subsequent entries with an equivalent query param name MUST be ignored. If a query param is repeated in an HTTP request, the behavior is purposely left undefined, since different data planes have different capabilities. However, it is *recommended* that implementations should match against the first value of the param if the data plane supports it, as this behavior is expected in other load balancing contexts outside of the Gateway API. Users SHOULD NOT route traffic based on repeated query params to guard themselves against potential differences in the implementations. maxLength: 256 minLength: 1 pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ type: string type: default: Exact description: |- Type specifies how to match against the value of the query parameter. Support: Extended (Exact) Support: Implementation-specific (RegularExpression) Since RegularExpression QueryParamMatchType has Implementation-specific conformance, implementations can support POSIX, PCRE or any other dialects of regular expressions. Please read the implementation's documentation to determine the supported dialect. enum: - Exact - RegularExpression type: string value: description: Value is the value of HTTP query param to be matched. maxLength: 1024 minLength: 1 type: string required: - name - value type: object maxItems: 16 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object maxItems: 64 type: array x-kubernetes-list-type: atomic name: description: |- Name is the name of the route rule. This name MUST be unique within a Route if it is set. Support: Extended maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string timeouts: description: |- Timeouts defines the timeouts that can be configured for an HTTP request. Support: Extended properties: backendRequest: description: |- BackendRequest specifies a timeout for an individual request from the gateway to a backend. This covers the time from when the request first starts being sent from the gateway to when the full response has been received from the backend. Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout completely. Implementations that cannot completely disable the timeout MUST instead interpret the zero duration as the longest possible value to which the timeout can be set. An entire client HTTP transaction with a gateway, covered by the Request timeout, may result in more than one call from the gateway to the destination backend, for example, if automatic retries are supported. The value of BackendRequest must be a Gateway API Duration string as defined by GEP-2257. When this field is unspecified, its behavior is implementation-specific; when specified, the value of BackendRequest must be no more than the value of the Request timeout (since the Request timeout encompasses the BackendRequest timeout). Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string request: description: |- Request specifies the maximum duration for a gateway to respond to an HTTP request. If the gateway has not been able to respond before this deadline is met, the gateway MUST return a timeout error. For example, setting the `rules.timeouts.request` field to the value `10s` in an `HTTPRoute` will cause a timeout if a client request is taking longer than 10 seconds to complete. Setting a timeout to the zero duration (e.g. "0s") SHOULD disable the timeout completely. Implementations that cannot completely disable the timeout MUST instead interpret the zero duration as the longest possible value to which the timeout can be set. This timeout is intended to cover as close to the whole request-response transaction as possible although an implementation MAY choose to start the timeout after the entire request stream has been received instead of immediately after the transaction is initiated by the client. The value of Request is a Gateway API Duration string as defined by GEP-2257. When this field is unspecified, request timeout behavior is implementation-specific. Support: Extended pattern: ^([0-9]{1,5}(h|m|s|ms)){1,4}$ type: string type: object x-kubernetes-validations: - message: backendRequest timeout cannot be longer than request timeout rule: '!(has(self.request) && has(self.backendRequest) && duration(self.request) != duration(''0s'') && duration(self.backendRequest) > duration(self.request))' type: object x-kubernetes-validations: - message: RequestRedirect filter must not be used together with backendRefs rule: '(has(self.backendRefs) && size(self.backendRefs) > 0) ? (!has(self.filters) || self.filters.all(f, !has(f.requestRedirect))): true' - message: When using RequestRedirect filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified rule: '(has(self.filters) && self.filters.exists_one(f, has(f.requestRedirect) && has(f.requestRedirect.path) && f.requestRedirect.path.type == ''ReplacePrefixMatch'' && has(f.requestRedirect.path.replacePrefixMatch))) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != ''PathPrefix'') ? false : true) : true' - message: When using URLRewrite filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified rule: '(has(self.filters) && self.filters.exists_one(f, has(f.urlRewrite) && has(f.urlRewrite.path) && f.urlRewrite.path.type == ''ReplacePrefixMatch'' && has(f.urlRewrite.path.replacePrefixMatch))) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != ''PathPrefix'') ? false : true) : true' - message: Within backendRefs, when using RequestRedirect filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified rule: '(has(self.backendRefs) && self.backendRefs.exists_one(b, (has(b.filters) && b.filters.exists_one(f, has(f.requestRedirect) && has(f.requestRedirect.path) && f.requestRedirect.path.type == ''ReplacePrefixMatch'' && has(f.requestRedirect.path.replacePrefixMatch))) )) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != ''PathPrefix'') ? false : true) : true' - message: Within backendRefs, When using URLRewrite filter with path.replacePrefixMatch, exactly one PathPrefix match must be specified rule: '(has(self.backendRefs) && self.backendRefs.exists_one(b, (has(b.filters) && b.filters.exists_one(f, has(f.urlRewrite) && has(f.urlRewrite.path) && f.urlRewrite.path.type == ''ReplacePrefixMatch'' && has(f.urlRewrite.path.replacePrefixMatch))) )) ? ((size(self.matches) != 1 || !has(self.matches[0].path) || self.matches[0].path.type != ''PathPrefix'') ? false : true) : true' maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: While 16 rules and 64 matches per rule are allowed, the total number of matches across all rules in a route must be less than 128 rule: '(self.size() > 0 ? self[0].matches.size() : 0) + (self.size() > 1 ? self[1].matches.size() : 0) + (self.size() > 2 ? self[2].matches.size() : 0) + (self.size() > 3 ? self[3].matches.size() : 0) + (self.size() > 4 ? self[4].matches.size() : 0) + (self.size() > 5 ? self[5].matches.size() : 0) + (self.size() > 6 ? self[6].matches.size() : 0) + (self.size() > 7 ? self[7].matches.size() : 0) + (self.size() > 8 ? self[8].matches.size() : 0) + (self.size() > 9 ? self[9].matches.size() : 0) + (self.size() > 10 ? self[10].matches.size() : 0) + (self.size() > 11 ? self[11].matches.size() : 0) + (self.size() > 12 ? self[12].matches.size() : 0) + (self.size() > 13 ? self[13].matches.size() : 0) + (self.size() > 14 ? self[14].matches.size() : 0) + (self.size() > 15 ? self[15].matches.size() : 0) <= 128' type: object status: description: Status defines the current state of HTTPRoute. properties: parents: description: |- Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway. items: description: |- RouteParentStatus describes the status of a route with respect to an associated Parent. properties: conditions: description: |- Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the "Accepted" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. A Route MUST be considered "Accepted" if at least one of the Route's rules is implemented by the Gateway. There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: * The Route refers to a nonexistent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace to which the controller does not have access. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string parentRef: description: |- ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object required: - conditions - controllerName - parentRef type: object maxItems: 32 type: array x-kubernetes-list-type: atomic required: - parents type: object required: - spec type: object served: true storage: false subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/standard/gateway.networking.k8s.io_listenersets.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: standard name: listenersets.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: ListenerSet listKind: ListenerSetList plural: listenersets shortNames: - lset singular: listenerset scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .status.conditions[?(@.type=="Accepted")].status name: Accepted type: string - jsonPath: .status.conditions[?(@.type=="Programmed")].status name: Programmed type: string - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1 schema: openAPIV3Schema: description: |- ListenerSet defines a set of additional listeners to attach to an existing Gateway. This resource provides a mechanism to merge multiple listeners into a single Gateway. The parent Gateway must explicitly allow ListenerSet attachment through its AllowedListeners configuration. By default, Gateways do not allow ListenerSet attachment. Routes can attach to a ListenerSet by specifying it as a parentRef, and can optionally target specific listeners using the sectionName field. Policy Attachment: - Policies that attach to a ListenerSet apply to all listeners defined in that resource - Policies do not impact listeners in the parent Gateway - Different ListenerSets attached to the same Gateway can have different policies - If an implementation cannot apply a policy to specific listeners, it should reject the policy ReferenceGrant Semantics: - ReferenceGrants applied to a Gateway are not inherited by child ListenerSets - ReferenceGrants applied to a ListenerSet do not grant permission to the parent Gateway's listeners - A ListenerSet can reference secrets/backends in its own namespace without a ReferenceGrant Gateway Integration: - The parent Gateway's status will include "AttachedListenerSets" which is the count of ListenerSets that have successfully attached to a Gateway A ListenerSet is successfully attached to a Gateway when all the following conditions are met: - The ListenerSet is selected by the Gateway's AllowedListeners field - The ListenerSet has a valid ParentRef selecting the Gateway - The ListenerSet's status has the condition "Accepted: true" properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of ListenerSet. properties: listeners: description: |- Listeners associated with this ListenerSet. Listeners define logical endpoints that are bound on this referenced parent Gateway's addresses. Listeners in a `Gateway` and their attached `ListenerSets` are concatenated as a list when programming the underlying infrastructure. Each listener name does not need to be unique across the Gateway and ListenerSets. See ListenerEntry.Name for more details. Implementations MUST treat the parent Gateway as having the merged list of all listeners from itself and attached ListenerSets using the following precedence: 1. "parent" Gateway 2. ListenerSet ordered by creation time (oldest first) 3. ListenerSet ordered alphabetically by "{namespace}/{name}". An implementation MAY reject listeners by setting the ListenerEntryStatus `Accepted` condition to False with the Reason `TooManyListeners` If a listener has a conflict, this will be reported in the Status.ListenerEntryStatus setting the `Conflicted` condition to True. Implementations SHOULD be cautious about what information from the parent or siblings are reported to avoid accidentally leaking sensitive information that the child would not otherwise have access to. This can include contents of secrets etc. items: properties: allowedRoutes: default: namespaces: from: Same description: |- AllowedRoutes defines the types of routes that MAY be attached to a Listener and the trusted namespaces where those Route resources MAY be present. Although a client request may match multiple route rules, only one rule may ultimately receive the request. Matching precedence MUST be determined in order of the following criteria: * The most specific match as defined by the Route type. * The oldest Route based on creation timestamp. For example, a Route with a creation timestamp of "2020-09-08 01:02:03" is given precedence over a Route with a creation timestamp of "2020-09-08 01:02:04". * If everything else is equivalent, the Route appearing first in alphabetical order (namespace/name) should be given precedence. For example, foo/bar is given precedence over foo/baz. All valid rules within a Route attached to this Listener should be implemented. Invalid Route rules can be ignored (sometimes that will mean the full Route). If a Route rule transitions from valid to invalid, support for that Route rule should be dropped to ensure consistency. For example, even if a filter specified by a Route rule is invalid, the rest of the rules within that Route should still be supported. properties: kinds: description: |- Kinds specifies the groups and kinds of Routes that are allowed to bind to this Gateway Listener. When unspecified or empty, the kinds of Routes selected are determined using the Listener protocol. A RouteGroupKind MUST correspond to kinds of Routes that are compatible with the application protocol specified in the Listener's Protocol field. If an implementation does not support or recognize this resource type, it MUST set the "ResolvedRefs" condition to False for this Listener with the "InvalidRouteKinds" reason. Support: Core items: description: RouteGroupKind indicates the group and kind of a Route resource. properties: group: default: gateway.networking.k8s.io description: Group is the group of the Route. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is the kind of the Route. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string required: - kind type: object maxItems: 8 type: array x-kubernetes-list-type: atomic namespaces: default: from: Same description: |- Namespaces indicates namespaces from which Routes may be attached to this Listener. This is restricted to the namespace of this Gateway by default. Support: Core properties: from: default: Same description: |- From indicates where Routes will be selected for this Gateway. Possible values are: * All: Routes in all namespaces may be used by this Gateway. * Selector: Routes in namespaces selected by the selector may be used by this Gateway. * Same: Only Routes in the same namespace may be used by this Gateway. Support: Core enum: - All - Selector - Same type: string selector: description: |- Selector must be specified when From is set to "Selector". In that case, only Routes in Namespaces matching this Selector will be selected by this Gateway. This field is ignored for other values of "From". Support: Core properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: |- A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: |- operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: |- values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array x-kubernetes-list-type: atomic required: - key - operator type: object type: array x-kubernetes-list-type: atomic matchLabels: additionalProperties: type: string description: |- matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object x-kubernetes-map-type: atomic type: object type: object hostname: description: |- Hostname specifies the virtual hostname to match for protocol types that define this concept. When unspecified, all hostnames are matched. This field is ignored for protocols that don't require hostname based matching. Implementations MUST apply Hostname matching appropriately for each of the following protocols: * TLS: The Listener Hostname MUST match the SNI. * HTTP: The Listener Hostname MUST match the Host header of the request. * HTTPS: The Listener Hostname SHOULD match at both the TLS and HTTP protocol layers as described above. If an implementation does not ensure that both the SNI and Host header match the Listener hostname, it MUST clearly document that. For HTTPRoute and TLSRoute resources, there is an interaction with the `spec.hostnames` array. When both listener and route specify hostnames, there MUST be an intersection between the values for a Route to be accepted. For more information, refer to the Route specific Hostnames documentation. Hostnames that are prefixed with a wildcard label (`*.`) are interpreted as a suffix match. That means that a match for `*.example.com` would match both `test.example.com`, and `foo.test.example.com`, but not `example.com`. maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string name: description: |- Name is the name of the Listener. This name MUST be unique within a ListenerSet. Name is not required to be unique across a Gateway and ListenerSets. Routes can attach to a Listener by having a ListenerSet as a parentRef and setting the SectionName maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string port: description: |- Port is the network port. Multiple listeners may use the same port, subject to the Listener compatibility rules. format: int32 maximum: 65535 minimum: 1 type: integer protocol: description: Protocol specifies the network protocol this listener expects to receive. maxLength: 255 minLength: 1 pattern: ^[a-zA-Z0-9]([-a-zA-Z0-9]*[a-zA-Z0-9])?$|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9]+$ type: string tls: description: |- TLS is the TLS configuration for the Listener. This field is required if the Protocol field is "HTTPS" or "TLS". It is invalid to set this field if the Protocol field is "HTTP", "TCP", or "UDP". The association of SNIs to Certificate defined in ListenerTLSConfig is defined based on the Hostname field for this listener. The GatewayClass MUST use the longest matching SNI out of all available certificates for any TLS handshake. properties: certificateRefs: description: |- CertificateRefs contains a series of references to Kubernetes objects that contains TLS certificates and private keys. These certificates are used to establish a TLS handshake for requests that match the hostname of the associated listener. A single CertificateRef to a Kubernetes Secret has "Core" support. Implementations MAY choose to support attaching multiple certificates to a Listener, but this behavior is implementation-specific. References to a resource in different namespace are invalid UNLESS there is a ReferenceGrant in the target namespace that allows the certificate to be attached. If a ReferenceGrant does not allow this reference, the "ResolvedRefs" condition MUST be set to False for this listener with the "RefNotPermitted" reason. This field is required to have at least one element when the mode is set to "Terminate" (default) and is optional otherwise. CertificateRefs can reference to standard Kubernetes resources, i.e. Secret, or implementation-specific custom resources. Support: Core - A single reference to a Kubernetes Secret of type kubernetes.io/tls Support: Implementation-specific (More than one reference or other resource types) items: description: |- SecretObjectReference identifies an API object including its namespace, defaulting to Secret. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. References to objects with invalid Group and Kind are not valid, and must be rejected by the implementation, with appropriate Conditions set on the containing object. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Secret description: Kind is kind of the referent. For example "Secret". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referenced object. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - name type: object maxItems: 64 type: array x-kubernetes-list-type: atomic mode: default: Terminate description: |- Mode defines the TLS behavior for the TLS session initiated by the client. There are two possible modes: - Terminate: The TLS session between the downstream client and the Gateway is terminated at the Gateway. This mode requires certificates to be specified in some way, such as populating the certificateRefs field. - Passthrough: The TLS session is NOT terminated by the Gateway. This implies that the Gateway can't decipher the TLS stream except for the ClientHello message of the TLS protocol. The certificateRefs field is ignored in this mode. Support: Core enum: - Terminate - Passthrough type: string options: additionalProperties: description: |- AnnotationValue is the value of an annotation in Gateway API. This is used for validation of maps such as TLS options. This roughly matches Kubernetes annotation validation, although the length validation in that case is based on the entire size of the annotations struct. maxLength: 4096 minLength: 0 type: string description: |- Options are a list of key/value pairs to enable extended TLS configuration for each implementation. For example, configuring the minimum TLS version or supported cipher suites. A set of common keys MAY be defined by the API in the future. To avoid any ambiguity, implementation-specific definitions MUST use domain-prefixed names, such as `example.com/my-custom-option`. Un-prefixed names are reserved for key names defined by Gateway API. Support: Implementation-specific maxProperties: 16 type: object type: object x-kubernetes-validations: - message: certificateRefs or options must be specified when mode is Terminate rule: 'self.mode == ''Terminate'' ? size(self.certificateRefs) > 0 || size(self.options) > 0 : true' required: - name - port - protocol type: object maxItems: 64 minItems: 1 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map x-kubernetes-validations: - message: tls must not be specified for protocols ['HTTP', 'TCP', 'UDP'] rule: 'self.all(l, l.protocol in [''HTTP'', ''TCP'', ''UDP''] ? !has(l.tls) : true)' - message: tls mode must be Terminate for protocol HTTPS rule: 'self.all(l, (l.protocol == ''HTTPS'' && has(l.tls)) ? (l.tls.mode == '''' || l.tls.mode == ''Terminate'') : true)' - message: tls mode must be set for protocol TLS rule: 'self.all(l, (l.protocol == ''TLS'' ? has(l.tls) && has(l.tls.mode) && l.tls.mode != '''' : true))' - message: hostname must not be specified for protocols ['TCP', 'UDP'] rule: 'self.all(l, l.protocol in [''TCP'', ''UDP''] ? (!has(l.hostname) || l.hostname == '''') : true)' - message: Listener name must be unique within the Gateway rule: self.all(l1, self.exists_one(l2, l1.name == l2.name)) - message: Combination of port, protocol and hostname must be unique for each listener rule: 'self.all(l1, !has(l1.port) || self.exists_one(l2, has(l2.port) && l1.port == l2.port && l1.protocol == l2.protocol && (has(l1.hostname) && has(l2.hostname) ? l1.hostname == l2.hostname : !has(l1.hostname) && !has(l2.hostname))))' parentRef: description: ParentRef references the Gateway that the listeners are attached to. properties: group: default: gateway.networking.k8s.io description: Group is the group of the referent. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: Kind is kind of the referent. For example "Gateway". maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. If not present, the namespace of the referent is assumed to be the same as the namespace of the referring object. maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - name type: object required: - listeners - parentRef type: object status: default: conditions: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Programmed description: Status defines the current state of ListenerSet. properties: conditions: default: - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Accepted - lastTransitionTime: "1970-01-01T00:00:00Z" message: Waiting for controller reason: Pending status: Unknown type: Programmed description: |- Conditions describe the current conditions of the ListenerSet. Implementations MUST express ListenerSet conditions using the `ListenerSetConditionType` and `ListenerSetConditionReason` constants so that operators and tools can converge on a common vocabulary to describe ListenerSet state. Known condition types are: * "Accepted" * "Programmed" items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map listeners: description: Listeners provide status for each unique listener port defined in the Spec. items: description: ListenerStatus is the status associated with a Listener. properties: attachedRoutes: description: |- AttachedRoutes represents the total number of Routes that have been successfully attached to this Listener. Successful attachment of a Route to a Listener is based solely on the combination of the AllowedRoutes field on the corresponding Listener and the Route's ParentRefs field. A Route is successfully attached to a Listener when it is selected by the Listener's AllowedRoutes field AND the Route has a valid ParentRef selecting the whole Gateway resource or a specific Listener as a parent resource (more detail on attachment semantics can be found in the documentation on the various Route kinds ParentRefs fields). Listener status does not impact successful attachment, i.e. the AttachedRoutes field count MUST be set for Listeners, even if the Accepted condition of an individual Listener is set to "False". The AttachedRoutes number represents the number of Routes with the Accepted condition set to "True" that have been attached to this Listener. Routes with any other value for the Accepted condition MUST NOT be included in this count. Uses for this field include troubleshooting Route attachment and measuring blast radius/impact of changes to a Listener. format: int32 type: integer conditions: description: Conditions describe the current condition of this listener. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map name: description: Name is the name of the Listener that this status corresponds to. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string supportedKinds: description: |- SupportedKinds is the list indicating the Kinds supported by this listener. This MUST represent the kinds supported by an implementation for that Listener configuration. If kinds are specified in Spec that are not supported, they MUST NOT appear in this list and an implementation MUST set the "ResolvedRefs" condition to "False" with the "InvalidRouteKinds" reason. If both valid and invalid Route kinds are specified, the implementation MUST reference the valid Route kinds that have been specified. items: description: RouteGroupKind indicates the group and kind of a Route resource. properties: group: default: gateway.networking.k8s.io description: Group is the group of the Route. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: Kind is the kind of the Route. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string required: - kind type: object maxItems: 8 type: array x-kubernetes-list-type: atomic required: - attachedRoutes - conditions - name type: object maxItems: 64 type: array x-kubernetes-list-map-keys: - name x-kubernetes-list-type: map type: object required: - spec type: object served: true storage: true subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/standard/gateway.networking.k8s.io_referencegrants.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: standard name: referencegrants.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: ReferenceGrant listKind: ReferenceGrantList plural: referencegrants shortNames: - refgrant singular: referencegrant scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1 schema: openAPIV3Schema: description: |- ReferenceGrant identifies kinds of resources in other namespaces that are trusted to reference the specified kinds of resources in the same namespace as the policy. Each ReferenceGrant can be used to represent a unique trust relationship. Additional Reference Grants can be used to add to the set of trusted sources of inbound references for the namespace they are defined within. All cross-namespace references in Gateway API (with the exception of cross-namespace Gateway-route attachment) require a ReferenceGrant. ReferenceGrant is a form of runtime verification allowing users to assert which cross-namespace object references are permitted. Implementations that support ReferenceGrant MUST NOT permit cross-namespace references which have no grant, and MUST respond to the removal of a grant by revoking the access that the grant allowed. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of ReferenceGrant. properties: from: description: |- From describes the trusted namespaces and kinds that can reference the resources described in "To". Each entry in this list MUST be considered to be an additional place that references can be valid from, or to put this another way, entries MUST be combined using OR. Support: Core items: description: ReferenceGrantFrom describes trusted namespaces and kinds. properties: group: description: |- Group is the group of the referent. When empty, the Kubernetes core API group is inferred. Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: |- Kind is the kind of the referent. Although implementations may support additional resources, the following types are part of the "Core" support level for this field. When used to permit a SecretObjectReference: * Gateway When used to permit a BackendObjectReference: * GRPCRoute * HTTPRoute * TCPRoute * TLSRoute * UDPRoute maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string namespace: description: |- Namespace is the namespace of the referent. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - group - kind - namespace type: object maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic to: description: |- To describes the resources that may be referenced by the resources described in "From". Each entry in this list MUST be considered to be an additional place that references can be valid to, or to put this another way, entries MUST be combined using OR. Support: Core items: description: |- ReferenceGrantTo describes what Kinds are allowed as targets of the references. properties: group: description: |- Group is the group of the referent. When empty, the Kubernetes core API group is inferred. Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: |- Kind is the kind of the referent. Although implementations may support additional resources, the following types are part of the "Core" support level for this field: * Secret when used to permit a SecretObjectReference * Service when used to permit a BackendObjectReference maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. When unspecified, this policy refers to all resources of the specified Group and Kind in the local namespace. maxLength: 253 minLength: 1 type: string required: - group - kind type: object maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic required: - from - to type: object type: object served: true storage: false subresources: {} - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1beta1 schema: openAPIV3Schema: description: |- ReferenceGrant identifies kinds of resources in other namespaces that are trusted to reference the specified kinds of resources in the same namespace as the policy. Each ReferenceGrant can be used to represent a unique trust relationship. Additional Reference Grants can be used to add to the set of trusted sources of inbound references for the namespace they are defined within. All cross-namespace references in Gateway API (with the exception of cross-namespace Gateway-route attachment) require a ReferenceGrant. ReferenceGrant is a form of runtime verification allowing users to assert which cross-namespace object references are permitted. Implementations that support ReferenceGrant MUST NOT permit cross-namespace references which have no grant, and MUST respond to the removal of a grant by revoking the access that the grant allowed. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of ReferenceGrant. properties: from: description: |- From describes the trusted namespaces and kinds that can reference the resources described in "To". Each entry in this list MUST be considered to be an additional place that references can be valid from, or to put this another way, entries MUST be combined using OR. Support: Core items: description: ReferenceGrantFrom describes trusted namespaces and kinds. properties: group: description: |- Group is the group of the referent. When empty, the Kubernetes core API group is inferred. Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: |- Kind is the kind of the referent. Although implementations may support additional resources, the following types are part of the "Core" support level for this field. When used to permit a SecretObjectReference: * Gateway When used to permit a BackendObjectReference: * GRPCRoute * HTTPRoute * TCPRoute * TLSRoute * UDPRoute maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string namespace: description: |- Namespace is the namespace of the referent. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string required: - group - kind - namespace type: object maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic to: description: |- To describes the resources that may be referenced by the resources described in "From". Each entry in this list MUST be considered to be an additional place that references can be valid to, or to put this another way, entries MUST be combined using OR. Support: Core items: description: |- ReferenceGrantTo describes what Kinds are allowed as targets of the references. properties: group: description: |- Group is the group of the referent. When empty, the Kubernetes core API group is inferred. Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: description: |- Kind is the kind of the referent. Although implementations may support additional resources, the following types are part of the "Core" support level for this field: * Secret when used to permit a SecretObjectReference * Service when used to permit a BackendObjectReference maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. When unspecified, this policy refers to all resources of the specified Group and Kind in the local namespace. maxLength: 253 minLength: 1 type: string required: - group - kind type: object maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic required: - from - to type: object type: object served: true storage: true subresources: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/standard/gateway.networking.k8s.io_tlsroutes.yaml ================================================ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/3328 gateway.networking.k8s.io/bundle-version: v1.4.1 gateway.networking.k8s.io/channel: standard name: tlsroutes.gateway.networking.k8s.io spec: group: gateway.networking.k8s.io names: categories: - gateway-api kind: TLSRoute listKind: TLSRouteList plural: tlsroutes singular: tlsroute scope: Namespaced versions: - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date name: v1 schema: openAPIV3Schema: description: |- The TLSRoute resource is similar to TCPRoute, but can be configured to match against TLS-specific metadata. This allows more flexibility in matching streams for a given TLS listener. If you need to forward traffic to a single target for a TLS listener, you could choose to use a TCPRoute with a TLS listener. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of TLSRoute. properties: hostnames: description: |- Hostnames defines a set of SNI hostnames that should match against the SNI attribute of TLS ClientHello message in TLS handshake. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed in SNI hostnames per RFC 6066. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. items: description: |- Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. Hostname can be "precise" which is a domain name without the terminating dot of a network host (e.g. "foo.example.com") or "wildcard", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed. maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: Hostnames cannot contain an IP rule: self.all(h, !isIP(h)) - message: Hostnames must be valid based on RFC-1123 rule: 'self.all(h, !h.contains(''*'') ? h.matches(''^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)$'') : true)' - message: Wildcards on hostnames must be the first label, and the rest of hostname must be valid based on RFC-1123 rule: 'self.all(h, h.contains(''*'') ? (h.startsWith(''*.'') && h.substring(2).matches(''^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)$'')) : true)' parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. For Services, that means the Service must either be in the same namespace for a "producer" route, or the mesh implementation must support and allow "consumer" routes for the referenced Service. ReferenceGrant is not applicable for governing ParentRefs to Services - it is not possible to create a "producer" route for a Service in a different namespace from the Route. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. ParentRefs must be _distinct_. This means either that: * They select different objects. If this is the case, then parentRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, `namespace`, and `name` must be unique across all parentRef entries in the Route. * They do not select different objects, but for each optional field used, each ParentRef that selects the same object must set the same set of optional fields to different values. If one ParentRef sets a combination of optional fields, all must set the same combination. Some examples: * If one ParentRef sets `sectionName`, all ParentRefs referencing the same object must also set `sectionName`. * If one ParentRef sets `port`, all ParentRefs referencing the same object must also set `port`. * If one ParentRef sets `sectionName` and `port`, all ParentRefs referencing the same object must also set `sectionName` and `port`. It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable other kinds of cross-namespace reference. items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object maxItems: 32 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''')) : true))' - message: sectionName must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)))) rules: description: Rules are a list of actions. items: description: TLSRouteRule is the configuration for a given rule. properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a nonexistent resource or a Service with no endpoints), the rule performs no forwarding; if no filters are specified that would result in a response being sent, the underlying implementation must actively reject request attempts to this backend, by rejecting the connection or returning a 500 status code. Request rejections must respect weight; if an invalid backend is requested to have 80% of requests, then 80% of requests must be rejected instead. Support: Core for Kubernetes Service Support: Extended for Kubernetes ServiceImport Support: Implementation-specific for any other resource Support for weight: Extended items: description: |- BackendRef defines how a Route should forward a request to a Kubernetes resource. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Note that when the BackendTLSPolicy object is enabled by the implementation, there are some extra rules about validity to consider here. See the fields where this struct is used for more information about the exact behavior. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer weight: default: 1 description: |- Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. Support for this field varies based on the context where used. format: int32 maximum: 1000000 minimum: 0 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic name: description: Name is the name of the route rule. This name MUST be unique within a Route if it is set. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - backendRefs type: object maxItems: 1 minItems: 1 type: array x-kubernetes-list-type: atomic required: - hostnames - rules type: object status: description: Status defines the current state of TLSRoute. properties: parents: description: |- Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway. items: description: |- RouteParentStatus describes the status of a route with respect to an associated Parent. properties: conditions: description: |- Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the "Accepted" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. A Route MUST be considered "Accepted" if at least one of the Route's rules is implemented by the Gateway. There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: * The Route refers to a nonexistent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace to which the controller does not have access. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string parentRef: description: |- ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object required: - conditions - controllerName - parentRef type: object maxItems: 32 type: array x-kubernetes-list-type: atomic required: - parents type: object required: - spec type: object served: true storage: true subresources: status: {} - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date deprecated: true deprecationWarning: The v1alpha2 version of TLSRoute has been deprecated and will be removed in a future release of the API. Please upgrade to v1. name: v1alpha2 schema: openAPIV3Schema: description: |- The TLSRoute resource is similar to TCPRoute, but can be configured to match against TLS-specific metadata. This allows more flexibility in matching streams for a given TLS listener. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of TLSRoute. properties: hostnames: description: |- Hostnames defines a set of SNI names that should match against the SNI attribute of TLS ClientHello message in TLS handshake. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed in SNI names per RFC 6066. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. If a hostname is specified by both the Listener and TLSRoute, there must be at least one intersecting hostname for the TLSRoute to be attached to the Listener. For example: * A Listener with `test.example.com` as the hostname matches TLSRoutes that have either not specified any hostnames, or have specified at least one of `test.example.com` or `*.example.com`. * A Listener with `*.example.com` as the hostname matches TLSRoutes that have either not specified any hostnames or have specified at least one hostname that matches the Listener hostname. For example, `test.example.com` and `*.example.com` would both match. On the other hand, `example.com` and `test.example.net` would not match. If both the Listener and TLSRoute have specified hostnames, any TLSRoute hostnames that do not match the Listener hostname MUST be ignored. For example, if a Listener specified `*.example.com`, and the TLSRoute specified `test.example.com` and `test.example.net`, `test.example.net` must not be considered for a match. If both the Listener and TLSRoute have specified hostnames, and none match with the criteria above, then the TLSRoute is not accepted. The implementation must raise an 'Accepted' Condition with a status of `False` in the corresponding RouteParentStatus. Support: Core items: description: |- Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. Hostname can be "precise" which is a domain name without the terminating dot of a network host (e.g. "foo.example.com") or "wildcard", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed. maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string maxItems: 16 type: array x-kubernetes-list-type: atomic parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. For Services, that means the Service must either be in the same namespace for a "producer" route, or the mesh implementation must support and allow "consumer" routes for the referenced Service. ReferenceGrant is not applicable for governing ParentRefs to Services - it is not possible to create a "producer" route for a Service in a different namespace from the Route. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. ParentRefs must be _distinct_. This means either that: * They select different objects. If this is the case, then parentRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, `namespace`, and `name` must be unique across all parentRef entries in the Route. * They do not select different objects, but for each optional field used, each ParentRef that selects the same object must set the same set of optional fields to different values. If one ParentRef sets a combination of optional fields, all must set the same combination. Some examples: * If one ParentRef sets `sectionName`, all ParentRefs referencing the same object must also set `sectionName`. * If one ParentRef sets `port`, all ParentRefs referencing the same object must also set `port`. * If one ParentRef sets `sectionName` and `port`, all ParentRefs referencing the same object must also set `sectionName` and `port`. It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable other kinds of cross-namespace reference. items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object maxItems: 32 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''')) : true))' - message: sectionName must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)))) rules: description: Rules are a list of TLS matchers and actions. items: description: TLSRouteRule is the configuration for a given rule. properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a nonexistent resource or a Service with no endpoints), the rule performs no forwarding; if no filters are specified that would result in a response being sent, the underlying implementation must actively reject request attempts to this backend, by rejecting the connection or returning a 500 status code. Request rejections must respect weight; if an invalid backend is requested to have 80% of requests, then 80% of requests must be rejected instead. Support: Core for Kubernetes Service Support: Extended for Kubernetes ServiceImport Support: Implementation-specific for any other resource Support for weight: Extended items: description: |- BackendRef defines how a Route should forward a request to a Kubernetes resource. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Note that when the BackendTLSPolicy object is enabled by the implementation, there are some extra rules about validity to consider here. See the fields where this struct is used for more information about the exact behavior. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer weight: default: 1 description: |- Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. Support for this field varies based on the context where used. format: int32 maximum: 1000000 minimum: 0 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic name: description: Name is the name of the route rule. This name MUST be unique within a Route if it is set. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - backendRefs type: object maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic required: - rules type: object status: description: Status defines the current state of TLSRoute. properties: parents: description: |- Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway. items: description: |- RouteParentStatus describes the status of a route with respect to an associated Parent. properties: conditions: description: |- Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the "Accepted" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. A Route MUST be considered "Accepted" if at least one of the Route's rules is implemented by the Gateway. There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: * The Route refers to a nonexistent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace to which the controller does not have access. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string parentRef: description: |- ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object required: - conditions - controllerName - parentRef type: object maxItems: 32 type: array x-kubernetes-list-type: atomic required: - parents type: object required: - spec type: object served: false storage: false subresources: status: {} - additionalPrinterColumns: - jsonPath: .metadata.creationTimestamp name: Age type: date deprecated: true deprecationWarning: The v1alpha3 version of TLSRoute has been deprecated and will be removed in a future release of the API. Please upgrade to v1. name: v1alpha3 schema: openAPIV3Schema: description: |- The TLSRoute resource is similar to TCPRoute, but can be configured to match against TLS-specific metadata. This allows more flexibility in matching streams for a given TLS listener. If you need to forward traffic to a single target for a TLS listener, you could choose to use a TCPRoute with a TLS listener. properties: apiVersion: description: |- APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources type: string kind: description: |- Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds type: string metadata: type: object spec: description: Spec defines the desired state of TLSRoute. properties: hostnames: description: |- Hostnames defines a set of SNI hostnames that should match against the SNI attribute of TLS ClientHello message in TLS handshake. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed in SNI hostnames per RFC 6066. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. items: description: |- Hostname is the fully qualified domain name of a network host. This matches the RFC 1123 definition of a hostname with 2 notable exceptions: 1. IPs are not allowed. 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard label must appear by itself as the first label. Hostname can be "precise" which is a domain name without the terminating dot of a network host (e.g. "foo.example.com") or "wildcard", which is a domain name prefixed with a single wildcard label (e.g. `*.example.com`). Note that as per RFC1035 and RFC1123, a *label* must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character. No other punctuation is allowed. maxLength: 253 minLength: 1 pattern: ^(\*\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: Hostnames cannot contain an IP rule: self.all(h, !isIP(h)) - message: Hostnames must be valid based on RFC-1123 rule: 'self.all(h, !h.contains(''*'') ? h.matches(''^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)$'') : true)' - message: Wildcards on hostnames must be the first label, and the rest of hostname must be valid based on RFC-1123 rule: 'self.all(h, h.contains(''*'') ? (h.startsWith(''*.'') && h.substring(2).matches(''^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)$'')) : true)' parentRefs: description: |- ParentRefs references the resources (usually Gateways) that a Route wants to be attached to. Note that the referenced parent resource needs to allow this for the attachment to be complete. For Gateways, that means the Gateway needs to allow attachment from Routes of this kind and namespace. For Services, that means the Service must either be in the same namespace for a "producer" route, or the mesh implementation must support and allow "consumer" routes for the referenced Service. ReferenceGrant is not applicable for governing ParentRefs to Services - it is not possible to create a "producer" route for a Service in a different namespace from the Route. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. ParentRefs must be _distinct_. This means either that: * They select different objects. If this is the case, then parentRef entries are distinct. In terms of fields, this means that the multi-part key defined by `group`, `kind`, `namespace`, and `name` must be unique across all parentRef entries in the Route. * They do not select different objects, but for each optional field used, each ParentRef that selects the same object must set the same set of optional fields to different values. If one ParentRef sets a combination of optional fields, all must set the same combination. Some examples: * If one ParentRef sets `sectionName`, all ParentRefs referencing the same object must also set `sectionName`. * If one ParentRef sets `port`, all ParentRefs referencing the same object must also set `port`. * If one ParentRef sets `sectionName` and `port`, all ParentRefs referencing the same object must also set `sectionName` and `port`. It is possible to separately reference multiple distinct objects that may be collapsed by an implementation. For example, some implementations may choose to merge compatible Gateway Listeners together. If that is the case, the list of routes attached to those resources should also be merged. Note that for ParentRefs that cross namespace boundaries, there are specific rules. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example, Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable other kinds of cross-namespace reference. items: description: |- ParentReference identifies an API object (usually a Gateway) that can be considered a parent of this resource (usually a route). There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) This API may be extended in the future to support additional kinds of parent resources. The API object must be valid in the cluster; the Group and Kind must be registered in the cluster for this reference to be valid. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object maxItems: 32 type: array x-kubernetes-list-type: atomic x-kubernetes-validations: - message: sectionName must be specified when parentRefs includes 2 or more references to the same parent rule: 'self.all(p1, self.all(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '''') && (!has(p2.__namespace__) || p2.__namespace__ == '''')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) ? ((!has(p1.sectionName) || p1.sectionName == '''') == (!has(p2.sectionName) || p2.sectionName == '''')) : true))' - message: sectionName must be unique when parentRefs includes 2 or more references to the same parent rule: self.all(p1, self.exists_one(p2, p1.group == p2.group && p1.kind == p2.kind && p1.name == p2.name && (((!has(p1.__namespace__) || p1.__namespace__ == '') && (!has(p2.__namespace__) || p2.__namespace__ == '')) || (has(p1.__namespace__) && has(p2.__namespace__) && p1.__namespace__ == p2.__namespace__ )) && (((!has(p1.sectionName) || p1.sectionName == '') && (!has(p2.sectionName) || p2.sectionName == '')) || (has(p1.sectionName) && has(p2.sectionName) && p1.sectionName == p2.sectionName)))) rules: description: Rules are a list of actions. items: description: TLSRouteRule is the configuration for a given rule. properties: backendRefs: description: |- BackendRefs defines the backend(s) where matching requests should be sent. If unspecified or invalid (refers to a nonexistent resource or a Service with no endpoints), the rule performs no forwarding; if no filters are specified that would result in a response being sent, the underlying implementation must actively reject request attempts to this backend, by rejecting the connection or returning a 500 status code. Request rejections must respect weight; if an invalid backend is requested to have 80% of requests, then 80% of requests must be rejected instead. Support: Core for Kubernetes Service Support: Extended for Kubernetes ServiceImport Support: Implementation-specific for any other resource Support for weight: Extended items: description: |- BackendRef defines how a Route should forward a request to a Kubernetes resource. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Note that when the BackendTLSPolicy object is enabled by the implementation, there are some extra rules about validity to consider here. See the fields where this struct is used for more information about the exact behavior. properties: group: default: "" description: |- Group is the group of the referent. For example, "gateway.networking.k8s.io". When unspecified or empty string, core API group is inferred. maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Service description: |- Kind is the Kubernetes resource kind of the referent. For example "Service". Defaults to "Service" when not specified. ExternalName services can refer to CNAME DNS records that may live outside of the cluster and as such are difficult to reason about in terms of conformance. They also may not be safe to forward to (see CVE-2021-25740 for more information). Implementations SHOULD NOT support ExternalName Services. Support: Core (Services with a type other than ExternalName) Support: Implementation-specific (Services with type ExternalName) maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: Name is the name of the referent. maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the backend. When unspecified, the local namespace is inferred. Note that when a namespace different than the local namespace is specified, a ReferenceGrant object is required in the referent namespace to allow that namespace's owner to accept the reference. See the ReferenceGrant documentation for details. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port specifies the destination port number to use for this resource. Port is required when the referent is a Kubernetes Service. In this case, the port number is the service port number, not the target port. For other resources, destination port might be derived from the referent resource or this field. format: int32 maximum: 65535 minimum: 1 type: integer weight: default: 1 description: |- Weight specifies the proportion of requests forwarded to the referenced backend. This is computed as weight/(sum of all weights in this BackendRefs list). For non-zero values, there may be some epsilon from the exact proportion defined here depending on the precision an implementation supports. Weight is not a percentage and the sum of weights does not need to equal 100. If only one backend is specified and it has a weight greater than 0, 100% of the traffic is forwarded to that backend. If weight is set to 0, no traffic should be forwarded for this entry. If unspecified, weight defaults to 1. Support for this field varies based on the context where used. format: int32 maximum: 1000000 minimum: 0 type: integer required: - name type: object x-kubernetes-validations: - message: Must have port for Service reference rule: '(size(self.group) == 0 && self.kind == ''Service'') ? has(self.port) : true' maxItems: 16 minItems: 1 type: array x-kubernetes-list-type: atomic name: description: Name is the name of the route rule. This name MUST be unique within a Route if it is set. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - backendRefs type: object maxItems: 1 minItems: 1 type: array x-kubernetes-list-type: atomic required: - hostnames - rules type: object status: description: Status defines the current state of TLSRoute. properties: parents: description: |- Parents is a list of parent resources (usually Gateways) that are associated with the route, and the status of the route with respect to each parent. When this route attaches to a parent, the controller that manages the parent must add an entry to this list when the controller first sees the route and should update the entry as appropriate when the route or gateway is modified. Note that parent references that cannot be resolved by an implementation of this API will not be added to this list. Implementations of this API can only populate Route status for the Gateways/parent resources they are responsible for. A maximum of 32 Gateways will be represented in this list. An empty list means the route has not been attached to any Gateway. items: description: |- RouteParentStatus describes the status of a route with respect to an associated Parent. properties: conditions: description: |- Conditions describes the status of the route with respect to the Gateway. Note that the route's availability is also subject to the Gateway's own status conditions and listener status. If the Route's ParentRef specifies an existing Gateway that supports Routes of this kind AND that Gateway's controller has sufficient access, then that Gateway's controller MUST set the "Accepted" condition on the Route, to indicate whether the route has been accepted or rejected by the Gateway, and why. A Route MUST be considered "Accepted" if at least one of the Route's rules is implemented by the Gateway. There are a number of cases where the "Accepted" condition may not be set due to lack of controller visibility, that includes when: * The Route refers to a nonexistent parent. * The Route is of a type that the controller does not support. * The Route is in a namespace to which the controller does not have access. items: description: Condition contains details for one aspect of the current state of this API Resource. properties: lastTransitionTime: description: |- lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: description: |- message is a human readable message indicating details about the transition. This may be an empty string. maxLength: 32768 type: string observedGeneration: description: |- observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: description: |- reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ type: string status: description: status of the condition, one of True, False, Unknown. enum: - "True" - "False" - Unknown type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string required: - lastTransitionTime - message - reason - status - type type: object maxItems: 8 minItems: 1 type: array x-kubernetes-list-map-keys: - type x-kubernetes-list-type: map controllerName: description: |- ControllerName is a domain/path string that indicates the name of the controller that wrote this status. This corresponds with the controllerName field on GatewayClass. Example: "example.net/gateway-controller". The format of this field is DOMAIN "/" PATH, where DOMAIN and PATH are valid Kubernetes names (https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names). Controllers MUST populate this field when writing status. Controllers should ensure that entries to status populated with their ControllerName are cleaned up when they are no longer necessary. maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$ type: string parentRef: description: |- ParentRef corresponds with a ParentRef in the spec that this RouteParentStatus struct describes the status of. properties: group: default: gateway.networking.k8s.io description: |- Group is the group of the referent. When unspecified, "gateway.networking.k8s.io" is inferred. To set the core API group (such as for a "Service" kind referent), Group must be explicitly set to "" (empty string). Support: Core maxLength: 253 pattern: ^$|^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string kind: default: Gateway description: |- Kind is kind of the referent. There are two kinds of parent resources with "Core" support: * Gateway (Gateway conformance profile) * Service (Mesh conformance profile, ClusterIP Services only) Support for other resources is Implementation-Specific. maxLength: 63 minLength: 1 pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$ type: string name: description: |- Name is the name of the referent. Support: Core maxLength: 253 minLength: 1 type: string namespace: description: |- Namespace is the namespace of the referent. When unspecified, this refers to the local namespace of the Route. Note that there are specific rules for ParentRefs which cross namespace boundaries. Cross-namespace references are only valid if they are explicitly allowed by something in the namespace they are referring to. For example: Gateway has the AllowedRoutes field, and ReferenceGrant provides a generic way to enable any other kind of cross-namespace reference. Support: Core maxLength: 63 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ type: string port: description: |- Port is the network port this Route targets. It can be interpreted differently based on the type of parent resource. When the parent resource is a Gateway, this targets all listeners listening on the specified port that also support this kind of Route(and select this Route). It's not recommended to set `Port` unless the networking behaviors specified in a Route must apply to a specific port as opposed to a listener(s) whose port(s) may be changed. When both Port and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support other parent resources. Implementations supporting other types of parent resources MUST clearly document how/if Port is interpreted. For the purpose of status, an attachment is considered successful as long as the parent resource accepts it partially. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Extended format: int32 maximum: 65535 minimum: 1 type: integer sectionName: description: |- SectionName is the name of a section within the target resource. In the following resources, SectionName is interpreted as the following: * Gateway: Listener name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. * Service: Port name. When both Port (experimental) and SectionName are specified, the name and port of the selected listener must match both specified values. Implementations MAY choose to support attaching Routes to other resources. If that is the case, they MUST clearly document how SectionName is interpreted. When unspecified (empty string), this will reference the entire resource. For the purpose of status, an attachment is considered successful if at least one section in the parent resource accepts it. For example, Gateway listeners can restrict which Routes can attach to them by Route kind, namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from the referencing Route, the Route MUST be considered successfully attached. If no Gateway listeners accept attachment from this Route, the Route MUST be considered detached from the Gateway. Support: Core maxLength: 253 minLength: 1 pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$ type: string required: - name type: object required: - conditions - controllerName - parentRef type: object maxItems: 32 type: array x-kubernetes-list-type: atomic required: - parents type: object required: - spec type: object served: false storage: false subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: null storedVersions: null ================================================ FILE: pkg/gateway/crds/standard/gateway.networking.k8s.io_vap_safeupgrades.yaml ================================================ apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicy metadata: annotations: gateway.networking.k8s.io/bundle-version: v1.5.0-dev gateway.networking.k8s.io/channel: standard name: "safe-upgrades.gateway.networking.k8s.io" spec: failurePolicy: Fail matchConstraints: resourceRules: - apiGroups: ["apiextensions.k8s.io"] apiVersions: ["v1"] operations: ["CREATE", "UPDATE"] resources: ["*"] validations: - expression: "object.spec.group != 'gateway.networking.k8s.io' || ( has(object.metadata.annotations) && object.metadata.annotations.exists(k, k == 'gateway.networking.k8s.io/channel') && object.metadata.annotations['gateway.networking.k8s.io/channel'] == 'standard' )" message: "Installing experimental CRDs on top of standard channel CRDs is prohibited by default. Uninstall ValidatingAdmissionPolicy safe-upgrades.gateway.networking.k8s.io to install experimental CRDs on top of standard channel CRDs." reason: Invalid - expression: "object.spec.group != 'gateway.networking.k8s.io' || (has(object.metadata.annotations) && object.metadata.annotations.exists(k, k == 'gateway.networking.k8s.io/bundle-version') && !matches(object.metadata.annotations['gateway.networking.k8s.io/bundle-version'], 'v1.[0-3].\\\\d+') && !matches(object.metadata.annotations['gateway.networking.k8s.io/bundle-version'], 'v0'))" #TODO Kubernetes 1.37: Migrate to kubernetes semver library message: "Installing CRDs with version before v1.5.0 is prohibited by default. Uninstall ValidatingAdmissionPolicy safe-upgrades.gateway.networking.k8s.io to install older versions." reason: Invalid --- apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingAdmissionPolicyBinding metadata: annotations: gateway.networking.k8s.io/bundle-version: v1.5.0-dev gateway.networking.k8s.io/channel: standard name: safe-upgrades.gateway.networking.k8s.io spec: policyName: safe-upgrades.gateway.networking.k8s.io validationActions: [Deny] matchResources: resourceRules: - apiGroups: ["apiextensions.k8s.io"] apiVersions: ["v1"] resources: ["customresourcedefinitions"] operations: ["CREATE", "UPDATE"] ================================================ FILE: pkg/gateway/envoy.go ================================================ package gateway import ( "bytes" "crypto/sha256" "fmt" "io" "net" "net/netip" "os" "strings" "text/template" "k8s.io/klog/v2" "sigs.k8s.io/cloud-provider-kind/pkg/config" "sigs.k8s.io/cloud-provider-kind/pkg/constants" "sigs.k8s.io/cloud-provider-kind/pkg/container" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" ) // keep in sync with dynamicControlPlaneConfig const ( proxyConfigPath = "/home/envoy/envoy.yaml" envoyAdminPort = 10000 // well known dns to reach host from containers // https://github.com/containerd/nerdctl/issues/747 dockerInternal = "host.docker.internal" limaInternal = "host.lima.internal" ) // https://www.envoyproxy.io/docs/envoy/latest/start/quick-start/configuration-dynamic-control-plane const dynamicControlPlaneConfig = `node: cluster: {{ .Cluster }} id: {{ .ID }} dynamic_resources: ads_config: api_type: GRPC grpc_services: - envoy_grpc: cluster_name: xds_cluster cds_config: ads: {} lds_config: ads: {} static_resources: clusters: - type: STRICT_DNS typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions explicit_http_config: http2_protocol_options: {} name: xds_cluster load_assignment: cluster_name: xds_cluster endpoints: - lb_endpoints: - endpoint: address: socket_address: address: {{ .ControlPlaneAddress }} port_value: {{ .ControlPlanePort }} - endpoint: address: socket_address: address: host.docker.internal port_value: {{ .ControlPlanePort }} - endpoint: address: socket_address: address: host.lima.internal port_value: {{ .ControlPlanePort }} admin: access_log_path: /dev/stdout address: socket_address: address: 0.0.0.0 port_value: {{ .AdminPort }} ` type configData struct { Cluster string ID string AdminPort int ControlPlaneAddress string ControlPlanePort int } // generateEnvoyConfig returns an envoy config generated from config data func generateEnvoyConfig(data *configData) (string, error) { if data.Cluster == "" || data.ID == "" || data.AdminPort == 0 || data.ControlPlaneAddress == "" || data.ControlPlanePort == 0 { return "", fmt.Errorf("missing parameters") } t, err := template.New("gateway-config").Parse(dynamicControlPlaneConfig) if err != nil { return "", fmt.Errorf("failed to parse config template: %w", err) } // execute the template var buff bytes.Buffer err = t.Execute(&buff, data) if err != nil { return "", fmt.Errorf("error executing config template: %w", err) } return buff.String(), nil } // gatewayName name is a unique name for the gateway container func gatewayName(clusterName, namespace, name string) string { h := sha256.New() _, err := io.WriteString(h, gatewaySimpleName(clusterName, namespace, name)) if err != nil { panic(err) } hash := h.Sum(nil) return fmt.Sprintf("%s-gw-%x", constants.ContainerPrefix, hash[:6]) } func gatewaySimpleName(clusterName, namespace, name string) string { return clusterName + "/" + namespace + "/" + name } // createGateway create a docker container with a gateway func createGateway(clusterName string, nameserver string, localAddress string, localPort int, gateway *gatewayv1.Gateway, enableTunnel bool) error { name := gatewayName(clusterName, gateway.Namespace, gateway.Name) simpleName := gatewaySimpleName(clusterName, gateway.Namespace, gateway.Name) envoyConfigData := &configData{ ID: name, Cluster: simpleName, AdminPort: envoyAdminPort, ControlPlaneAddress: localAddress, ControlPlanePort: localPort, } dynamicFilesystemConfig, err := generateEnvoyConfig(envoyConfigData) if err != nil { return err } networkName := constants.FixedNetworkName if n := os.Getenv("KIND_EXPERIMENTAL_DOCKER_NETWORK"); n != "" { networkName = n } args := []string{ "--detach", "--tty", "--user=0", "--label", fmt.Sprintf("%s=%s", constants.NodeCCMLabelKey, clusterName), "--label", fmt.Sprintf("%s=%s", constants.GatewayNameLabelKey, simpleName), "--net", networkName, "--dns", nameserver, "--init=false", "--hostname", name, "--privileged", "--restart=on-failure", "--sysctl=net.ipv4.ip_forward=1", "--sysctl=net.ipv4.conf.all.rp_filter=0", "--sysctl=net.ipv4.ip_unprivileged_port_start=1", } // support to specify addresses // only the first of each IP family will be used // listenAddress tracks the host bind address for port publishing: "0.0.0.0" for IPv4-only, // "::" for IPv6-only, or "" for dual-stack/unspecified (publish on all interfaces). var ipv4, ipv6, listenAddress string for _, address := range gateway.Spec.Addresses { if address.Type != nil && *address.Type != gatewayv1.IPAddressType { continue } ip, err := netip.ParseAddr(address.Value) if err != nil { continue } if ip.Is4() { if ipv4 == "" { ipv4 = address.Value args = append(args, "--ip", ip.String()) if ipv6 == "" { listenAddress = "0.0.0.0" } else { listenAddress = "" // dual-stack } } } else if ip.Is6() { if ipv6 == "" { ipv6 = address.Value args = append(args, "--ip6", ip.String()) if ipv4 == "" { listenAddress = "::" } else { listenAddress = "" // dual-stack } } } } args = append(args, []string{ "--sysctl=net.ipv6.conf.all.disable_ipv6=0", "--sysctl=net.ipv6.conf.all.forwarding=1", }...) if enableTunnel || config.DefaultConfig.LoadBalancerConnectivity == config.Portmap { // Forward the Listener Ports to the host so they are accessible on Mac and Windows. // For single IP-family gateways, explicitly bind on the matching listen address to avoid // dual-stack host bindings that cause connection resets in environments where IPv6 is // advertised but not functional end-to-end (e.g. some GitHub Actions runners). // For dual-stack or unspecified gateways, publish without an explicit address. // See https://github.com/kubernetes-sigs/cloud-provider-kind/issues/387 for _, listener := range gateway.Spec.Listeners { proto := "tcp" if listener.Protocol == gatewayv1.UDPProtocolType { proto = "udp" } if listenAddress != "" { hostPortBinding := net.JoinHostPort(listenAddress, fmt.Sprintf("%d", listener.Port)) args = append(args, fmt.Sprintf("--publish=%s:%d/%s", hostPortBinding, listener.Port, proto)) } else { args = append(args, fmt.Sprintf("--publish=%d/%s", listener.Port, proto)) } } } args = append(args, "--publish-all") // Construct the multi-step command var startupCmd strings.Builder startupCmd.WriteString(fmt.Sprintf("echo -en '%s' > %s && ", dynamicFilesystemConfig, proxyConfigPath)) startupCmd.WriteString(fmt.Sprintf("while true; do envoy -c %s && break; sleep 1; done", proxyConfigPath)) args = append(args, config.DefaultConfig.ProxyImage) cmd := []string{"bash", "-c", startupCmd.String()} args = append(args, cmd...) klog.V(2).Infof("creating gateway with parameters: %v", args) err = container.Create(name, args) if err != nil { return fmt.Errorf("failed to create containers %s %v: %w", name, args, err) } return nil } ================================================ FILE: pkg/gateway/envoy_test.go ================================================ package gateway import "testing" func TestGenerateEnvoyConfigTable(t *testing.T) { tests := []struct { name string configData *configData expectedConfig string wantErr bool }{ { name: "Default Configuration", configData: &configData{ Cluster: "test-cluster", ID: "test-id", AdminPort: 9000, ControlPlaneAddress: "192.168.1.10", ControlPlanePort: 8080, }, expectedConfig: `node: cluster: test-cluster id: test-id dynamic_resources: ads_config: api_type: GRPC grpc_services: - envoy_grpc: cluster_name: xds_cluster cds_config: ads: {} lds_config: ads: {} static_resources: clusters: - type: STRICT_DNS typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions explicit_http_config: http2_protocol_options: {} name: xds_cluster load_assignment: cluster_name: xds_cluster endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 192.168.1.10 port_value: 8080 - endpoint: address: socket_address: address: host.docker.internal port_value: 8080 - endpoint: address: socket_address: address: host.lima.internal port_value: 8080 admin: access_log_path: /dev/stdout address: socket_address: address: 0.0.0.0 port_value: 9000 `, wantErr: false, }, { name: "Different Ports and Addresses", configData: &configData{ Cluster: "another-cluster", ID: "instance-01", AdminPort: 12345, ControlPlaneAddress: "10.0.1.5", ControlPlanePort: 50051, }, expectedConfig: `node: cluster: another-cluster id: instance-01 dynamic_resources: ads_config: api_type: GRPC grpc_services: - envoy_grpc: cluster_name: xds_cluster cds_config: ads: {} lds_config: ads: {} static_resources: clusters: - type: STRICT_DNS typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions explicit_http_config: http2_protocol_options: {} name: xds_cluster load_assignment: cluster_name: xds_cluster endpoints: - lb_endpoints: - endpoint: address: socket_address: address: 10.0.1.5 port_value: 50051 - endpoint: address: socket_address: address: host.docker.internal port_value: 50051 - endpoint: address: socket_address: address: host.lima.internal port_value: 50051 admin: access_log_path: /dev/stdout address: socket_address: address: 0.0.0.0 port_value: 12345 `, wantErr: false, }, { name: "Empty Cluster and ID", configData: &configData{ Cluster: "", ID: "", AdminPort: 80, ControlPlaneAddress: "localhost", ControlPlanePort: 8080, }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { config, err := generateEnvoyConfig(tt.configData) if (err != nil) != tt.wantErr { t.Errorf("generateEnvoyConfig() error = %v, wantErr %v", err, tt.wantErr) return } if config != tt.expectedConfig { t.Errorf("generateEnvoyConfig() got = \n%v\nwant = \n%v", config, tt.expectedConfig) } }) } } ================================================ FILE: pkg/gateway/gateway.go ================================================ package gateway import ( "context" "errors" "fmt" "net" "reflect" "strings" "time" "google.golang.org/protobuf/types/known/durationpb" clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" endpointv3 "github.com/envoyproxy/go-control-plane/envoy/config/endpoint/v3" listenerv3 "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" typev3 "github.com/envoyproxy/go-control-plane/envoy/type/v3" envoyproxytypes "github.com/envoyproxy/go-control-plane/pkg/cache/types" cachev3 "github.com/envoyproxy/go-control-plane/pkg/cache/v3" resourcev3 "github.com/envoyproxy/go-control-plane/pkg/resource/v3" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/conversion" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/retry" "k8s.io/klog/v2" "k8s.io/utils/ptr" "sigs.k8s.io/cloud-provider-kind/pkg/container" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" ) var ( CloudProviderSupportedKinds = sets.New[gatewayv1.Kind]( "HTTPRoute", // "GRPCRoute", ) ) func (c *Controller) syncGateway(ctx context.Context, key string) error { startTime := time.Now() defer func() { klog.V(2).Infof("Finished syncing gateway %q (%v)", key, time.Since(startTime)) }() namespace, name, err := cache.SplitMetaNamespaceKey(key) if err != nil { return err } gw, err := c.gatewayLister.Gateways(namespace).Get(name) if apierrors.IsNotFound(err) { klog.V(2).Infof("Gateway %s has been deleted, cleaning up resources", key) return c.deleteGatewayResources(ctx, name, namespace) } if err != nil { return fmt.Errorf("failed to get gateway %s: %w", key, err) } if gw.Spec.GatewayClassName != GWClassName { klog.V(2).Infof("Gateway %s is not for this controller, ignoring", key) return nil } containerName := gatewayName(c.clusterName, namespace, name) klog.Infof("Syncing Gateway %s, container %s", key, containerName) err = c.ensureGatewayContainer(ctx, gw) if err != nil { return fmt.Errorf("failed to ensure gateway container %s: %w", containerName, err) } newGw := gw.DeepCopy() ipv4, ipv6, err := container.IPs(containerName) if err != nil { if strings.Contains(err.Error(), "failed to get container details") { return err } return err } newGw.Status.Addresses = []gatewayv1.GatewayStatusAddress{} if net.ParseIP(ipv4) != nil { newGw.Status.Addresses = append(newGw.Status.Addresses, gatewayv1.GatewayStatusAddress{ Type: ptr.To(gatewayv1.IPAddressType), Value: ipv4, }) } if net.ParseIP(ipv6) != nil { newGw.Status.Addresses = append(newGw.Status.Addresses, gatewayv1.GatewayStatusAddress{ Type: ptr.To(gatewayv1.IPAddressType), Value: ipv6, }) } // Get the desired state envoyResources, listenerStatuses, httpRouteStatuses, grpcRouteStatuses := c.buildEnvoyResourcesForGateway(newGw) // Apply the desired state to the data plane (Envoy). newGw.Status.Listeners = listenerStatuses err = c.UpdateXDSServer(ctx, containerName, envoyResources) // forward traffic from the host on Mac and Windows if c.tunnelManager != nil { err := c.tunnelManager.SetupTunnels(containerName) if err != nil { klog.Errorf("failed to set up tunnels for gateway %s: %v", key, err) } } // Calculate and set the Gateway's own status conditions based on the build results. setGatewayConditions(newGw, listenerStatuses, err) if !reflect.DeepEqual(gw.Status, newGw.Status) { _, err := c.gwClient.GatewayV1().Gateways(newGw.Namespace).UpdateStatus(ctx, newGw, metav1.UpdateOptions{}) if err != nil { klog.Errorf("Failed to update gateway status: %v", err) return err } } return c.updateRouteStatuses(ctx, httpRouteStatuses, grpcRouteStatuses) } // Main State Calculation Function func (c *Controller) buildEnvoyResourcesForGateway(gateway *gatewayv1.Gateway) ( map[resourcev3.Type][]envoyproxytypes.Resource, []gatewayv1.ListenerStatus, map[types.NamespacedName][]gatewayv1.RouteParentStatus, // HTTPRoutes map[types.NamespacedName][]gatewayv1.RouteParentStatus, // GRPCRoutes ) { httpRouteStatuses := make(map[types.NamespacedName][]gatewayv1.RouteParentStatus) grpcRouteStatuses := make(map[types.NamespacedName][]gatewayv1.RouteParentStatus) routesByListener := make(map[gatewayv1.SectionName][]*gatewayv1.HTTPRoute) // Validate all HTTPRoutes against this Gateway allHTTPRoutesForGateway := c.getHTTPRoutesForGateway(gateway) for _, httpRoute := range allHTTPRoutesForGateway { key := types.NamespacedName{Name: httpRoute.Name, Namespace: httpRoute.Namespace} parentStatuses, acceptingListeners := c.validateHTTPRoute(gateway, httpRoute) // Store the definitive status for the route. if len(parentStatuses) > 0 { httpRouteStatuses[key] = parentStatuses } // If the route was accepted, associate it with the listeners that accepted it. if len(acceptingListeners) > 0 { // Associate the accepted route with the listeners that will handle it. // Use a set to prevent adding a route multiple times to the same listener. processedListeners := make(map[gatewayv1.SectionName]bool) for _, listener := range acceptingListeners { if _, ok := processedListeners[listener.Name]; !ok { routesByListener[listener.Name] = append(routesByListener[listener.Name], httpRoute) processedListeners[listener.Name] = true } } } } // Build Envoy config using only the pre-validated and accepted routes envoyRoutes := []envoyproxytypes.Resource{} envoyClusters := make(map[string]envoyproxytypes.Resource) allListenerStatuses := make(map[gatewayv1.SectionName]gatewayv1.ListenerStatus) // Aggregate Listeners by Port listenersByPort := make(map[gatewayv1.PortNumber][]gatewayv1.Listener) for _, listener := range gateway.Spec.Listeners { listenersByPort[listener.Port] = append(listenersByPort[listener.Port], listener) } // validate listeners that may reuse the same port listenerValidationConditions := c.validateListeners(gateway) finalEnvoyListeners := []envoyproxytypes.Resource{} // Process Listeners by Port for port, listeners := range listenersByPort { // This slice will hold the filter chains. var filterChains []*listenerv3.FilterChain // Prepare to collect ALL virtual hosts for this port into a single list. virtualHostsForPort := make(map[string]*routev3.VirtualHost) routeName := fmt.Sprintf("route-%d", port) // All these listeners have the same port for _, listener := range listeners { var attachedRoutes int32 listenerStatus := gatewayv1.ListenerStatus{ Name: gatewayv1.SectionName(listener.Name), SupportedKinds: []gatewayv1.RouteGroupKind{}, Conditions: []metav1.Condition{}, AttachedRoutes: 0, } // Find old status to preserve condition timestamps for _, oldStatus := range gateway.Status.Listeners { if oldStatus.Name == listener.Name { listenerStatus.Conditions = oldStatus.Conditions // Copy old conditions break } } // Apply pre-calculated validation conditions if validationConds, ok := listenerValidationConditions[listener.Name]; ok { for _, cond := range validationConds { meta.SetStatusCondition(&listenerStatus.Conditions, cond) } } supportedKinds, allKindsValid := getSupportedKinds(listener) listenerStatus.SupportedKinds = supportedKinds if !allKindsValid { meta.SetStatusCondition(&listenerStatus.Conditions, metav1.Condition{ Type: string(gatewayv1.ListenerConditionResolvedRefs), Status: metav1.ConditionFalse, Reason: string(gatewayv1.ListenerReasonInvalidRouteKinds), Message: "Invalid route kinds specified in allowedRoutes", ObservedGeneration: gateway.Generation, }) allListenerStatuses[listener.Name] = listenerStatus continue // Stop processing this invalid listener } isConflicted := meta.IsStatusConditionTrue(listenerStatus.Conditions, string(gatewayv1.ListenerConditionConflicted)) // If the listener is conflicted set its status and skip Envoy config generation. if isConflicted { allListenerStatuses[listener.Name] = listenerStatus continue } // If there are not references issues then set it to tru if !meta.IsStatusConditionFalse(listenerStatus.Conditions, string(gatewayv1.ListenerConditionResolvedRefs)) { meta.SetStatusCondition(&listenerStatus.Conditions, metav1.Condition{ Type: string(gatewayv1.ListenerConditionResolvedRefs), Status: metav1.ConditionTrue, Reason: string(gatewayv1.ListenerReasonResolvedRefs), Message: "All references resolved", ObservedGeneration: gateway.Generation, }) } switch listener.Protocol { case gatewayv1.HTTPProtocolType, gatewayv1.HTTPSProtocolType: // Process HTTPRoutes // Get the routes that were pre-validated for this specific listener. for _, httpRoute := range routesByListener[listener.Name] { routes, validBackendRefs, resolvedRefsCondition := translateHTTPRouteToEnvoyRoutes(httpRoute, c.serviceLister, c.referenceGrantLister) key := types.NamespacedName{Name: httpRoute.Name, Namespace: httpRoute.Namespace} currentParentStatuses := httpRouteStatuses[key] for i := range currentParentStatuses { // Only add the ResolvedRefs condition if the parent was Accepted. if meta.IsStatusConditionTrue(currentParentStatuses[i].Conditions, string(gatewayv1.RouteConditionAccepted)) { meta.SetStatusCondition(¤tParentStatuses[i].Conditions, resolvedRefsCondition) } } httpRouteStatuses[key] = currentParentStatuses // Create the necessary Envoy Cluster resources from the valid backends. for _, backendRef := range validBackendRefs { cluster, err := c.translateBackendRefToCluster(httpRoute.Namespace, backendRef) if err == nil && cluster != nil { if _, exists := envoyClusters[cluster.Name]; !exists { envoyClusters[cluster.Name] = cluster } } } // Aggregate Envoy routes into VirtualHosts. if routes != nil { attachedRoutes++ // Get the domain for this listener's VirtualHost. vhostDomains := getIntersectingHostnames(listener, httpRoute.Spec.Hostnames) for _, domain := range vhostDomains { vh, ok := virtualHostsForPort[domain] if !ok { vh = &routev3.VirtualHost{ Name: fmt.Sprintf("%s-vh-%d-%s", gateway.Name, port, domain), Domains: []string{domain}, } virtualHostsForPort[domain] = vh } vh.Routes = append(vh.Routes, routes...) klog.V(4).Infof("created VirtualHost %s for listener %s with domain %s", vh.Name, listener. Name, domain) if klog.V(4).Enabled() { for _, route := range routes { klog.Infof("adding route %s to VirtualHost %s", route.Name, vh.Name) } } } } } // TODO: Process GRPCRoutes default: klog.Warningf("Unsupported listener protocol for route processing: %s", listener.Protocol) } vhSlice := make([]*routev3.VirtualHost, 0, len(virtualHostsForPort)) for _, vh := range virtualHostsForPort { vhSlice = append(vhSlice, vh) } filterChain, err := c.translateListenerToFilterChain(gateway, listener, vhSlice, routeName) if err != nil { meta.SetStatusCondition(&listenerStatus.Conditions, metav1.Condition{ Type: string(gatewayv1.ListenerConditionProgrammed), Status: metav1.ConditionFalse, Reason: string(gatewayv1.ListenerReasonInvalid), Message: fmt.Sprintf("Failed to program listener: %v", err), ObservedGeneration: gateway.Generation, }) } else { meta.SetStatusCondition(&listenerStatus.Conditions, metav1.Condition{ Type: string(gatewayv1.ListenerConditionProgrammed), Status: metav1.ConditionTrue, Reason: string(gatewayv1.ListenerReasonProgrammed), Message: "Listener is programmed", ObservedGeneration: gateway.Generation, }) filterChains = append(filterChains, filterChain) } listenerStatus.AttachedRoutes = attachedRoutes meta.SetStatusCondition(&listenerStatus.Conditions, metav1.Condition{ Type: string(gatewayv1.ListenerConditionAccepted), Status: metav1.ConditionTrue, Reason: string(gatewayv1.ListenerReasonAccepted), Message: "Listener is valid", ObservedGeneration: gateway.Generation, }) allListenerStatuses[listener.Name] = listenerStatus } allVirtualHosts := make([]*routev3.VirtualHost, 0, len(virtualHostsForPort)) for _, vh := range virtualHostsForPort { sortRoutes(vh.Routes) allVirtualHosts = append(allVirtualHosts, vh) } // now aggregate all the listeners on the same port routeConfig := &routev3.RouteConfiguration{ Name: routeName, VirtualHosts: allVirtualHosts, IgnorePortInHostMatching: true, // tricky to figure out thanks to howardjohn } envoyRoutes = append(envoyRoutes, routeConfig) if len(filterChains) > 0 { envoyListener := &listenerv3.Listener{ Name: fmt.Sprintf("listener-%d", port), Address: createEnvoyAddress(uint32(port)), FilterChains: filterChains, ListenerFilters: createListenerFilters(), } // If this is plain HTTP, we must now create exactly ONE default filter chain. // Use first listener as a template // For HTTPS, we create one filter chain per listener because they have unique // SNI matches and TLS settings. if listeners[0].Protocol == gatewayv1.HTTPProtocolType { filterChain, _ := c.translateListenerToFilterChain(gateway, listeners[0], allVirtualHosts, routeName) envoyListener.FilterChains = []*listenerv3.FilterChain{filterChain} } finalEnvoyListeners = append(finalEnvoyListeners, envoyListener) } } clustersSlice := make([]envoyproxytypes.Resource, 0, len(envoyClusters)) for _, cluster := range envoyClusters { clustersSlice = append(clustersSlice, cluster) } orderedStatuses := make([]gatewayv1.ListenerStatus, len(gateway.Spec.Listeners)) for i, listener := range gateway.Spec.Listeners { orderedStatuses[i] = allListenerStatuses[listener.Name] } return map[resourcev3.Type][]envoyproxytypes.Resource{ resourcev3.ListenerType: finalEnvoyListeners, resourcev3.RouteType: envoyRoutes, resourcev3.ClusterType: clustersSlice, }, orderedStatuses, httpRouteStatuses, grpcRouteStatuses } func getSupportedKinds(listener gatewayv1.Listener) ([]gatewayv1.RouteGroupKind, bool) { supportedKinds := []gatewayv1.RouteGroupKind{} allKindsValid := true groupName := gatewayv1.Group(gatewayv1.GroupName) if listener.AllowedRoutes != nil && len(listener.AllowedRoutes.Kinds) > 0 { for _, kind := range listener.AllowedRoutes.Kinds { if (kind.Group == nil || *kind.Group == groupName) && CloudProviderSupportedKinds.Has(kind.Kind) { supportedKinds = append(supportedKinds, gatewayv1.RouteGroupKind{ Group: &groupName, Kind: kind.Kind, }) } else { allKindsValid = false } } } else if listener.Protocol == gatewayv1.HTTPProtocolType || listener.Protocol == gatewayv1.HTTPSProtocolType { for _, kind := range CloudProviderSupportedKinds.UnsortedList() { supportedKinds = append(supportedKinds, gatewayv1.RouteGroupKind{ Group: &groupName, Kind: kind, }, ) } } return supportedKinds, allKindsValid } var semanticIgnoreLastTransitionTime = conversion.EqualitiesOrDie( func(a, b metav1.Condition) bool { a.LastTransitionTime = metav1.Time{} b.LastTransitionTime = metav1.Time{} return a == b }, ) func (c *Controller) updateRouteStatuses( ctx context.Context, httpRouteStatuses map[types.NamespacedName][]gatewayv1.RouteParentStatus, grpcRouteStatuses map[types.NamespacedName][]gatewayv1.RouteParentStatus, ) error { var errGroup []error // --- Process HTTPRoutes --- for key, desiredParentStatuses := range httpRouteStatuses { err := retry.RetryOnConflict(retry.DefaultRetry, func() error { // GET the latest version of the route from the cache. originalRoute, err := c.httprouteLister.HTTPRoutes(key.Namespace).Get(key.Name) if apierrors.IsNotFound(err) { // Route has been deleted, nothing to do. return nil } else if err != nil { return err } // Create a mutable copy to work with. routeToUpdate := originalRoute.DeepCopy() routeToUpdate.Status.Parents = desiredParentStatuses // Only make an API call if the status has actually changed. if !semanticIgnoreLastTransitionTime.DeepEqual(originalRoute.Status, routeToUpdate.Status) { _, updateErr := c.gwClient.GatewayV1().HTTPRoutes(routeToUpdate.Namespace).UpdateStatus(ctx, routeToUpdate, metav1.UpdateOptions{}) return updateErr } // Status is already up-to-date. return nil }) if err != nil { errGroup = append(errGroup, fmt.Errorf("failed to update status for HTTPRoute %s: %w", key, err)) } } // TODO: Process GRPCRoutes (repeat the same logic) return errors.Join(errGroup...) } // getHTTPRoutesForGateway returns all HTTPRoutes that have a ParentRef pointing to the specified Gateway. func (c *Controller) getHTTPRoutesForGateway(gw *gatewayv1.Gateway) []*gatewayv1.HTTPRoute { var matchingRoutes []*gatewayv1.HTTPRoute allRoutes, err := c.httprouteLister.List(labels.Everything()) if err != nil { klog.Errorf("failed to list HTTPRoutes: %v", err) return matchingRoutes } for _, route := range allRoutes { for _, parentRef := range route.Spec.ParentRefs { // Check if the ParentRef targets the Gateway, defaulting to the route's namespace. refNamespace := route.Namespace if parentRef.Namespace != nil { refNamespace = string(*parentRef.Namespace) } if parentRef.Name == gatewayv1.ObjectName(gw.Name) && refNamespace == gw.Namespace { matchingRoutes = append(matchingRoutes, route) break // Found a matching ref for this gateway, no need to check others. } } } return matchingRoutes } // validateHTTPRoute is the definitive validation function. It iterates through all // parentRefs of an HTTPRoute and generates a complete RouteParentStatus for each one // that targets the specified Gateway. It also returns a slice of all listeners // that ended up accepting the route. func (c *Controller) validateHTTPRoute( gateway *gatewayv1.Gateway, httpRoute *gatewayv1.HTTPRoute, ) ([]gatewayv1.RouteParentStatus, []gatewayv1.Listener) { var parentStatuses []gatewayv1.RouteParentStatus // Use a map to collect a unique set of listeners that accepted the route. acceptedListenerSet := make(map[gatewayv1.SectionName]gatewayv1.Listener) // --- Determine the ResolvedRefs status for the entire Route first. --- // This is a property of the route itself, independent of any parent. resolvedRefsCondition := metav1.Condition{ Type: string(gatewayv1.RouteConditionResolvedRefs), ObservedGeneration: httpRoute.Generation, } if c.areBackendsValid(httpRoute) { resolvedRefsCondition.Status = metav1.ConditionTrue resolvedRefsCondition.Reason = string(gatewayv1.RouteReasonResolvedRefs) resolvedRefsCondition.Message = "All backend references have been resolved." } else { resolvedRefsCondition.Status = metav1.ConditionFalse resolvedRefsCondition.Reason = string(gatewayv1.RouteReasonBackendNotFound) resolvedRefsCondition.Message = "One or more backend references could not be found." } // --- Iterate over EACH ParentRef in the HTTPRoute --- for _, parentRef := range httpRoute.Spec.ParentRefs { // We only care about refs that target our current Gateway. refNamespace := httpRoute.Namespace if parentRef.Namespace != nil { refNamespace = string(*parentRef.Namespace) } if parentRef.Name != gatewayv1.ObjectName(gateway.Name) || refNamespace != gateway.Namespace { continue // This ref is for another Gateway. } // This ref targets our Gateway. We MUST generate a status for it. var listenersForThisRef []gatewayv1.Listener rejectionReason := gatewayv1.RouteReasonNoMatchingParent // --- Find all listeners on the Gateway that match this specific parentRef --- for _, listener := range gateway.Spec.Listeners { sectionNameMatches := (parentRef.SectionName == nil) || (*parentRef.SectionName == listener.Name) portMatches := (parentRef.Port == nil) || (*parentRef.Port == listener.Port) if sectionNameMatches && portMatches { // The listener matches the ref. Now check if the listener's policy (e.g., hostname) allows it. if !isAllowedByListener(gateway, listener, httpRoute, c.namespaceLister) { rejectionReason = gatewayv1.RouteReasonNotAllowedByListeners continue } if !isAllowedByHostname(listener, httpRoute) { rejectionReason = gatewayv1.RouteReasonNoMatchingListenerHostname continue } listenersForThisRef = append(listenersForThisRef, listener) } } // --- Build the final status for this ParentRef --- status := gatewayv1.RouteParentStatus{ ParentRef: parentRef, ControllerName: controllerName, Conditions: []metav1.Condition{}, } // Create the 'Accepted' condition based on the listener validation. acceptedCondition := metav1.Condition{ Type: string(gatewayv1.RouteConditionAccepted), ObservedGeneration: httpRoute.Generation, } if len(listenersForThisRef) == 0 { acceptedCondition.Status = metav1.ConditionFalse acceptedCondition.Reason = string(rejectionReason) acceptedCondition.Message = "No listener matched the parentRef." if rejectionReason == gatewayv1.RouteReasonNotAllowedByListeners { acceptedCondition.Message = "Route is not allowed by a listener's policy." } else { acceptedCondition.Message = "The route's hostnames do not match any listener hostnames." } } else { acceptedCondition.Status = metav1.ConditionTrue acceptedCondition.Reason = string(gatewayv1.RouteReasonAccepted) acceptedCondition.Message = "Route is accepted." for _, l := range listenersForThisRef { acceptedListenerSet[l.Name] = l } } // --- 4. Combine the two independent conditions into the final status. --- meta.SetStatusCondition(&status.Conditions, acceptedCondition) meta.SetStatusCondition(&status.Conditions, resolvedRefsCondition) parentStatuses = append(parentStatuses, status) } var allAcceptingListeners []gatewayv1.Listener for _, l := range acceptedListenerSet { allAcceptingListeners = append(allAcceptingListeners, l) } return parentStatuses, allAcceptingListeners } // areBackendsValid is a helper extracted from the original validate function. func (c *Controller) areBackendsValid(httpRoute *gatewayv1.HTTPRoute) bool { for _, rule := range httpRoute.Spec.Rules { if ruleHasRedirectFilter(rule) { continue } for _, backendRef := range rule.BackendRefs { ns := httpRoute.Namespace if backendRef.Namespace != nil { ns = string(*backendRef.Namespace) } if _, err := c.serviceLister.Services(ns).Get(string(backendRef.Name)); err != nil { return false } } } return true } // Helper to check for redirect filters func ruleHasRedirectFilter(rule gatewayv1.HTTPRouteRule) bool { for _, filter := range rule.Filters { if filter.Type == gatewayv1.HTTPRouteFilterRequestRedirect { return true } } return false } func (c *Controller) translateBackendRefToCluster(defaultNamespace string, backendRef gatewayv1.BackendRef) (*clusterv3.Cluster, error) { ns := defaultNamespace if backendRef.Namespace != nil { ns = string(*backendRef.Namespace) } service, err := c.serviceLister.Services(ns).Get(string(backendRef.Name)) if err != nil { return nil, fmt.Errorf("could not find service %s/%s: %w", ns, backendRef.Name, err) } clusterName, err := backendRefToClusterName(defaultNamespace, backendRef) if err != nil { return nil, err } // Create the base cluster configuration. cluster := &clusterv3.Cluster{ Name: clusterName, ConnectTimeout: durationpb.New(5 * time.Second), CommonLbConfig: &clusterv3.Cluster_CommonLbConfig{ HealthyPanicThreshold: &typev3.Percent{ Value: 0, }, }, } if service.Spec.ClusterIP == corev1.ClusterIPNone { // Use STRICT_DNS discovery and the service's FQDN. cluster.ClusterDiscoveryType = &clusterv3.Cluster_Type{Type: clusterv3.Cluster_STRICT_DNS} // Construct the FQDN for the service. fqdn := fmt.Sprintf("%s.%s.svc.cluster.local", service.Name, service.Namespace) // Get the port of the endpoints. targetPort := 0 for _, port := range service.Spec.Ports { if port.Port == int32(*backendRef.Port) { targetPort = int(port.TargetPort.IntVal) break } } if targetPort == 0 { return nil, fmt.Errorf("could not find port %d in service %s/%s", *backendRef.Port, service.Namespace, service.Name) } cluster.LoadAssignment = createClusterLoadAssignment(clusterName, fqdn, uint32(targetPort)) } else { // Use STATIC discovery with the service's ClusterIP. cluster.ClusterDiscoveryType = &clusterv3.Cluster_Type{Type: clusterv3.Cluster_STATIC} cluster.LoadAssignment = createClusterLoadAssignment(clusterName, service.Spec.ClusterIP, uint32(*backendRef.Port)) } return cluster, nil } func (c *Controller) deleteGatewayResources(ctx context.Context, name, namespace string) error { klog.Infof("Deleting resources for Gateway: %s/%s", namespace, name) containerName := gatewayName(c.clusterName, namespace, name) c.xdsVersion.Add(1) version := fmt.Sprintf("%d", c.xdsVersion.Load()) snapshot, err := cachev3.NewSnapshot(version, map[resourcev3.Type][]envoyproxytypes.Resource{ resourcev3.ListenerType: {}, resourcev3.RouteType: {}, resourcev3.ClusterType: {}, resourcev3.EndpointType: {}, }) if err != nil { return fmt.Errorf("failed to create empty snapshot for deleted gateway %s: %w", name, err) } if err := c.xdscache.SetSnapshot(ctx, containerName, snapshot); err != nil { return fmt.Errorf("failed to set empty snapshot for deleted gateway %s: %w", name, err) } if c.tunnelManager != nil { err := c.tunnelManager.RemoveTunnels(containerName) if err != nil { klog.Errorf("failed to remove tunnels for deleted gateway %s: %v", name, err) } } if err := container.Delete(containerName); err != nil { return fmt.Errorf("failed to delete container for gateway %s: %v", name, err) } klog.Infof("Successfully cleared resources for deleted Gateway: %s", name) return nil } func createClusterLoadAssignment(clusterName, serviceHost string, servicePort uint32) *endpointv3.ClusterLoadAssignment { return &endpointv3.ClusterLoadAssignment{ ClusterName: clusterName, Endpoints: []*endpointv3.LocalityLbEndpoints{ { LbEndpoints: []*endpointv3.LbEndpoint{ { HostIdentifier: &endpointv3.LbEndpoint_Endpoint{ Endpoint: &endpointv3.Endpoint{ Address: &corev3.Address{ Address: &corev3.Address_SocketAddress{ SocketAddress: &corev3.SocketAddress{ Address: serviceHost, PortSpecifier: &corev3.SocketAddress_PortValue{ PortValue: servicePort, }, }, }, }, }, }, }, }, }, }, } } // getRouteHostnames determines the effective hostnames for a route. func getRouteHostnames(routeHostnames []gatewayv1.Hostname, listener gatewayv1.Listener) []string { if len(routeHostnames) > 0 { hostnames := make([]string, len(routeHostnames)) for i, h := range routeHostnames { hostnames[i] = string(h) } return hostnames } if listener.Hostname != nil && *listener.Hostname != "" { return []string{string(*listener.Hostname)} } return []string{"*"} } // setGatewayConditions calculates and sets the final status conditions for the Gateway // based on the results of the reconciliation loop. func setGatewayConditions(newGw *gatewayv1.Gateway, listenerStatuses []gatewayv1.ListenerStatus, err error) { programmedCondition := metav1.Condition{ Type: string(gatewayv1.GatewayConditionProgrammed), ObservedGeneration: newGw.Generation, } if err != nil { // If the Envoy update fails, the Gateway is not programmed. programmedCondition.Status = metav1.ConditionFalse programmedCondition.Reason = "ReconciliationError" programmedCondition.Message = fmt.Sprintf("Failed to program envoy config: %s", err.Error()) } else { // If the Envoy update succeeds, check if all individual listeners were programmed. listenersProgrammed := 0 for _, listenerStatus := range listenerStatuses { if meta.IsStatusConditionTrue(listenerStatus.Conditions, string(gatewayv1.ListenerConditionProgrammed)) { listenersProgrammed++ } } if listenersProgrammed == len(listenerStatuses) { // The Gateway is only fully programmed if all listeners are programmed. programmedCondition.Status = metav1.ConditionTrue programmedCondition.Reason = string(gatewayv1.GatewayReasonProgrammed) programmedCondition.Message = "Envoy configuration updated successfully" } else { // If any listener failed, the Gateway as a whole is not fully programmed. programmedCondition.Status = metav1.ConditionFalse programmedCondition.Reason = "ListenersNotProgrammed" programmedCondition.Message = fmt.Sprintf("%d out of %d listeners failed to be programmed", listenersProgrammed, len(listenerStatuses)) } } meta.SetStatusCondition(&newGw.Status.Conditions, programmedCondition) meta.SetStatusCondition(&newGw.Status.Conditions, metav1.Condition{ Type: string(gatewayv1.GatewayConditionAccepted), Status: metav1.ConditionTrue, Reason: string(gatewayv1.GatewayReasonAccepted), Message: "Gateway is accepted", ObservedGeneration: newGw.Generation, }) } ================================================ FILE: pkg/gateway/gateway_test.go ================================================ package gateway import ( "reflect" "testing" "k8s.io/utils/ptr" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" ) func Test_getSupportedKinds(t *testing.T) { type args struct { listener gatewayv1.Listener } group := gatewayv1.Group(gatewayv1.GroupName) tests := []struct { name string args args want []gatewayv1.RouteGroupKind want1 bool }{ { name: "default kinds for HTTP protocol", args: args{ listener: gatewayv1.Listener{ Protocol: gatewayv1.HTTPProtocolType, }, }, want: []gatewayv1.RouteGroupKind{ {Group: &group, Kind: "HTTPRoute"}, }, want1: true, }, { name: "default kinds for HTTPS protocol", args: args{ listener: gatewayv1.Listener{ Protocol: gatewayv1.HTTPSProtocolType, }, }, want: []gatewayv1.RouteGroupKind{ {Group: &group, Kind: "HTTPRoute"}, }, want1: true, }, { name: "no default kinds for other protocols", args: args{ listener: gatewayv1.Listener{ Protocol: gatewayv1.TCPProtocolType, }, }, want: []gatewayv1.RouteGroupKind{}, want1: true, }, { name: "user defined kinds", args: args{ listener: gatewayv1.Listener{ Protocol: gatewayv1.HTTPProtocolType, AllowedRoutes: &gatewayv1.AllowedRoutes{ Kinds: []gatewayv1.RouteGroupKind{ {Kind: "HTTPRoute"}, }, }, }, }, want: []gatewayv1.RouteGroupKind{ {Group: &group, Kind: "HTTPRoute"}, }, want1: true, }, { name: "user defined kinds with invalid kind", args: args{ listener: gatewayv1.Listener{ Protocol: gatewayv1.HTTPProtocolType, AllowedRoutes: &gatewayv1.AllowedRoutes{ Kinds: []gatewayv1.RouteGroupKind{ {Kind: "HTTPRoute"}, {Kind: "TCPRoute"}, }, }, }, }, want: []gatewayv1.RouteGroupKind{ {Group: &group, Kind: "HTTPRoute"}, }, want1: false, }, { name: "user defined kinds with invalid group", args: args{ listener: gatewayv1.Listener{ Protocol: gatewayv1.HTTPProtocolType, AllowedRoutes: &gatewayv1.AllowedRoutes{ Kinds: []gatewayv1.RouteGroupKind{ {Group: ptr.To(gatewayv1.Group("foo")), Kind: "HTTPRoute"}, }, }, }, }, want: []gatewayv1.RouteGroupKind{}, want1: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, got1 := getSupportedKinds(tt.args.listener) if !reflect.DeepEqual(got, tt.want) { t.Errorf("getSupportedKinds() got = %v, want %v", got, tt.want) } if got1 != tt.want1 { t.Errorf("getSupportedKinds() got1 = %v, want %v", got1, tt.want1) } }) } } ================================================ FILE: pkg/gateway/grpcroute.go ================================================ package gateway import ( "errors" "fmt" routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" "google.golang.org/protobuf/types/known/wrapperspb" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" corev1listers "k8s.io/client-go/listers/core/v1" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" ) // translateGRPCRouteToEnvoyRoutes translates a full GRPCRoute into a slice of Envoy Routes. // It is a pure function that returns the result, a list of required cluster names, and a final // status condition without mutating any arguments. func translateGRPCRouteToEnvoyRoutes( grpcRoute *gatewayv1.GRPCRoute, serviceLister corev1listers.ServiceLister, ) ([]*routev3.Route, []gatewayv1.BackendRef, metav1.Condition) { var envoyRoutes []*routev3.Route var validBackendRefs []gatewayv1.BackendRef isOverallSuccess := true var finalFailureMessage string var finalFailureReason gatewayv1.RouteConditionReason for ruleIndex, rule := range grpcRoute.Spec.Rules { // Attempt to build the forwarding action. This will return an error if backends are invalid. routeAction, backendRefs, err := buildGRPCRouteAction( grpcRoute.Namespace, rule.BackendRefs, serviceLister, ) validBackendRefs = append(validBackendRefs, backendRefs...) // This helper function creates the appropriate Envoy route (forward or direct response) // based on the success of the backend resolution. buildRoutesForRule := func(match gatewayv1.GRPCRouteMatch, matchIndex int) (*routev3.Route, metav1.Condition) { routeMatch, matchCondition := translateGRPCRouteMatch(match, grpcRoute.Generation) if matchCondition.Status == metav1.ConditionFalse { return nil, matchCondition } envoyRoute := &routev3.Route{ Name: fmt.Sprintf("%s-%s-rule%d-match%d", grpcRoute.Namespace, grpcRoute.Name, ruleIndex, matchIndex), Match: routeMatch, } var controllerErr *ControllerError if errors.As(err, &controllerErr) { // Backend resolution failed. Create a DirectResponse and a False condition. if isOverallSuccess { // Capture first failure details isOverallSuccess = false finalFailureMessage = controllerErr.Message finalFailureReason = gatewayv1.RouteConditionReason(controllerErr.Reason) } envoyRoute.Action = &routev3.Route_DirectResponse{ DirectResponse: &routev3.DirectResponseAction{Status: 500}, } } else { // Backend resolution succeeded. Create a normal forwarding route. envoyRoute.Action = &routev3.Route_Route{ Route: routeAction, } } return envoyRoute, createSuccessCondition(grpcRoute.Generation) } // GRPCRoute requires at least one match per rule. if len(rule.Matches) == 0 { if isOverallSuccess { isOverallSuccess = false finalFailureMessage = "GRPCRoute rule must have at least one match" finalFailureReason = gatewayv1.RouteReasonBackendNotFound } } else { for matchIndex, match := range rule.Matches { envoyRoute, cond := buildRoutesForRule(match, matchIndex) if cond.Status == metav1.ConditionFalse { return nil, nil, cond } envoyRoutes = append(envoyRoutes, envoyRoute) } } } if isOverallSuccess { return envoyRoutes, validBackendRefs, createSuccessCondition(grpcRoute.Generation) } return envoyRoutes, validBackendRefs, createFailureCondition(finalFailureReason, finalFailureMessage, grpcRoute.Generation) } // buildGRPCRouteAction attempts to create a forwarding RouteAction and returns a structured error on failure. func buildGRPCRouteAction( namespace string, backendRefs []gatewayv1.GRPCBackendRef, serviceLister corev1listers.ServiceLister, ) (*routev3.RouteAction, []gatewayv1.BackendRef, error) { weightedClusters := &routev3.WeightedCluster{} var validBackendRefs []gatewayv1.BackendRef for _, backendRef := range backendRefs { ns := namespace if backendRef.Namespace != nil { ns = string(*backendRef.Namespace) } if _, err := serviceLister.Services(ns).Get(string(backendRef.Name)); err != nil { return nil, nil, &ControllerError{ Reason: string(gatewayv1.RouteReasonBackendNotFound), Message: "backend not found", } } // GRPCRoute uses BackendObjectReference which is slightly different but can be converted. clusterName, err := backendRefToClusterName(namespace, backendRef.BackendRef) if err != nil { return nil, nil, err } weight := int32(1) if backendRef.Weight != nil { weight = *backendRef.Weight } if weight == 0 { continue } validBackendRefs = append(validBackendRefs, backendRef.BackendRef) weightedClusters.Clusters = append(weightedClusters.Clusters, &routev3.WeightedCluster_ClusterWeight{ Name: clusterName, Weight: &wrapperspb.UInt32Value{Value: uint32(weight)}, }) } if len(weightedClusters.Clusters) == 0 { return nil, nil, &ControllerError{Reason: string(gatewayv1.RouteReasonAccepted), Message: "no valid backends provided with a weight > 0"} } var action *routev3.RouteAction if len(weightedClusters.Clusters) == 1 { action = &routev3.RouteAction{ClusterSpecifier: &routev3.RouteAction_Cluster{Cluster: weightedClusters.Clusters[0].Name}} } else { action = &routev3.RouteAction{ClusterSpecifier: &routev3.RouteAction_WeightedClusters{WeightedClusters: weightedClusters}} } return action, validBackendRefs, nil } // translateGRPCRouteMatch translates a Gateway API GRPCRouteMatch into an Envoy RouteMatch. func translateGRPCRouteMatch(match gatewayv1.GRPCRouteMatch, generation int64) (*routev3.RouteMatch, metav1.Condition) { routeMatch := &routev3.RouteMatch{ // gRPC requests are HTTP/2 POSTs with a specific content-type. Headers: []*routev3.HeaderMatcher{ { Name: ":method", HeaderMatchSpecifier: &routev3.HeaderMatcher_ExactMatch{ExactMatch: "POST"}, }, { Name: "content-type", HeaderMatchSpecifier: &routev3.HeaderMatcher_PrefixMatch{PrefixMatch: "application/grpc"}, }, }, } // Translate gRPC method match into a :path header match. if match.Method != nil { if match.Method.Service == nil || match.Method.Method == nil { msg := "GRPCMethodMatch requires both service and method to be set" return nil, createFailureCondition(gatewayv1.RouteReasonUnsupportedValue, msg, generation) } path := fmt.Sprintf("/%s/%s", *match.Method.Service, *match.Method.Method) matchType := gatewayv1.GRPCMethodMatchExact if match.Method.Type != nil { matchType = *match.Method.Type } pathMatcher := &routev3.HeaderMatcher{Name: ":path"} switch matchType { case gatewayv1.GRPCMethodMatchExact: pathMatcher.HeaderMatchSpecifier = &routev3.HeaderMatcher_ExactMatch{ExactMatch: path} case gatewayv1.GRPCMethodMatchRegularExpression: pathMatcher.HeaderMatchSpecifier = &routev3.HeaderMatcher_SafeRegexMatch{ SafeRegexMatch: &matcherv3.RegexMatcher{ EngineType: &matcherv3.RegexMatcher_GoogleRe2{GoogleRe2: &matcherv3.RegexMatcher_GoogleRE2{}}, Regex: path, }, } default: msg := fmt.Sprintf("unsupported gRPC method match type: %s", matchType) return nil, createFailureCondition(gatewayv1.RouteReasonUnsupportedValue, msg, generation) } routeMatch.Headers = append(routeMatch.Headers, pathMatcher) } // Translate header matches. for _, headerMatch := range match.Headers { headerMatcher := &routev3.HeaderMatcher{Name: string(headerMatch.Name)} matchType := gatewayv1.GRPCHeaderMatchExact if headerMatch.Type != nil { matchType = *headerMatch.Type } switch matchType { case gatewayv1.GRPCHeaderMatchExact: // CHANGED: Use the modern direct `ExactMatch` field. headerMatcher.HeaderMatchSpecifier = &routev3.HeaderMatcher_ExactMatch{ExactMatch: headerMatch.Value} case gatewayv1.GRPCHeaderMatchRegularExpression: headerMatcher.HeaderMatchSpecifier = &routev3.HeaderMatcher_SafeRegexMatch{ SafeRegexMatch: &matcherv3.RegexMatcher{ EngineType: &matcherv3.RegexMatcher_GoogleRe2{GoogleRe2: &matcherv3.RegexMatcher_GoogleRE2{}}, Regex: headerMatch.Value, }, } default: msg := fmt.Sprintf("unsupported header match type: %s", matchType) return nil, createFailureCondition(gatewayv1.RouteReasonUnsupportedValue, msg, generation) } routeMatch.Headers = append(routeMatch.Headers, headerMatcher) } return routeMatch, createSuccessCondition(generation) } ================================================ FILE: pkg/gateway/httproute.go ================================================ package gateway import ( "errors" "fmt" "sort" "strings" corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" "google.golang.org/protobuf/types/known/wrapperspb" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" corev1listers "k8s.io/client-go/listers/core/v1" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" gatewaylistersv1 "sigs.k8s.io/gateway-api/pkg/client/listers/apis/v1" ) // translateHTTPRouteToEnvoyRoutes translates a full HTTPRoute into a slice of Envoy Routes. // It now correctly handles RequestHeaderModifier filters. func translateHTTPRouteToEnvoyRoutes( httpRoute *gatewayv1.HTTPRoute, serviceLister corev1listers.ServiceLister, referenceGrantLister gatewaylistersv1.ReferenceGrantLister, ) ([]*routev3.Route, []gatewayv1.BackendRef, metav1.Condition) { var envoyRoutes []*routev3.Route var allValidBackendRefs []gatewayv1.BackendRef overallCondition := createSuccessCondition(httpRoute.Generation) for ruleIndex, rule := range httpRoute.Spec.Rules { var redirectAction *routev3.RedirectAction var headersToAdd []*corev3.HeaderValueOption var headersToRemove []string for _, filter := range rule.Filters { if filter.Type == gatewayv1.HTTPRouteFilterRequestRedirect && filter.RequestRedirect != nil { redirect := filter.RequestRedirect redirectAction = &routev3.RedirectAction{} if redirect.Hostname != nil { redirectAction.HostRedirect = string(*redirect.Hostname) } if redirect.StatusCode != nil { switch *redirect.StatusCode { case 301: redirectAction.ResponseCode = routev3.RedirectAction_MOVED_PERMANENTLY case 302: redirectAction.ResponseCode = routev3.RedirectAction_FOUND case 303: redirectAction.ResponseCode = routev3.RedirectAction_SEE_OTHER case 307: redirectAction.ResponseCode = routev3.RedirectAction_TEMPORARY_REDIRECT case 308: redirectAction.ResponseCode = routev3.RedirectAction_PERMANENT_REDIRECT default: redirectAction.ResponseCode = routev3.RedirectAction_MOVED_PERMANENTLY } } else { // The Gateway API spec defaults to a 302 redirect. // The corresponding Envoy enum is "FOUND". redirectAction.ResponseCode = routev3.RedirectAction_FOUND } break // Only one redirect filter is allowed per rule. } if filter.Type == gatewayv1.HTTPRouteFilterRequestHeaderModifier && filter.RequestHeaderModifier != nil { // Handle "set" actions (overwrite) for _, header := range filter.RequestHeaderModifier.Set { headersToAdd = append(headersToAdd, &corev3.HeaderValueOption{ Header: &corev3.HeaderValue{ Key: string(header.Name), Value: header.Value, }, // This tells Envoy to overwrite the header if it exists. AppendAction: corev3.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD, }) } // Handle "add" actions (append) for _, header := range filter.RequestHeaderModifier.Add { headersToAdd = append(headersToAdd, &corev3.HeaderValueOption{ Header: &corev3.HeaderValue{ Key: string(header.Name), Value: header.Value, }, // This tells Envoy to append the value if the header already exists. AppendAction: corev3.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD, }) } // Handle "remove" actions headersToRemove = append(headersToRemove, filter.RequestHeaderModifier.Remove...) } } buildRoutesForRule := func(match gatewayv1.HTTPRouteMatch, matchIndex int) { routeMatch, matchCondition := translateHTTPRouteMatch(match, httpRoute.Generation) if matchCondition.Status == metav1.ConditionFalse { overallCondition = matchCondition return } envoyRoute := &routev3.Route{ Name: fmt.Sprintf("%s-%s-rule%d-match%d", httpRoute.Namespace, httpRoute.Name, ruleIndex, matchIndex), Match: routeMatch, RequestHeadersToAdd: headersToAdd, RequestHeadersToRemove: headersToRemove, } if redirectAction != nil { // If this is a redirect, set the Redirect action. No backends are needed. envoyRoute.Action = &routev3.Route_Redirect{ Redirect: redirectAction, } } else { // Attempt to build the forwarding action and get valid backends. routeAction, validBackends, err := buildHTTPRouteAction( httpRoute.Namespace, rule.BackendRefs, serviceLister, referenceGrantLister, ) var controllerErr *ControllerError if errors.As(err, &controllerErr) { overallCondition = createFailureCondition(gatewayv1.RouteConditionReason(controllerErr.Reason), controllerErr.Message, httpRoute.Generation) envoyRoute.Action = &routev3.Route_DirectResponse{ DirectResponse: &routev3.DirectResponseAction{Status: 500}, } } else { allValidBackendRefs = append(allValidBackendRefs, validBackends...) envoyRoute.Action = &routev3.Route_Route{ Route: routeAction, } } } envoyRoutes = append(envoyRoutes, envoyRoute) } if len(rule.Matches) == 0 { buildRoutesForRule(gatewayv1.HTTPRouteMatch{}, 0) } else { for matchIndex, match := range rule.Matches { buildRoutesForRule(match, matchIndex) } } } return envoyRoutes, allValidBackendRefs, overallCondition } // buildHTTPRouteAction returns an action, a list of *valid* BackendRefs, and a structured error. func buildHTTPRouteAction(namespace string, backendRefs []gatewayv1.HTTPBackendRef, serviceLister corev1listers.ServiceLister, referenceGrantLister gatewaylistersv1.ReferenceGrantLister) (*routev3.RouteAction, []gatewayv1.BackendRef, error) { weightedClusters := &routev3.WeightedCluster{} var validBackendRefs []gatewayv1.BackendRef for _, httpBackendRef := range backendRefs { backendRef := httpBackendRef.BackendRef ns := namespace if backendRef.Namespace != nil { ns = string(*backendRef.Namespace) } // If it's a cross-namespace reference, we must check for a ReferenceGrant. if ns != namespace { from := gatewayv1.ReferenceGrantFrom{ Group: gatewayv1.GroupName, Kind: "HTTPRoute", Namespace: gatewayv1.Namespace(namespace), } to := gatewayv1.ReferenceGrantTo{ Group: "", // Core group for Service Kind: "Service", Name: &backendRef.Name, } if !isCrossNamespaceRefAllowed(from, to, ns, referenceGrantLister) { // The reference is not permitted. return nil, nil, &ControllerError{ Reason: string(gatewayv1.RouteReasonRefNotPermitted), Message: "permission error", } } } if _, err := serviceLister.Services(ns).Get(string(backendRef.Name)); err != nil { return nil, nil, &ControllerError{ Reason: string(gatewayv1.RouteReasonBackendNotFound), Message: "backend not found", } } clusterName, err := backendRefToClusterName(namespace, backendRef) if err != nil { return nil, nil, err } weight := int32(1) if httpBackendRef.Weight != nil { weight = *httpBackendRef.Weight } if weight == 0 { continue } validBackendRefs = append(validBackendRefs, backendRef) weightedClusters.Clusters = append(weightedClusters.Clusters, &routev3.WeightedCluster_ClusterWeight{ Name: clusterName, Weight: &wrapperspb.UInt32Value{Value: uint32(weight)}, }) } if len(weightedClusters.Clusters) == 0 { return nil, nil, &ControllerError{Reason: string(gatewayv1.RouteReasonUnsupportedValue), Message: "no valid backends provided with a weight > 0"} } var action *routev3.RouteAction if len(weightedClusters.Clusters) == 1 { action = &routev3.RouteAction{ClusterSpecifier: &routev3.RouteAction_Cluster{Cluster: weightedClusters.Clusters[0].Name}} } else { action = &routev3.RouteAction{ClusterSpecifier: &routev3.RouteAction_WeightedClusters{WeightedClusters: weightedClusters}} } return action, validBackendRefs, nil } // translateHTTPRouteMatch translates a Gateway API HTTPRouteMatch into an Envoy RouteMatch. // It returns the result and a condition indicating success or failure. func translateHTTPRouteMatch(match gatewayv1.HTTPRouteMatch, generation int64) (*routev3.RouteMatch, metav1.Condition) { routeMatch := &routev3.RouteMatch{} if match.Path != nil { pathType := gatewayv1.PathMatchPathPrefix if match.Path.Type != nil { pathType = *match.Path.Type } if match.Path.Value == nil { msg := "path match value cannot be nil" return nil, createFailureCondition(gatewayv1.RouteReasonUnsupportedValue, msg, generation) } pathValue := *match.Path.Value switch pathType { case gatewayv1.PathMatchExact: routeMatch.PathSpecifier = &routev3.RouteMatch_Path{Path: pathValue} case gatewayv1.PathMatchPathPrefix: if pathValue == "/" { routeMatch.PathSpecifier = &routev3.RouteMatch_Prefix{Prefix: "/"} } else { path := strings.TrimSuffix(pathValue, "/") routeMatch.PathSpecifier = &routev3.RouteMatch_PathSeparatedPrefix{PathSeparatedPrefix: path} } case gatewayv1.PathMatchRegularExpression: routeMatch.PathSpecifier = &routev3.RouteMatch_SafeRegex{ SafeRegex: &matcherv3.RegexMatcher{ EngineType: &matcherv3.RegexMatcher_GoogleRe2{GoogleRe2: &matcherv3.RegexMatcher_GoogleRE2{}}, Regex: pathValue, }, } default: msg := fmt.Sprintf("unsupported path match type: %s", pathType) return nil, createFailureCondition(gatewayv1.RouteReasonUnsupportedValue, msg, generation) } } else { // As per Gateway API spec, a nil path match defaults to matching everything. routeMatch.PathSpecifier = &routev3.RouteMatch_Prefix{Prefix: "/"} } // Translate Header Matches for _, headerMatch := range match.Headers { headerMatcher := &routev3.HeaderMatcher{ Name: string(headerMatch.Name), } matchType := gatewayv1.HeaderMatchExact if headerMatch.Type != nil { matchType = *headerMatch.Type } switch matchType { case gatewayv1.HeaderMatchExact: headerMatcher.HeaderMatchSpecifier = &routev3.HeaderMatcher_StringMatch{ StringMatch: &matcherv3.StringMatcher{ MatchPattern: &matcherv3.StringMatcher_Exact{Exact: headerMatch.Value}, }, } case gatewayv1.HeaderMatchRegularExpression: headerMatcher.HeaderMatchSpecifier = &routev3.HeaderMatcher_SafeRegexMatch{ SafeRegexMatch: &matcherv3.RegexMatcher{ EngineType: &matcherv3.RegexMatcher_GoogleRe2{GoogleRe2: &matcherv3.RegexMatcher_GoogleRE2{}}, Regex: headerMatch.Value, }, } default: msg := fmt.Sprintf("unsupported header match type: %s", matchType) return nil, createFailureCondition(gatewayv1.RouteReasonUnsupportedValue, msg, generation) } routeMatch.Headers = append(routeMatch.Headers, headerMatcher) } // Translate Query Parameter Matches for _, queryMatch := range match.QueryParams { // Gateway API only supports "Exact" match for query parameters. queryMatcher := &routev3.QueryParameterMatcher{ Name: string(queryMatch.Name), QueryParameterMatchSpecifier: &routev3.QueryParameterMatcher_StringMatch{ StringMatch: &matcherv3.StringMatcher{ MatchPattern: &matcherv3.StringMatcher_Exact{Exact: queryMatch.Value}, }, }, } routeMatch.QueryParameters = append(routeMatch.QueryParameters, queryMatcher) } // If all translations were successful, return the final object and a success condition. return routeMatch, createSuccessCondition(generation) } func createSuccessCondition(generation int64) metav1.Condition { return metav1.Condition{ Type: string(gatewayv1.RouteConditionResolvedRefs), Status: metav1.ConditionTrue, Reason: string(gatewayv1.RouteReasonResolvedRefs), Message: "All references resolved", ObservedGeneration: generation, } } func createFailureCondition(reason gatewayv1.RouteConditionReason, message string, generation int64) metav1.Condition { return metav1.Condition{ Type: string(gatewayv1.RouteConditionResolvedRefs), Status: metav1.ConditionFalse, Reason: string(reason), Message: message, ObservedGeneration: generation, } } // sortRoutes is the definitive sorter for Envoy routes based on Gateway API precedence. func sortRoutes(routes []*routev3.Route) { sort.Slice(routes, func(i, j int) bool { matchI := routes[i].GetMatch() matchJ := routes[j].GetMatch() // De-prioritize the catch-all route, ensuring it's always last. isCatchAllI := isCatchAll(matchI) isCatchAllJ := isCatchAll(matchJ) if isCatchAllI != isCatchAllJ { // If I is the catch-all, it should come after J (return false). // If J is the catch-all, it should come after I (return true). return isCatchAllJ } // Precedence Rule 1: Exact Path Match vs. Other Path Matches isExactPathI := matchI.GetPath() != "" isExactPathJ := matchJ.GetPath() != "" if isExactPathI != isExactPathJ { return isExactPathI // Exact path is higher precedence } // Precedence Rule 2: Longest Prefix Match prefixI := getPathMatchValue(matchI) prefixJ := getPathMatchValue(matchJ) if len(prefixI) != len(prefixJ) { return len(prefixI) > len(prefixJ) // Longer prefix is higher precedence } // Precedence Rule 3: Number of Header Matches headerCountI := len(matchI.GetHeaders()) headerCountJ := len(matchJ.GetHeaders()) if headerCountI != headerCountJ { return headerCountI > headerCountJ // More headers is higher precedence } // Precedence Rule 4: Number of Query Param Matches queryCountI := len(matchI.GetQueryParameters()) queryCountJ := len(matchJ.GetQueryParameters()) if queryCountI != queryCountJ { return queryCountI > queryCountJ // More query params is higher precedence } // If all else is equal, maintain original order (stable sort) return false }) } // getPathMatchValue is a helper to extract the path string for comparison. func getPathMatchValue(match *routev3.RouteMatch) string { if match.GetPath() != "" { return match.GetPath() } if match.GetPrefix() != "" { return match.GetPrefix() } if match.GetPathSeparatedPrefix() != "" { return match.GetPathSeparatedPrefix() } if sr := match.GetSafeRegex(); sr != nil { // Regex Match (used for other PathPrefix) // This correctly handles the output of translateHTTPRouteMatch. regex := sr.GetRegex() // Remove the trailing regex that matches subpaths. path := strings.TrimSuffix(regex, "(/.*)?") // Remove the quoting added by regexp.QuoteMeta. path = strings.ReplaceAll(path, `\`, "") return path } return "" } // isCatchAll determines if a route match is a generic "catch-all" rule. // A catch-all matches all paths ("/") and has no other specific conditions. func isCatchAll(match *routev3.RouteMatch) bool { if match == nil { return false } // It's a catch-all if the path match is for "/" AND there are no other constraints. isRootPrefix := match.GetPrefix() == "/" hasNoHeaders := len(match.GetHeaders()) == 0 hasNoParams := len(match.GetQueryParameters()) == 0 return isRootPrefix && hasNoHeaders && hasNoParams } ================================================ FILE: pkg/gateway/kindcluster.go ================================================ package gateway import ( "bytes" "context" _ "embed" "fmt" "net/netip" "runtime" "strings" "time" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/klog/v2" "sigs.k8s.io/cloud-provider-kind/pkg/config" "sigs.k8s.io/cloud-provider-kind/pkg/container" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" ) //go:embed routeadder/route-adder-amd64 var routeAdderAmd64 []byte //go:embed routeadder/route-adder-arm64 var routeAdderArm64 []byte func getRouteAdderBinaryForArch() ([]byte, error) { arch := runtime.GOARCH switch arch { case "amd64": return routeAdderAmd64, nil case "arm64": return routeAdderArm64, nil default: return nil, fmt.Errorf("unsupported architecture: %s", arch) } } // getClusterRoutingMap creates a unified map of all routes required for the container. // It includes routes for each node's PodCIDRs and for the cluster's ServiceCIDRs. func (c *Controller) getClusterRoutingMap(ctx context.Context) (map[string]string, error) { nodes, err := c.client.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) if err != nil { return nil, fmt.Errorf("failed to list kubernetes nodes: %w", err) } if len(nodes.Items) == 0 { return nil, fmt.Errorf("no kubernetes nodes found in the cluster") } routeMap := make(map[string]string) controlPlaneIPs := make(map[bool]string) // map[isIPv6]address // --- 1. Process all nodes for PodCIDR routes and find a control-plane IP --- for _, node := range nodes.Items { nodeIPs := make(map[bool]string) // map[isIPv6]address for _, addr := range node.Status.Addresses { if addr.Type == corev1.NodeInternalIP { ip, err := netip.ParseAddr(addr.Address) if err != nil { continue // Skip invalid IPs } nodeIPs[ip.Is6()] = addr.Address } } // If this is a control-plane node, save its IPs for service routes if _, ok := node.Labels["node-role.kubernetes.io/control-plane"]; ok && len(controlPlaneIPs) == 0 { controlPlaneIPs = nodeIPs } // Map PodCIDRs to this node's IPs, matching family for _, podCIDR := range node.Spec.PodCIDRs { prefix, err := netip.ParsePrefix(podCIDR) if err != nil { continue } if gatewayIP, found := nodeIPs[prefix.Addr().Is6()]; found { routeMap[podCIDR] = gatewayIP } } } if len(controlPlaneIPs) == 0 { return nil, fmt.Errorf("could not find any control-plane node to use as a gateway for services") } // --- 2. Get ServiceCIDRs and add them to the map --- serviceCIDRs, err := c.getServiceCIDRs(ctx) if err != nil { return nil, fmt.Errorf("failed to get service CIDRs: %w", err) } for _, serviceCIDR := range serviceCIDRs { prefix, err := netip.ParsePrefix(serviceCIDR) if err != nil { klog.Warningf("Invalid ServiceCIDR '%s', skipping.", serviceCIDR) continue } // Match the service CIDR family to the control-plane IP family if gatewayIP, found := controlPlaneIPs[prefix.Addr().Is6()]; found { klog.Infof("Found route for services: ServiceCIDR %s -> Gateway %s", serviceCIDR, gatewayIP) routeMap[serviceCIDR] = gatewayIP } else { klog.Warningf("No matching control-plane IP found for ServiceCIDR family '%s'", serviceCIDR) } } if len(routeMap) == 0 { return nil, fmt.Errorf("could not construct any valid routes") } return routeMap, nil } func (c *Controller) getServiceCIDRs(ctx context.Context) ([]string, error) { serviceCIDRs, err := c.client.NetworkingV1().ServiceCIDRs().List(ctx, metav1.ListOptions{}) if err != nil { return nil, fmt.Errorf("failed to list servicecidrs: %w. Is the ServiceCIDR feature gate enabled?", err) } if len(serviceCIDRs.Items) == 0 { return nil, fmt.Errorf("no servicecidrs found in the cluster") } cidrs := sets.Set[string]{} for _, serviceCIDRObject := range serviceCIDRs.Items { cidrs.Insert(serviceCIDRObject.Spec.CIDRs...) } if len(cidrs) == 0 { return nil, fmt.Errorf("no CIDRs found in any ServiceCIDR object") } return cidrs.UnsortedList(), nil } func (c *Controller) configureContainerNetworking(ctx context.Context, containerName string) error { binaryData, err := getRouteAdderBinaryForArch() if err != nil { return err } containerBinaryPath := "/tmp/route-adder" // 2. Combine copy and chmod into a single Exec call. // The shell command 'cat > file && chmod +x file' does both steps sequentially. setupCmd := []string{"sh", "-c", fmt.Sprintf("cat > %s && chmod +x %s", containerBinaryPath, containerBinaryPath)} stdinReader := bytes.NewReader(binaryData) klog.Infof("Streaming and setting up route-adder utility in %s", containerName) if err := container.Exec(containerName, setupCmd, stdinReader, nil, nil); err != nil { return fmt.Errorf("failed to setup route-adder binary in container %s: %w", containerName, err) } klog.Infof("Successfully installed route-adder utility in container %s", containerName) routeMap, err := c.getClusterRoutingMap(ctx) if err != nil { return fmt.Errorf("failed to get kubernetes cluster routing information: %w", err) } // 3. Iterate through the map and add a route for each entry. var routesAdded int var stdout, stderr bytes.Buffer for cidr, gatewayIP := range routeMap { cmd := []string{containerBinaryPath, cidr, gatewayIP} klog.Infof("Adding route to container %s: %s", containerName, strings.Join(cmd, " ")) stdout.Reset() stderr.Reset() if err := container.Exec(containerName, cmd, nil, &stdout, &stderr); err != nil { return fmt.Errorf("failed to add route '%s' via %s to container %s: %w, stderr: %s", cidr, gatewayIP, containerName, err, stderr.String()) } routesAdded++ } if routesAdded == 0 { return fmt.Errorf("no valid cluster routes were found to configure") } klog.Infof("Successfully added %d pod and service routes to container %s", routesAdded, containerName) return nil } func (c *Controller) ensureGatewayContainer(ctx context.Context, gw *gatewayv1.Gateway) error { namespace := gw.Namespace name := gw.Name containerName := gatewayName(c.clusterName, namespace, name) if !container.IsRunning(containerName) { klog.Infof("container %s for gateway %s/%s is not running", containerName, namespace, name) if container.Exist(containerName) { if err := container.Delete(containerName); err != nil { return err } } } if !container.Exist(containerName) { klog.V(2).Infof("creating container %s for gateway %s/%s on cluster %s", containerName, namespace, name, c.clusterName) enableTunnels := c.tunnelManager != nil || config.DefaultConfig.LoadBalancerConnectivity == config.Portmap err := createGateway(c.clusterName, c.clusterNameserver, c.xdsLocalAddress, c.xdsLocalPort, gw, enableTunnels) if err != nil { return err } // TODO fix this hack time.Sleep(250 * time.Millisecond) if err := c.configureContainerNetworking(ctx, containerName); err != nil { if delErr := container.Delete(containerName); delErr != nil { klog.Errorf("failed to delete container %s after networking setup failed: %v", containerName, delErr) } return fmt.Errorf("failed to configure networking for new gateway container %s: %w", containerName, err) } } return nil } ================================================ FILE: pkg/gateway/listener.go ================================================ package gateway import ( "context" "encoding/pem" "fmt" corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" routerv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/router/v3" tlsinspector "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/listener/tls_inspector/v3" hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" tcpproxyv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/tcp_proxy/v3" udpproxy "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/udp/udp_proxy/v3" tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3" "github.com/envoyproxy/go-control-plane/pkg/wellknown" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/wrapperspb" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/klog/v2" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" ) // setListenerCondition is a helper to safely set a condition on a listener's status // in a map of conditions. func setListenerCondition( conditionsMap map[gatewayv1.SectionName][]metav1.Condition, listenerName gatewayv1.SectionName, condition metav1.Condition, ) { // This "get, modify, set" pattern is the standard way to // work around the Go constraint that map values are not addressable. conditions := conditionsMap[listenerName] if conditions == nil { conditions = []metav1.Condition{} } meta.SetStatusCondition(&conditions, condition) conditionsMap[listenerName] = conditions } // validateListeners checks for conflicts among all listeners on a Gateway as per the spec. // It returns a map of conflicted listener conditions and a Gateway-level condition if any conflicts exist. func (c *Controller) validateListeners(gateway *gatewayv1.Gateway) map[gatewayv1.SectionName][]metav1.Condition { listenerConditions := make(map[gatewayv1.SectionName][]metav1.Condition) for _, listener := range gateway.Spec.Listeners { // Initialize with a fresh slice. listenerConditions[listener.Name] = []metav1.Condition{} } // Check for Port and Hostname Conflicts listenersByPort := make(map[gatewayv1.PortNumber][]gatewayv1.Listener) for _, listener := range gateway.Spec.Listeners { listenersByPort[listener.Port] = append(listenersByPort[listener.Port], listener) } for _, listenersOnPort := range listenersByPort { // Rule: A TCP listener cannot share a port with HTTP/HTTPS/TLS listeners. hasTCP := false hasHTTPTLS := false for _, listener := range listenersOnPort { if listener.Protocol == gatewayv1.TCPProtocolType || listener.Protocol == gatewayv1.UDPProtocolType { hasTCP = true } if listener.Protocol == gatewayv1.HTTPProtocolType || listener.Protocol == gatewayv1.HTTPSProtocolType || listener.Protocol == gatewayv1.TLSProtocolType { hasHTTPTLS = true } } if hasTCP && hasHTTPTLS { for _, listener := range listenersOnPort { setListenerCondition(listenerConditions, listener.Name, metav1.Condition{ Type: string(gatewayv1.ListenerConditionConflicted), Status: metav1.ConditionTrue, Reason: string(gatewayv1.ListenerReasonProtocolConflict), Message: "Protocol conflict: TCP/UDP listeners cannot share a port with HTTP/HTTPS/TLS listeners.", }) } continue // Skip further checks for this port } // Rule: HTTP/HTTPS/TLS listeners on the same port must have unique hostnames. seenHostnames := make(map[gatewayv1.Hostname]gatewayv1.SectionName) for _, listener := range listenersOnPort { // This check only applies to protocols that use hostnames for distinction. if listener.Protocol == gatewayv1.HTTPProtocolType || listener.Protocol == gatewayv1.HTTPSProtocolType || listener.Protocol == gatewayv1.TLSProtocolType { hostname := gatewayv1.Hostname("") if listener.Hostname != nil { hostname = *listener.Hostname } if conflictingListenerName, exists := seenHostnames[hostname]; exists { conflictedCondition := metav1.Condition{ Type: string(gatewayv1.ListenerConditionConflicted), Status: metav1.ConditionTrue, Reason: string(gatewayv1.ListenerReasonHostnameConflict), Message: fmt.Sprintf("Hostname '%s' conflicts with another listener on the same port.", hostname), } setListenerCondition(listenerConditions, listener.Name, conflictedCondition) setListenerCondition(listenerConditions, conflictingListenerName, conflictedCondition) } else { seenHostnames[hostname] = listener.Name } } } } for _, listener := range gateway.Spec.Listeners { // If a listener is already conflicted, we don't need to check its secrets. if meta.IsStatusConditionTrue(listenerConditions[listener.Name], string(gatewayv1.ListenerConditionConflicted)) { continue } if listener.TLS == nil { // No TLS config, so no secrets to resolve. This listener is considered resolved. setListenerCondition(listenerConditions, listener.Name, metav1.Condition{ Type: string(gatewayv1.ListenerConditionResolvedRefs), Status: metav1.ConditionTrue, Reason: string(gatewayv1.ListenerReasonResolvedRefs), Message: "All references resolved", }) continue } for _, certRef := range listener.TLS.CertificateRefs { if certRef.Group != nil && *certRef.Group != "" { setListenerCondition(listenerConditions, listener.Name, metav1.Condition{ Type: string(gatewayv1.ListenerConditionResolvedRefs), Status: metav1.ConditionFalse, Reason: string(gatewayv1.ListenerReasonInvalidCertificateRef), Message: fmt.Sprintf("unsupported certificate ref grup: %s", *certRef.Group), }) break } if certRef.Kind != nil && *certRef.Kind != "Secret" { setListenerCondition(listenerConditions, listener.Name, metav1.Condition{ Type: string(gatewayv1.ListenerConditionResolvedRefs), Status: metav1.ConditionFalse, Reason: string(gatewayv1.ListenerReasonInvalidCertificateRef), Message: fmt.Sprintf("unsupported certificate ref kind: %s", *certRef.Kind), }) break } secretNamespace := gateway.Namespace if certRef.Namespace != nil { secretNamespace = string(*certRef.Namespace) } if secretNamespace != gateway.Namespace { from := gatewayv1.ReferenceGrantFrom{ Group: gatewayv1.GroupName, Kind: "Gateway", Namespace: gatewayv1.Namespace(gateway.Namespace), } to := gatewayv1.ReferenceGrantTo{ Group: "", // Core group for Secret Kind: "Secret", Name: &certRef.Name, } if !isCrossNamespaceRefAllowed(from, to, secretNamespace, c.referenceGrantLister) { setListenerCondition(listenerConditions, listener.Name, metav1.Condition{ Type: string(gatewayv1.ListenerConditionResolvedRefs), Status: metav1.ConditionFalse, Reason: string(gatewayv1.ListenerReasonRefNotPermitted), Message: fmt.Sprintf("reference to Secret %s/%s not permitted by any ReferenceGrant", secretNamespace, certRef.Name), }) break } } secret, err := c.secretLister.Secrets(secretNamespace).Get(string(certRef.Name)) if err != nil { setListenerCondition(listenerConditions, listener.Name, metav1.Condition{ Type: string(gatewayv1.ListenerConditionResolvedRefs), Status: metav1.ConditionFalse, Reason: string(gatewayv1.ListenerReasonInvalidCertificateRef), Message: fmt.Sprintf("reference to Secret %s/%s not found", secretNamespace, certRef.Name), }) break } if err := validateSecretCertificate(secret); err != nil { setListenerCondition(listenerConditions, listener.Name, metav1.Condition{ Type: string(gatewayv1.ListenerConditionResolvedRefs), Status: metav1.ConditionFalse, Reason: string(gatewayv1.ListenerReasonInvalidCertificateRef), Message: fmt.Sprintf("malformed Secret %s/%s : %v", secretNamespace, certRef.Name, err.Error()), }) break } } // Set the ResolvedRefs condition based on the outcome of the secret validation. if !meta.IsStatusConditionFalse(listenerConditions[listener.Name], string(gatewayv1.ListenerConditionResolvedRefs)) { setListenerCondition(listenerConditions, listener.Name, metav1.Condition{ Type: string(gatewayv1.ListenerConditionResolvedRefs), Status: metav1.ConditionTrue, Reason: string(gatewayv1.ListenerReasonResolvedRefs), Message: "All references resolved", }) } } return listenerConditions } func (c *Controller) translateListenerToFilterChain(gateway *gatewayv1.Gateway, lis gatewayv1.Listener, virtualHosts []*routev3.VirtualHost, routeName string) (*listener.FilterChain, error) { var filterChain *listener.FilterChain switch lis.Protocol { case gatewayv1.HTTPProtocolType, gatewayv1.HTTPSProtocolType: routerProto := &routerv3.Router{} routerAny, err := anypb.New(routerProto) if err != nil { klog.Errorf("Failed to marshal router config: %v", err) return nil, err } hcmConfig := &hcm.HttpConnectionManager{ StatPrefix: string(lis.Name), // Enable X-Forwarded-For header // https://github.com/kubernetes-sigs/cloud-provider-kind/issues/296 UseRemoteAddress: &wrapperspb.BoolValue{Value: true}, // Support websocket upgrade // https://github.com/kubernetes-sigs/cloud-provider-kind/issues/355 UpgradeConfigs: []*hcm.HttpConnectionManager_UpgradeConfig{{ UpgradeType: "websocket", }}, RouteSpecifier: &hcm.HttpConnectionManager_Rds{ Rds: &hcm.Rds{ ConfigSource: &corev3.ConfigSource{ ResourceApiVersion: corev3.ApiVersion_V3, ConfigSourceSpecifier: &corev3.ConfigSource_Ads{Ads: &corev3.AggregatedConfigSource{}}, }, RouteConfigName: routeName, }, }, HttpFilters: []*hcm.HttpFilter{{ Name: wellknown.Router, ConfigType: &hcm.HttpFilter_TypedConfig{ TypedConfig: routerAny, }, }}, } hcmAny, err := anypb.New(hcmConfig) if err != nil { return nil, err } filterChain = &listener.FilterChain{ Filters: []*listener.Filter{{ Name: wellknown.HTTPConnectionManager, ConfigType: &listener.Filter_TypedConfig{ TypedConfig: hcmAny, }, }}, } case gatewayv1.TCPProtocolType, gatewayv1.TLSProtocolType: // TCP and TLS listeners require a TCP proxy filter. // We'll assume for now that routes for these are not supported and it's a direct pass-through. tcpProxy := &tcpproxyv3.TcpProxy{ StatPrefix: string(lis.Name), ClusterSpecifier: &tcpproxyv3.TcpProxy_Cluster{ Cluster: "some_static_cluster", // This needs to be determined from a TCPRoute/TLSRoute }, } tcpProxyAny, err := anypb.New(tcpProxy) if err != nil { return nil, err } filterChain = &listener.FilterChain{ Filters: []*listener.Filter{{ Name: wellknown.TCPProxy, ConfigType: &listener.Filter_TypedConfig{ TypedConfig: tcpProxyAny, }, }}, } case gatewayv1.UDPProtocolType: udpProxy := &udpproxy.UdpProxyConfig{ StatPrefix: string(lis.Name), RouteSpecifier: &udpproxy.UdpProxyConfig_Cluster{ Cluster: "some_udp_cluster", // This needs to be determined from a UDPRoute }, } udpProxyAny, err := anypb.New(udpProxy) if err != nil { return nil, err } filterChain = &listener.FilterChain{ Filters: []*listener.Filter{{ Name: "envoy.filters.udp_listener.udp_proxy", ConfigType: &listener.Filter_TypedConfig{ TypedConfig: udpProxyAny, }, }}, } } // Add SNI matching for applicable protocols if lis.Protocol == gatewayv1.HTTPSProtocolType || lis.Protocol == gatewayv1.TLSProtocolType { if lis.Hostname != nil && *lis.Hostname != "" { filterChain.FilterChainMatch = &listener.FilterChainMatch{ ServerNames: []string{string(*lis.Hostname)}, } } // Configure TLS context tlsContext, err := c.buildDownstreamTLSContext(context.Background(), gateway, lis) if err != nil { return nil, fmt.Errorf("failed to build TLS context for listener %s: %w", lis.Name, err) } if tlsContext != nil { filterChain.TransportSocket = &corev3.TransportSocket{ Name: "envoy.transport_sockets.tls", ConfigType: &corev3.TransportSocket_TypedConfig{ TypedConfig: tlsContext, }, } } } return filterChain, nil } func (c *Controller) buildDownstreamTLSContext(ctx context.Context, gateway *gatewayv1.Gateway, lis gatewayv1.Listener) (*anypb.Any, error) { if lis.TLS == nil { return nil, nil } if len(lis.TLS.CertificateRefs) == 0 { return nil, fmt.Errorf("TLS is configured, but no certificate refs are provided") } tlsContext := &tlsv3.DownstreamTlsContext{ CommonTlsContext: &tlsv3.CommonTlsContext{ TlsCertificates: []*tlsv3.TlsCertificate{}, }, } for _, certRef := range lis.TLS.CertificateRefs { if certRef.Group != nil && *certRef.Group != "" { return nil, fmt.Errorf("unsupported certificate ref group: %s", *certRef.Group) } if certRef.Kind != nil && *certRef.Kind != "Secret" { return nil, fmt.Errorf("unsupported certificate ref kind: %s", *certRef.Kind) } secretNamespace := gateway.Namespace if certRef.Namespace != nil { secretNamespace = string(*certRef.Namespace) } secretName := string(certRef.Name) secret, err := c.secretLister.Secrets(secretNamespace).Get(secretName) if err != nil { // Per the spec, if the grant was missing, we must not reveal that the secret doesn't exist. // The error from the grant check above takes precedence. return nil, fmt.Errorf("failed to get secret %s/%s: %w", secretNamespace, secretName, err) } tlsCert, err := toEnvoyTlsCertificate(secret) if err != nil { return nil, fmt.Errorf("failed to convert secret to tls certificate: %v", err) } tlsContext.CommonTlsContext.TlsCertificates = append(tlsContext.CommonTlsContext.TlsCertificates, tlsCert) } any, err := anypb.New(tlsContext) if err != nil { return nil, err } return any, nil } func validateSecretCertificate(secret *corev1.Secret) error { privateKey, ok := secret.Data[corev1.TLSPrivateKeyKey] if !ok { return fmt.Errorf("secret %s/%s does not contain key %s", secret.Namespace, secret.Name, corev1.TLSPrivateKeyKey) } block, _ := pem.Decode(privateKey) if block == nil { return fmt.Errorf("secret %s/%s key %s does not contain a valid PEM-encoded private key", secret.Namespace, secret.Name, corev1.TLSPrivateKeyKey) } certChain, ok := secret.Data[corev1.TLSCertKey] if !ok { return fmt.Errorf("secret %s/%s does not contain key %s", secret.Namespace, secret.Name, corev1.TLSCertKey) } block, _ = pem.Decode(certChain) if block == nil { return fmt.Errorf("secret %s/%s key %s does not contain a valid PEM-encoded certificate chain", secret.Namespace, secret.Name, corev1.TLSCertKey) } return nil } func toEnvoyTlsCertificate(secret *corev1.Secret) (*tlsv3.TlsCertificate, error) { privateKey, ok := secret.Data[corev1.TLSPrivateKeyKey] if !ok { return nil, fmt.Errorf("secret %s/%s does not contain key %s", secret.Namespace, secret.Name, corev1.TLSPrivateKeyKey) } block, _ := pem.Decode(privateKey) if block == nil { return nil, fmt.Errorf("secret %s/%s key %s does not contain a valid PEM-encoded private key", secret.Namespace, secret.Name, corev1.TLSPrivateKeyKey) } certChain, ok := secret.Data[corev1.TLSCertKey] if !ok { return nil, fmt.Errorf("secret %s/%s does not contain key %s", secret.Namespace, secret.Name, corev1.TLSCertKey) } block, _ = pem.Decode(certChain) if block == nil { return nil, fmt.Errorf("secret %s/%s key %s does not contain a valid PEM-encoded certificate chain", secret.Namespace, secret.Name, corev1.TLSCertKey) } return &tlsv3.TlsCertificate{ CertificateChain: &corev3.DataSource{ Specifier: &corev3.DataSource_InlineBytes{ InlineBytes: certChain, }, }, PrivateKey: &corev3.DataSource{ Specifier: &corev3.DataSource_InlineBytes{ InlineBytes: privateKey, }, }, }, nil } func createEnvoyAddress(port uint32) *corev3.Address { return &corev3.Address{ Address: &corev3.Address_SocketAddress{ SocketAddress: &corev3.SocketAddress{ Protocol: corev3.SocketAddress_TCP, Address: "0.0.0.0", PortSpecifier: &corev3.SocketAddress_PortValue{ PortValue: port, }, }, }, } } func createListenerFilters() []*listener.ListenerFilter { tlsInspectorConfig, _ := anypb.New(&tlsinspector.TlsInspector{}) return []*listener.ListenerFilter{ { Name: wellknown.TlsInspector, ConfigType: &listener.ListenerFilter_TypedConfig{ TypedConfig: tlsInspectorConfig, }, }, } } ================================================ FILE: pkg/gateway/referencegrant.go ================================================ package gateway import ( "k8s.io/apimachinery/pkg/labels" "k8s.io/klog/v2" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" gatewayv1listers "sigs.k8s.io/gateway-api/pkg/client/listers/apis/v1" ) // isCrossNamespaceRefAllowed checks if a cross-namespace reference from a 'from' object // to a 'to' object is permitted by a ReferenceGrant in the 'to' object's namespace. func isCrossNamespaceRefAllowed( from gatewayv1.ReferenceGrantFrom, // Describes the referencing object (e.g., an HTTPRoute) to gatewayv1.ReferenceGrantTo, // Describes the referenced object (e.g., a Service) toNamespace string, // The namespace of the referenced object referenceGrantLister gatewayv1listers.ReferenceGrantLister, ) bool { // List all ReferenceGrants in the target namespace. grants, err := referenceGrantLister.ReferenceGrants(toNamespace).List(labels.Everything()) if err != nil { klog.Errorf("Failed to list ReferenceGrants in namespace %s: %v", toNamespace, err) return false } for _, grant := range grants { // Check if the grant's "From" section matches our referencing object. fromAllowed := false for _, grantFrom := range grant.Spec.From { if grantFrom.Group == from.Group && grantFrom.Kind == from.Kind && grantFrom.Namespace == from.Namespace { fromAllowed = true break } } if !fromAllowed { continue // This grant doesn't apply to our 'from' object. } // Check if the grant's "To" section matches our referenced object. toAllowed := false for _, grantTo := range grant.Spec.To { if grantTo.Group == to.Group && grantTo.Kind == to.Kind { // If the grant specifies a resource name, it must match. if grantTo.Name == nil || *grantTo.Name == "" { toAllowed = true // Grant applies to all resources of this kind. break } if to.Name != nil && *grantTo.Name == *to.Name { toAllowed = true // Grant applies to this specific resource name. break } } } if toAllowed { // We found a grant that explicitly allows this cross-namespace reference. return true } } // No grant was found that allows this reference. return false } ================================================ FILE: pkg/gateway/referencegrant_test.go ================================================ package gateway import ( "errors" "testing" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" gatewayv1listers "sigs.k8s.io/gateway-api/pkg/client/listers/apis/v1" ) // fakeReferenceGrantLister is a mock implementation of ReferenceGrantLister for testing. type fakeReferenceGrantLister struct { grants []*gatewayv1.ReferenceGrant err error } // newFakeReferenceGrantLister creates a new fakeReferenceGrantLister. func newFakeReferenceGrantLister(grants []*gatewayv1.ReferenceGrant, err error) gatewayv1listers.ReferenceGrantLister { return &fakeReferenceGrantLister{grants: grants, err: err} } // List returns the stored grants or an error. func (f *fakeReferenceGrantLister) List(selector labels.Selector) ([]*gatewayv1.ReferenceGrant, error) { if f.err != nil { return nil, f.err } return f.grants, nil } // Get returns the grant with the given name or an error. func (f *fakeReferenceGrantLister) Get(name string) (*gatewayv1.ReferenceGrant, error) { if f.err != nil { return nil, f.err } for _, grant := range f.grants { if grant.Name == name { return grant, nil } } return nil, errors.New("not found") } // ReferenceGrants returns a lister for a specific namespace. func (f *fakeReferenceGrantLister) ReferenceGrants(namespace string) gatewayv1listers.ReferenceGrantNamespaceLister { // For testing purposes, we can return the lister itself, as we don't use the namespace in the mock List. // A more sophisticated mock could filter by namespace here. return f } func TestIsCrossNamespaceRefAllowed(t *testing.T) { serviceKind := gatewayv1.Kind("Service") httpRouteKind := gatewayv1.Kind("HTTPRoute") gatewayGroup := gatewayv1.Group("gateway.networking.k8s.io") coreGroup := gatewayv1.Group("") specificServiceName := gatewayv1.ObjectName("specific-service") testCases := []struct { name string from gatewayv1.ReferenceGrantFrom to gatewayv1.ReferenceGrantTo toNamespace string grants []*gatewayv1.ReferenceGrant listerError error expected bool }{ { name: "allowed by grant", from: gatewayv1.ReferenceGrantFrom{ Group: gatewayGroup, Kind: httpRouteKind, Namespace: "default", }, to: gatewayv1.ReferenceGrantTo{ Group: coreGroup, Kind: serviceKind, }, toNamespace: "backend-ns", grants: []*gatewayv1.ReferenceGrant{ { ObjectMeta: metav1.ObjectMeta{Namespace: "backend-ns"}, Spec: gatewayv1.ReferenceGrantSpec{ From: []gatewayv1.ReferenceGrantFrom{ {Group: gatewayGroup, Kind: httpRouteKind, Namespace: "default"}, }, To: []gatewayv1.ReferenceGrantTo{ {Group: coreGroup, Kind: serviceKind}, }, }, }, }, expected: true, }, { name: "allowed by grant with specific resource name", from: gatewayv1.ReferenceGrantFrom{ Group: gatewayGroup, Kind: httpRouteKind, Namespace: "default", }, to: gatewayv1.ReferenceGrantTo{ Group: coreGroup, Kind: serviceKind, Name: &specificServiceName, }, toNamespace: "backend-ns", grants: []*gatewayv1.ReferenceGrant{ { ObjectMeta: metav1.ObjectMeta{Namespace: "backend-ns"}, Spec: gatewayv1.ReferenceGrantSpec{ From: []gatewayv1.ReferenceGrantFrom{ {Group: gatewayGroup, Kind: httpRouteKind, Namespace: "default"}, }, To: []gatewayv1.ReferenceGrantTo{ {Group: coreGroup, Kind: serviceKind, Name: &specificServiceName}, }, }, }, }, expected: true, }, { name: "denied because from namespace does not match", from: gatewayv1.ReferenceGrantFrom{ Group: gatewayGroup, Kind: httpRouteKind, Namespace: "another-ns", // Mismatch }, to: gatewayv1.ReferenceGrantTo{ Group: coreGroup, Kind: serviceKind, }, toNamespace: "backend-ns", grants: []*gatewayv1.ReferenceGrant{ { ObjectMeta: metav1.ObjectMeta{Namespace: "backend-ns"}, Spec: gatewayv1.ReferenceGrantSpec{ From: []gatewayv1.ReferenceGrantFrom{ {Group: gatewayGroup, Kind: httpRouteKind, Namespace: "default"}, }, To: []gatewayv1.ReferenceGrantTo{ {Group: coreGroup, Kind: serviceKind}, }, }, }, }, expected: false, }, { name: "denied because to kind does not match", from: gatewayv1.ReferenceGrantFrom{ Group: gatewayGroup, Kind: httpRouteKind, Namespace: "default", }, to: gatewayv1.ReferenceGrantTo{ Group: coreGroup, Kind: "Secret", // Mismatch }, toNamespace: "backend-ns", grants: []*gatewayv1.ReferenceGrant{ { ObjectMeta: metav1.ObjectMeta{Namespace: "backend-ns"}, Spec: gatewayv1.ReferenceGrantSpec{ From: []gatewayv1.ReferenceGrantFrom{ {Group: gatewayGroup, Kind: httpRouteKind, Namespace: "default"}, }, To: []gatewayv1.ReferenceGrantTo{ {Group: coreGroup, Kind: serviceKind}, }, }, }, }, expected: false, }, { name: "denied because specific resource name does not match", from: gatewayv1.ReferenceGrantFrom{ Group: gatewayGroup, Kind: httpRouteKind, Namespace: "default", }, to: gatewayv1.ReferenceGrantTo{ Group: coreGroup, Kind: serviceKind, Name: func() *gatewayv1.ObjectName { s := gatewayv1.ObjectName("other-service"); return &s }(), }, toNamespace: "backend-ns", grants: []*gatewayv1.ReferenceGrant{ { ObjectMeta: metav1.ObjectMeta{Namespace: "backend-ns"}, Spec: gatewayv1.ReferenceGrantSpec{ From: []gatewayv1.ReferenceGrantFrom{ {Group: gatewayGroup, Kind: httpRouteKind, Namespace: "default"}, }, To: []gatewayv1.ReferenceGrantTo{ {Group: coreGroup, Kind: serviceKind, Name: &specificServiceName}, }, }, }, }, expected: false, }, { name: "no grants in namespace", from: gatewayv1.ReferenceGrantFrom{ Group: gatewayGroup, Kind: httpRouteKind, Namespace: "default", }, to: gatewayv1.ReferenceGrantTo{ Group: coreGroup, Kind: serviceKind, }, toNamespace: "backend-ns", grants: []*gatewayv1.ReferenceGrant{}, expected: false, }, { name: "lister returns error", from: gatewayv1.ReferenceGrantFrom{ Group: gatewayGroup, Kind: httpRouteKind, Namespace: "default", }, to: gatewayv1.ReferenceGrantTo{ Group: coreGroup, Kind: serviceKind, }, toNamespace: "backend-ns", grants: nil, listerError: errors.New("failed to list"), expected: false, }, { name: "multiple grants, one allows", from: gatewayv1.ReferenceGrantFrom{ Group: gatewayGroup, Kind: httpRouteKind, Namespace: "default", }, to: gatewayv1.ReferenceGrantTo{ Group: coreGroup, Kind: serviceKind, }, toNamespace: "backend-ns", grants: []*gatewayv1.ReferenceGrant{ { // This one doesn't match ObjectMeta: metav1.ObjectMeta{Namespace: "backend-ns"}, Spec: gatewayv1.ReferenceGrantSpec{ From: []gatewayv1.ReferenceGrantFrom{ {Group: gatewayGroup, Kind: "OtherKind", Namespace: "default"}, }, To: []gatewayv1.ReferenceGrantTo{ {Group: coreGroup, Kind: serviceKind}, }, }, }, { // This one matches ObjectMeta: metav1.ObjectMeta{Namespace: "backend-ns"}, Spec: gatewayv1.ReferenceGrantSpec{ From: []gatewayv1.ReferenceGrantFrom{ {Group: gatewayGroup, Kind: httpRouteKind, Namespace: "default"}, }, To: []gatewayv1.ReferenceGrantTo{ {Group: coreGroup, Kind: serviceKind}, }, }, }, }, expected: true, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { lister := newFakeReferenceGrantLister(tc.grants, tc.listerError) result := isCrossNamespaceRefAllowed(tc.from, tc.to, tc.toNamespace, lister) if result != tc.expected { t.Errorf("expected %v, but got %v", tc.expected, result) } }) } } ================================================ FILE: pkg/gateway/routeadder/README.md ================================================ Packages generated with `hack/build-route-adder.sh` to inject a binary on the envoy container image that allows to configure routes. This is required because the envoy image does not have `route` or `iproute2` commands and, in order to forward the gateway traffic to a Service ClusterIP, we need to route it through one of the cluster nodes. ================================================ FILE: pkg/gateway/routes.go ================================================ package gateway import ( "strings" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/sets" corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/klog/v2" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" ) // isAllowedByListener checks if a given route is allowed to attach to a listener // based on the listener's `allowedRoutes` specification for namespaces and kinds. func isAllowedByListener(gateway *gatewayv1.Gateway, listener gatewayv1.Listener, route metav1.Object, namespaceLister corev1listers.NamespaceLister) bool { allowed := listener.AllowedRoutes if allowed == nil { // If AllowedRoutes is not set, only routes in the same namespace are allowed. return route.GetNamespace() == gateway.GetNamespace() } routeNamespace := route.GetNamespace() gatewayNamespace := gateway.GetNamespace() // Check if the route's namespace is allowed. namespaceAllowed := false effectiveFrom := gatewayv1.NamespacesFromSame if allowed.Namespaces != nil && allowed.Namespaces.From != nil { effectiveFrom = *allowed.Namespaces.From } switch effectiveFrom { case gatewayv1.NamespacesFromAll: namespaceAllowed = true case gatewayv1.NamespacesFromSame: namespaceAllowed = (routeNamespace == gatewayNamespace) case gatewayv1.NamespacesFromSelector: if allowed.Namespaces.Selector == nil { klog.Errorf("Invalid AllowedRoutes: Namespaces.From is 'Selector' but Namespaces.Selector is nil for Gateway %s/%s, Listener %s", gatewayNamespace, gateway.GetName(), listener.Name) return false } if namespaceLister == nil { klog.Warningf("Namespace selection using 'Selector' requires a Namespace Lister, but none was provided. Denying route %s/%s.", routeNamespace, route.GetName()) return false } selector, err := metav1.LabelSelectorAsSelector(allowed.Namespaces.Selector) if err != nil { klog.Errorf("Failed to parse label selector for Gateway %s/%s, Listener %s: %v", gatewayNamespace, gateway.GetName(), listener.Name, err) return false } routeNsObj, err := namespaceLister.Get(routeNamespace) if err != nil { klog.Warningf("Failed to get namespace %s for route %s/%s: %v", routeNamespace, routeNamespace, route.GetName(), err) return false } namespaceAllowed = selector.Matches(labels.Set(routeNsObj.GetLabels())) default: klog.Errorf("Unknown 'From' value %q in AllowedRoutes.Namespaces for Gateway %s/%s, Listener %s", effectiveFrom, gatewayNamespace, gateway.GetName(), listener.Name) return false } if !namespaceAllowed { return false } // If namespaces are allowed, check if the route's kind is allowed. if len(allowed.Kinds) == 0 { // No kinds specified, so all kinds are allowed. return true } var routeGroup, routeKind string switch route.(type) { case *gatewayv1.HTTPRoute: routeGroup = gatewayv1.GroupName routeKind = "HTTPRoute" case *gatewayv1.GRPCRoute: routeGroup = gatewayv1.GroupName routeKind = "GRPCRoute" default: klog.Warningf("Cannot determine GroupKind for route object type %T for route %s/%s", route, routeNamespace, route.GetName()) return false } for _, allowedKind := range allowed.Kinds { allowedGroup := gatewayv1.Group(gatewayv1.GroupName) if allowedKind.Group != nil && *allowedKind.Group != "" { allowedGroup = *allowedKind.Group } if routeKind == string(allowedKind.Kind) && routeGroup == string(allowedGroup) { return true } } // The route's kind is not in the allowed list. return false } // isAllowedByHostname checks if a route is allowed to attach to a listener // based on hostname matching rules. func isAllowedByHostname(listener gatewayv1.Listener, route metav1.Object) bool { // If the listener specifies no hostname, it allows all route hostnames. if listener.Hostname == nil || *listener.Hostname == "" { return true } listenerHostname := string(*listener.Hostname) var routeHostnames []gatewayv1.Hostname switch r := route.(type) { case *gatewayv1.HTTPRoute: routeHostnames = r.Spec.Hostnames case *gatewayv1.GRPCRoute: routeHostnames = r.Spec.Hostnames default: // Not a type with hostnames, so no hostname check needed. return true } // If the route specifies no hostnames, it inherits from the listener, which is always valid. if len(routeHostnames) == 0 { return true } // If the route specifies hostnames, at least one must be permitted by the listener. for _, routeHostname := range routeHostnames { if isHostnameSubset(string(routeHostname), listenerHostname) { // Found a valid hostname match. The route is allowed by this listener. return true } } // If we reach here, the route specified hostnames, but NONE of them were valid // for this listener. The route must not be attached. return false } // getIntersectingHostnames calculates the precise set of hostnames for an Envoy VirtualHost, // resolving each match to the most restrictive hostname as per the Gateway API specification. // It returns a slice of the resulting hostnames, or an empty slice if there is no valid intersection. func getIntersectingHostnames(listener gatewayv1.Listener, routeHostnames []gatewayv1.Hostname) []string { // Case 1: The listener has no hostname specified. It acts as a universal wildcard, // allowing any and all hostnames from the route. if listener.Hostname == nil || *listener.Hostname == "" { if len(routeHostnames) == 0 { return []string{"*"} // Universal match } // The result is simply the route's own hostnames. var names []string for _, h := range routeHostnames { names = append(names, string(h)) } return names } listenerHostname := string(*listener.Hostname) // Case 2: The route has no hostnames. It implicitly inherits the listener's specific hostname. if len(routeHostnames) == 0 { return []string{listenerHostname} } // Case 3: Both have hostnames. We must find the intersection and then determine // the most restrictive hostname for each match. intersection := sets.New[string]() for _, h := range routeHostnames { routeHostname := string(h) if isHostnameSubset(routeHostname, listenerHostname) { // A valid intersection was found. Now, determine the most specific // hostname to use for the configuration. if strings.HasPrefix(routeHostname, "*") && !strings.HasPrefix(listenerHostname, "*") { // If the route is a wildcard and the listener is specific, the listener's // specific hostname is the most restrictive result. intersection.Insert(listenerHostname) } else { // In all other valid cases (exact match, specific route on a wildcard listener), // the route's hostname is the most restrictive result. intersection.Insert(routeHostname) } } } // Return the unique set of resulting hostnames. return intersection.UnsortedList() } // isHostnameSubset checks if a route hostname is a valid subset of a listener hostname, // implementing the precise matching rules from the Gateway API specification. func isHostnameSubset(routeHostname, listenerHostname string) bool { // Rule 1: An exact match is always a valid intersection. if routeHostname == listenerHostname { return true } // Rule 2: Listener has a wildcard (e.g., "*.example.com"). if strings.HasPrefix(listenerHostname, "*.") { // Use the part of the string including the dot as the suffix. listenerSuffix := listenerHostname[1:] // e.g., ".example.com" // Case 2a: Route also has a wildcard (e.g., "*.foo.example.com"). // The route's suffix must be identical to or a sub-suffix of the listener's. if strings.HasPrefix(routeHostname, "*.") { routeSuffix := routeHostname[1:] // e.g., ".foo.example.com" return strings.HasSuffix(routeSuffix, listenerSuffix) } // Case 2b: Route is specific (e.g., "foo.example.com"). // The route must end with the listener's suffix. This correctly handles // the "parent domain" case because "example.com" does not have the // suffix ".example.com". return strings.HasSuffix(routeHostname, listenerSuffix) } // Rule 3: Route has a wildcard (e.g., "*.example.com"). if strings.HasPrefix(routeHostname, "*.") { routeSuffix := routeHostname[1:] // e.g., ".example.com" routeDomain := routeHostname[2:] // e.g., "example.com" // The listener must be more specific (not a wildcard). if !strings.HasPrefix(listenerHostname, "*.") { // The listener hostname must be the parent domain or a subdomain. // e.g., "example.com" or "foo.example.com" are subsets of "*.example.com". return listenerHostname == routeDomain || strings.HasSuffix(listenerHostname, routeSuffix) } } return false } ================================================ FILE: pkg/gateway/routes_test.go ================================================ package gateway import ( "fmt" "testing" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/utils/ptr" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" ) // A mock implementation of NamespaceLister for testing purposes. type mockNamespaceLister struct { namespaces map[string]*corev1.Namespace } func (m *mockNamespaceLister) List(selector labels.Selector) ([]*corev1.Namespace, error) { var matching []*corev1.Namespace for _, ns := range m.namespaces { if selector.Matches(labels.Set(ns.Labels)) { matching = append(matching, ns) } } return matching, nil } func (m *mockNamespaceLister) Get(name string) (*corev1.Namespace, error) { if ns, ok := m.namespaces[name]; ok { return ns, nil } return nil, fmt.Errorf("namespace %s not found", name) } func (m *mockNamespaceLister) GetPodNamespaces(pod *corev1.Pod) ([]*corev1.Namespace, error) { return nil, nil } func (m *mockNamespaceLister) Pods(namespace string) corev1listers.PodNamespaceLister { return nil } func TestIsAllowedByListener(t *testing.T) { testGateway := &gatewayv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: "test-gateway", Namespace: "gateway-ns", }, } httpRouteKind := gatewayv1.RouteGroupKind{Kind: "HTTPRoute"} grpcRouteKind := gatewayv1.RouteGroupKind{Group: ptr.To(gatewayv1.Group(gatewayv1.GroupName)), Kind: "GRPCRoute"} otherRouteKind := gatewayv1.RouteGroupKind{Group: ptr.To(gatewayv1.Group("other.group")), Kind: "OtherRoute"} mockNsLister := &mockNamespaceLister{ namespaces: map[string]*corev1.Namespace{ "gateway-ns": {ObjectMeta: metav1.ObjectMeta{Name: "gateway-ns", Labels: map[string]string{"type": "gateway"}}}, "route-ns-1": {ObjectMeta: metav1.ObjectMeta{Name: "route-ns-1", Labels: map[string]string{"app": "foo"}}}, "route-ns-2": {ObjectMeta: metav1.ObjectMeta{Name: "route-ns-2", Labels: map[string]string{"app": "bar"}}}, }, } tests := []struct { name string listener gatewayv1.Listener route metav1.Object namespaceList corev1listers.NamespaceLister // Use the interface type want bool }{ { name: "Nil AllowedRoutes, route in same namespace", listener: gatewayv1.Listener{ Name: "listener-1", AllowedRoutes: nil, // Default should be Same namespace }, route: &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "gateway-ns"}, // Same ns }, namespaceList: mockNsLister, want: true, }, { name: "Nil AllowedRoutes, route in different namespace", listener: gatewayv1.Listener{ Name: "listener-1", AllowedRoutes: nil, // Default should be Same namespace }, route: &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "other-ns"}, // Different ns }, namespaceList: mockNsLister, want: false, }, { name: "NamespacesFromSame, route in same namespace", listener: gatewayv1.Listener{ Name: "listener-1", AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{From: ptr.To(gatewayv1.NamespacesFromSame)}, }, }, route: &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "gateway-ns"}, // Same ns }, namespaceList: mockNsLister, want: true, }, { name: "NamespacesFromSame, route in different namespace", listener: gatewayv1.Listener{ Name: "listener-1", AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{From: ptr.To(gatewayv1.NamespacesFromSame)}, }, }, route: &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "other-ns"}, // Different ns }, namespaceList: mockNsLister, want: false, }, { name: "NamespacesFromAll, route in different namespace", listener: gatewayv1.Listener{ Name: "listener-1", AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{From: ptr.To(gatewayv1.NamespacesFromAll)}, }, }, route: &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "other-ns"}, // Different ns }, namespaceList: mockNsLister, want: true, }, { name: "NamespacesFromSelector, matching namespace", listener: gatewayv1.Listener{ Name: "listener-1", AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{ From: ptr.To(gatewayv1.NamespacesFromSelector), Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "foo"}}, }, }, }, route: &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "route-ns-1"}, // Matches selector }, namespaceList: mockNsLister, want: true, }, { name: "NamespacesFromSelector, non-matching namespace", listener: gatewayv1.Listener{ Name: "listener-1", AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{ From: ptr.To(gatewayv1.NamespacesFromSelector), Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "foo"}}, }, }, }, route: &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "route-ns-2"}, // Does not match selector }, namespaceList: mockNsLister, want: false, }, { name: "NamespacesFromSelector, namespace not found", listener: gatewayv1.Listener{ Name: "listener-1", AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{ From: ptr.To(gatewayv1.NamespacesFromSelector), Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"app": "foo"}}, }, }, }, route: &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "non-existent-ns"}, // NS doesn't exist }, namespaceList: mockNsLister, want: false, }, { name: "Allowed Kind matches HTTPRoute (default group)", listener: gatewayv1.Listener{ Name: "listener-1", AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{From: ptr.To(gatewayv1.NamespacesFromAll)}, Kinds: []gatewayv1.RouteGroupKind{httpRouteKind}, }, }, route: &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "any-ns"}, }, namespaceList: mockNsLister, want: true, }, { name: "Allowed Kind matches GRPCRoute (explicit group)", listener: gatewayv1.Listener{ Name: "listener-1", AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{From: ptr.To(gatewayv1.NamespacesFromAll)}, Kinds: []gatewayv1.RouteGroupKind{grpcRouteKind}, }, }, route: &gatewayv1.GRPCRoute{ // GRPCRoute type ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "any-ns"}, }, namespaceList: mockNsLister, want: true, }, { name: "Allowed Kind does not match route kind", listener: gatewayv1.Listener{ Name: "listener-1", AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{From: ptr.To(gatewayv1.NamespacesFromAll)}, Kinds: []gatewayv1.RouteGroupKind{grpcRouteKind}, // Only allow GRPCRoute }, }, route: &gatewayv1.HTTPRoute{ // Route is HTTPRoute ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "any-ns"}, }, namespaceList: mockNsLister, want: false, }, { name: "Allowed Kind does not match route group", listener: gatewayv1.Listener{ Name: "listener-1", AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{From: ptr.To(gatewayv1.NamespacesFromAll)}, Kinds: []gatewayv1.RouteGroupKind{otherRouteKind}, // Allow other.group/OtherRoute }, }, route: &gatewayv1.HTTPRoute{ // Route is gateway.networking.k8s.io/HTTPRoute ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "any-ns"}, }, namespaceList: mockNsLister, want: false, }, { name: "Empty Kinds list allows compatible kinds (HTTPRoute)", listener: gatewayv1.Listener{ Name: "listener-1", AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{From: ptr.To(gatewayv1.NamespacesFromAll)}, Kinds: []gatewayv1.RouteGroupKind{}, // Empty list }, }, route: &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "any-ns"}, }, namespaceList: mockNsLister, want: true, // Namespace check passes, empty Kinds passes }, { name: "Namespace allowed, Kind denied", listener: gatewayv1.Listener{ Name: "listener-1", AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{From: ptr.To(gatewayv1.NamespacesFromSame)}, // Same NS allowed Kinds: []gatewayv1.RouteGroupKind{grpcRouteKind}, // Only GRPCRoute allowed }, }, route: &gatewayv1.HTTPRoute{ // HTTPRoute in same NS ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "gateway-ns"}, }, namespaceList: mockNsLister, want: false, // Kind check fails }, { name: "Namespace denied, Kind allowed", listener: gatewayv1.Listener{ Name: "listener-1", AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{From: ptr.To(gatewayv1.NamespacesFromSame)}, // Same NS allowed Kinds: []gatewayv1.RouteGroupKind{httpRouteKind}, // HTTPRoute allowed }, }, route: &gatewayv1.HTTPRoute{ // HTTPRoute in different NS ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "other-ns"}, }, namespaceList: mockNsLister, want: false, // Namespace check fails }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := isAllowedByListener(testGateway, tt.listener, tt.route, tt.namespaceList); got != tt.want { t.Errorf("isAllowedByListener() = %v, want %v", got, tt.want) } }) } } func TestIsAllowedByHostname(t *testing.T) { tests := []struct { name string listener gatewayv1.Listener route metav1.Object want bool }{ { name: "Listener has no hostname, allows any route hostname", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: nil, }, route: &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "any-ns"}, Spec: gatewayv1.HTTPRouteSpec{Hostnames: []gatewayv1.Hostname{"foo.example.com"}}, }, want: true, }, { name: "Route has no hostname, inherits from listener", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: ptr.To(gatewayv1.Hostname("bar.example.com")), }, route: &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "any-ns"}, Spec: gatewayv1.HTTPRouteSpec{Hostnames: []gatewayv1.Hostname{}}, }, want: true, }, { name: "Exact match", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: ptr.To(gatewayv1.Hostname("foo.example.com")), }, route: &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "any-ns"}, Spec: gatewayv1.HTTPRouteSpec{Hostnames: []gatewayv1.Hostname{"foo.example.com"}}, }, want: true, }, { name: "Wildcard listener, subdomain route", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: ptr.To(gatewayv1.Hostname("*.example.com")), }, route: &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "any-ns"}, Spec: gatewayv1.HTTPRouteSpec{Hostnames: []gatewayv1.Hostname{"foo.example.com"}}, }, want: true, }, { name: "No match", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: ptr.To(gatewayv1.Hostname("foo.example.com")), }, route: &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "any-ns"}, Spec: gatewayv1.HTTPRouteSpec{Hostnames: []gatewayv1.Hostname{"bar.example.com"}}, }, want: false, }, { name: "Wildcard listener, non-matching route", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: ptr.To(gatewayv1.Hostname("*.example.com")), }, route: &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "any-ns"}, Spec: gatewayv1.HTTPRouteSpec{Hostnames: []gatewayv1.Hostname{"foo.another.com"}}, }, want: false, }, { name: "Wildcard listener, parent domain route (not allowed)", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: ptr.To(gatewayv1.Hostname("*.example.com")), }, route: &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{Name: "route1", Namespace: "any-ns"}, Spec: gatewayv1.HTTPRouteSpec{Hostnames: []gatewayv1.Hostname{"example.com"}}, }, want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := isAllowedByHostname(tt.listener, tt.route); got != tt.want { t.Errorf("isAllowedByHostname() = %v, want %v", got, tt.want) } }) } } func TestIsHostnameSubset(t *testing.T) { // Test cases are derived directly from the Gateway API specification // for hostname matching. testCases := []struct { name string listenerHostname string routeHostname string want bool }{ // --- Core Scenarios from Spec --- { name: "Spec: Exact match", listenerHostname: "test.example.com", routeHostname: "test.example.com", want: true, }, { name: "Spec: Wildcard route matches specific listener", listenerHostname: "test.example.com", routeHostname: "*.example.com", want: true, }, { name: "Spec: Specific route matches wildcard listener", listenerHostname: "*.example.com", routeHostname: "test.example.com", want: true, }, { name: "Spec: Multi-level specific route matches wildcard listener", listenerHostname: "*.example.com", routeHostname: "foo.test.example.com", want: true, }, { name: "Spec: Identical wildcards match", listenerHostname: "*.example.com", routeHostname: "*.example.com", want: true, }, // --- Explicit "Not Matching" Scenarios from Spec --- { name: "Spec: Parent domain does not match wildcard listener", listenerHostname: "*.example.com", routeHostname: "example.com", want: false, }, { name: "Spec: Different TLD does not match wildcard listener", listenerHostname: "*.example.com", routeHostname: "test.example.net", want: false, }, // --- Additional Edge Cases --- { name: "Route with more specific wildcard matches listener", listenerHostname: "*.example.com", routeHostname: "*.foo.example.com", want: true, }, { name: "Route with less specific wildcard does NOT match listener", listenerHostname: "*.foo.example.com", routeHostname: "*.example.com", want: false, }, { name: "Mismatched specific hostnames", listenerHostname: "foo.example.com", routeHostname: "bar.example.com", want: false, }, { name: "Wildcard route does not match different specific TLD", listenerHostname: "foo.example.org", routeHostname: "*.example.com", want: false, }, { name: "Wildcard route does match specific TLD", listenerHostname: "very.specific.com", routeHostname: "*.specific.com", want: true, }, { name: "Wildcard route does match partially", listenerHostname: "*.specific.com", routeHostname: "*.muchspecific.com", want: false, }, { name: "Wildcard route does match partially", listenerHostname: "*.muchspecific.com", routeHostname: "*.specific.com", want: false, }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { if got := isHostnameSubset(tc.routeHostname, tc.listenerHostname); got != tc.want { t.Errorf("isHostnameSubset(route: %q, listener: %q) = %v; want %v", tc.routeHostname, tc.listenerHostname, got, tc.want) } }) } } func TestGetIntersectingHostnames(t *testing.T) { tests := []struct { name string listener gatewayv1.Listener routeHostnames []gatewayv1.Hostname want []string }{ { name: "Listener has no hostname, route has none", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: nil, }, routeHostnames: []gatewayv1.Hostname{}, want: []string{"*"}, }, { name: "Listener has no hostname, route has one", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: nil, }, routeHostnames: []gatewayv1.Hostname{"foo.example.com"}, want: []string{"foo.example.com"}, }, { name: "Listener has no hostname, route has multiple", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: nil, }, routeHostnames: []gatewayv1.Hostname{"foo.example.com", "bar.example.com"}, want: []string{"foo.example.com", "bar.example.com"}, }, { name: "Listener has specific hostname, route has none", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: ptr.To(gatewayv1.Hostname("listener.example.com")), }, routeHostnames: []gatewayv1.Hostname{}, want: []string{"listener.example.com"}, }, { name: "Exact match", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: ptr.To(gatewayv1.Hostname("foo.example.com")), }, routeHostnames: []gatewayv1.Hostname{"foo.example.com"}, want: []string{"foo.example.com"}, }, { name: "Wildcard listener, specific route", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: ptr.To(gatewayv1.Hostname("*.example.com")), }, routeHostnames: []gatewayv1.Hostname{"foo.example.com"}, want: []string{"foo.example.com"}, }, { name: "Specific listener, wildcard route", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: ptr.To(gatewayv1.Hostname("foo.example.com")), }, routeHostnames: []gatewayv1.Hostname{"*.example.com"}, // The result is the listener's more specific hostname want: []string{"foo.example.com"}, }, { name: "Wildcard listener, more specific wildcard route", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: ptr.To(gatewayv1.Hostname("*.example.com")), }, routeHostnames: []gatewayv1.Hostname{"*.foo.example.com"}, want: []string{"*.foo.example.com"}, }, { name: "No intersection", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: ptr.To(gatewayv1.Hostname("a.com")), }, routeHostnames: []gatewayv1.Hostname{"b.com"}, want: []string{}, }, { name: "Multiple valid intersections", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: ptr.To(gatewayv1.Hostname("*.example.com")), }, routeHostnames: []gatewayv1.Hostname{"a.example.com", "b.example.com", "no.match.org"}, want: []string{"a.example.com", "b.example.com"}, }, { name: "Complex intersection with specific and wildcard", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: ptr.To(gatewayv1.Hostname("*.example.com")), }, routeHostnames: []gatewayv1.Hostname{"a.example.com", "*.b.example.com", "example.com"}, // "example.com" is not a valid subset want: []string{"a.example.com", "*.b.example.com"}, }, { name: "No intersection wildcards are per path", listener: gatewayv1.Listener{ Name: "listener-1", Hostname: ptr.To(gatewayv1.Hostname("*.wildcard.com")), }, routeHostnames: []gatewayv1.Hostname{"*.examplewildcard.com", "*.b.example.com", "example.com"}, want: []string{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := getIntersectingHostnames(tt.listener, tt.routeHostnames) if !equalUnordered(got, tt.want) { t.Errorf("getIntersectingHostnames() = %v, want %v", got, tt.want) } }) } } // equalUnordered checks if two string slices are equal, ignoring order. func equalUnordered(a, b []string) bool { if len(a) != len(b) { return false } m := make(map[string]int, len(a)) for _, v := range a { m[v]++ } for _, v := range b { if m[v] == 0 { return false } m[v]-- } return true } ================================================ FILE: pkg/ingress/README.md ================================================ # Ingress Controller (Ingress-to-Gateway Translation) This is a Kubernetes controller that implements the `networkingv1.Ingress` API specification. Its primary purpose is to act as a translation layer, not a data plane. It watches `Ingress` resources and translates their semantics into `Gateway API` resources, specifically `Gateway` and `HTTPRoute`. The actual proxying and traffic management can then be done by a separate, conformant `Gateway API` implementation that reads these translated resources. The controller's goal is to provide the exact semantics of the `Ingress` API using a standard `Gateway API` data plane. ## Architecture The controller's logic is built on four key components that work together to mimic `Ingress` behavior. ### Singleton Namespace Gateway The controller ensures that a single `gatewayv1.Gateway` resource exists for each namespace that contains a managed `Ingress`. This `Gateway` is given a predictable name (e.g., kind-ingress-gateway). It acts as the single, consolidated entry point for all `Ingress` traffic within that namespace. It is configured with a `GatewayClassName` provided to the controller, which links it to the underlying `Gateway API` data plane implementation. ### Ingress-to-HTTPRoute Translation (1:N Mapping) To correctly implement `Ingress` precedence (where exact hosts and paths are matched before wildcards or prefixes), the controller implements a 1-to-Many (1:N) mapping for each `Ingress` resource: * **Per-Host Rule:** For each rule defined in `ingress.spec.rules` (e.g., host: "foo.example.com"), a dedicated `HTTPRoute` resource is created. This `HTTPRoute` contains only the hostnames and rules (paths) for that specific host. This design correctly isolates rules per host. * **Default Backend Rule:** A single, separate `HTTPRoute` is created to handle the default backend. This route has its hostnames field omitted (nil), which in `Gateway API` semantics means it matches all traffic that wasn't captured by a more specific (per-host) `HTTPRoute`. Precedence: The controller implements `Ingress` default backend precedence. If an `Ingress` defines a rule with an empty host (host: ""), that rule's paths are used for the default backend `HTTPRoute`. If no such rule exists, the ingress.spec.defaultBackend is used as a fallback. ### Namespace-Wide TLS Aggregation The `Ingress` spec allows multiple `Ingress`es to provide TLS secrets. The controller consolidates this for the singleton Gateway. On every reconciliation, the controller scans all managed `Ingress`es in the namespace. It aggregates every unique secretName from all ingress.spec.tls sections. This combined, sorted list of secrets is set on the certificateRefs field of the singleton Gateway's HTTPS listener. This means all `Ingress`es in a namespace share one set of TLS certificates on a single listener, which is consistent with how `Ingress` is typically implemented. ### Ingress Status Reconciliation The controller watches the status of the singleton namespace Gateway. When the underlying Gateway API implementation provisions an IP or hostname for the Gateway, the controller detects this change. It copies this IP/hostname to the `status.loadBalancer` field of every managed `Ingress` in that namespace. ### Reconciliation Flow (syncHandler) The simplified logic for a single `Ingress` change (Create, Update) is as follows: 1. An `Ingress` event is received for ingress-A in namespace-foo. 2. The controller re-scans all `Ingress`es in namespace-foo to get a fresh, aggregated list of all TLS secrets. 3. It reconciles the singleton Gateway in namespace-foo, ensuring it exists and its HTTPS listener has the correct (sorted) list of aggregated secrets. 4. The controller generates a "desired" list of `HTTPRoute` resources for ingress-A (e.g., one for a.example.com and one for the default backend). 5. It lists all "existing" `HTTPRoute`s in namespace-foo that are owned by ingress-A. 6. It compares the "desired" and "existing" lists: 7. New routes are created. 8. Existing routes are updated if their spec (paths, backends) has changed. 9. Stale routes (e.g., for a host that was removed from ingress-A) are deleted. 10. The controller fetches the latest `Gateway.status` and updates ingress-A.status (and all other Ingresses in the namespace) with the provisioned IP/hostname. ## Known Incompatibilities This translation model is highly conformant but has known semantic mismatches with the `Ingress` API, as identified by the `Ingress` conformance test suite. Wildcard Host Matching: The `Ingress` API specifies that host: "*.foo.com" should not match baz.bar.foo.com (it matches only one DNS label). The Gateway API specifies that hostnames: ["*.foo.com"] does match baz.bar.foo.com (it matches all subdomains). This controller follows the Gateway API specification, as that is its underlying data plane. ================================================ FILE: pkg/ingress/controller.go ================================================ package ingress import ( "context" "crypto/sha256" // New import "encoding/hex" // New import "encoding/json" "fmt" "reflect" "sort" "sync/atomic" "time" corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/apimachinery/pkg/util/wait" corev1informers "k8s.io/client-go/informers/core/v1" networkingv1informers "k8s.io/client-go/informers/networking/v1" "k8s.io/client-go/kubernetes" corelisters "k8s.io/client-go/listers/core/v1" networkinglisters "k8s.io/client-go/listers/networking/v1" "k8s.io/client-go/tools/cache" "k8s.io/client-go/util/workqueue" "k8s.io/klog/v2" "k8s.io/utils/ptr" "sigs.k8s.io/cloud-provider-kind/pkg/config" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" gatewayclient "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned" gatewayinformers "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions/apis/v1" gatewaylisters "sigs.k8s.io/gateway-api/pkg/client/listers/apis/v1" ) const ( // IngressClassName is the name of the IngressClass resource IngressClassName = "cloud-provider-kind" // IngressClassController is the value of the spec.controller field IngressClassController = "kind.sigs.k8s.io/ingress-controller" // GatewayName is the well-defined name for the Gateway we create in each namespace GatewayName = "kind-ingress-gateway" ) // Controller is the controller implementation for Ingress resources type Controller struct { gatewayClassName string clientset kubernetes.Interface gwClientset gatewayclient.Interface ingressLister networkinglisters.IngressLister classLister networkinglisters.IngressClassLister serviceLister corelisters.ServiceLister secretLister corelisters.SecretLister httpRouteLister gatewaylisters.HTTPRouteLister gatewayLister gatewaylisters.GatewayLister ingressSynced cache.InformerSynced classSynced cache.InformerSynced serviceSynced cache.InformerSynced secretSynced cache.InformerSynced httpRouteSynced cache.InformerSynced gatewaySynced cache.InformerSynced isDefaultClass atomic.Bool workqueue workqueue.TypedRateLimitingInterface[string] } // NewController returns a new ingress controller func NewController( clientset kubernetes.Interface, gwClientset gatewayclient.Interface, gatewayClassName string, // Class for managed Gateways ingressInformer networkingv1informers.IngressInformer, ingressClassInformer networkingv1informers.IngressClassInformer, serviceInformer corev1informers.ServiceInformer, // Add Service informer secretInformer corev1informers.SecretInformer, // Add Secret informer httpRouteInformer gatewayinformers.HTTPRouteInformer, // Add HTTPRoute informer gatewayInformer gatewayinformers.GatewayInformer, // Add Gateway informer ) (*Controller, error) { controller := &Controller{ clientset: clientset, gwClientset: gwClientset, gatewayClassName: gatewayClassName, ingressLister: ingressInformer.Lister(), classLister: ingressClassInformer.Lister(), serviceLister: serviceInformer.Lister(), secretLister: secretInformer.Lister(), httpRouteLister: httpRouteInformer.Lister(), gatewayLister: gatewayInformer.Lister(), ingressSynced: ingressInformer.Informer().HasSynced, classSynced: ingressClassInformer.Informer().HasSynced, serviceSynced: serviceInformer.Informer().HasSynced, secretSynced: secretInformer.Informer().HasSynced, httpRouteSynced: httpRouteInformer.Informer().HasSynced, gatewaySynced: gatewayInformer.Informer().HasSynced, workqueue: workqueue.NewTypedRateLimitingQueueWithConfig( workqueue.DefaultTypedControllerRateLimiter[string](), workqueue.TypedRateLimitingQueueConfig[string]{Name: "ingress"}, ), } klog.Info("Setting up event handlers for Ingress controller") // Watch Ingresses _, err := ingressInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: controller.enqueueIngress, UpdateFunc: func(old, new interface{}) { controller.enqueueIngress(new) }, DeleteFunc: controller.enqueueIngress, }) if err != nil { return nil, err } // Watch IngressClasses _, err = ingressClassInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: controller.handleIngressClass, UpdateFunc: func(old, new interface{}) { controller.handleIngressClass(new) }, DeleteFunc: controller.handleIngressClass, }) if err != nil { return nil, err } // Watch Services _, err = serviceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: controller.handleObject, UpdateFunc: func(old, new interface{}) { controller.handleObject(new) }, DeleteFunc: controller.handleObject, }) if err != nil { return nil, err } // Watch Secrets _, err = secretInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: controller.handleObject, UpdateFunc: func(old, new interface{}) { controller.handleObject(new) }, DeleteFunc: controller.handleObject, }) if err != nil { return nil, err } // Watch HTTPRoutes _, err = httpRouteInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: controller.enqueueIngressFromRoute, UpdateFunc: func(old, new interface{}) { controller.enqueueIngressFromRoute(new) }, DeleteFunc: controller.enqueueIngressFromRoute, }) if err != nil { return nil, err } // Watch Gateways _, err = gatewayInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: controller.handleGateway, UpdateFunc: func(old, new interface{}) { controller.handleGateway(new) }, DeleteFunc: controller.handleGateway, }) if err != nil { return nil, err } return controller, nil } func (c *Controller) Init(ctx context.Context) error { logger := klog.FromContext(ctx).WithValues("controller", "ingress", "ingressClass", IngressClassName) // Wait for the caches to be synced before starting workers logger.Info("Waiting for informer caches to sync") if !cache.WaitForCacheSync(ctx.Done(), c.ingressSynced, c.classSynced, c.serviceSynced, c.secretSynced, c.httpRouteSynced, c.gatewaySynced, // Wait for Gateway cache ) { return fmt.Errorf("failed to wait for caches to sync") } _, err := c.classLister.Get(IngressClassName) if err == nil { logger.Info("IngressClass already exists") return nil } if !errors.IsNotFound(err) { return fmt.Errorf("failed to get IngressClass '%s': %v", IngressClassName, err) } logger.Info("IngressClass not found, creating...") ingressClass := &networkingv1.IngressClass{ ObjectMeta: metav1.ObjectMeta{ Name: IngressClassName, }, Spec: networkingv1.IngressClassSpec{ Controller: IngressClassController, }, } if config.DefaultConfig.IngressDefault { ingressClass.Annotations = map[string]string{ networkingv1.AnnotationIsDefaultIngressClass: "true", } } _, createErr := c.clientset.NetworkingV1().IngressClasses().Create(ctx, ingressClass, metav1.CreateOptions{}) return createErr } // Run will set up the event handlers for types we are interested in, as well // as start processing components for the specified number of workers. func (c *Controller) Run(ctx context.Context, workers int) { logger := klog.FromContext(ctx).WithName("ingress") ctx = klog.NewContext(ctx, logger) defer runtime.HandleCrash() defer c.workqueue.ShutDown() logger.Info("Starting Ingress controller") logger.Info("Starting workers") for i := 0; i < workers; i++ { go wait.UntilWithContext(ctx, c.runWorker, time.Second) } logger.Info("Started workers") <-ctx.Done() logger.Info("Shutting down workers") } func (c *Controller) runWorker(ctx context.Context) { for c.processNextWorkItem(ctx) { } } func (c *Controller) processNextWorkItem(ctx context.Context) bool { key, shutdown := c.workqueue.Get() if shutdown { return false } err := func() error { defer c.workqueue.Done(key) // Run the syncHandler if err := c.syncHandler(ctx, key); err != nil { // Requeue on error c.workqueue.AddRateLimited(key) return fmt.Errorf("error syncing '%s': %s, requeueing", key, err.Error()) } c.workqueue.Forget(key) return nil }() if err != nil { runtime.HandleError(err) } return true } // syncHandler compares the actual state with the desired, and attempts to // converge the two. func (c *Controller) syncHandler(ctx context.Context, key string) error { namespace, name, err := cache.SplitMetaNamespaceKey(key) if err != nil { runtime.HandleError(fmt.Errorf("invalid resource key: %s", key)) return nil } logger := klog.FromContext(ctx).WithValues("ingress", klog.KRef(namespace, name)) // Get the Ingress resource ingress, err := c.ingressLister.Ingresses(namespace).Get(name) if err != nil { if errors.IsNotFound(err) { // Kubernetes Garbage Collection will delete child HTTPRoutes due to OwnerReference. // We only need to reconcile the Gateway in case this Ingress // was the last one providing a particular TLS secret. logger.V(4).Info("Ingress deleted. Reconciling Gateway.") if _, err := c.reconcileNamespaceGateway(ctx, namespace); err != nil { // We still return an error to requeue, as Gateway reconciliation // might fail temporarily (e.g., API server issues). return fmt.Errorf("failed to reconcile Gateway after Ingress deletion: %w", err) } return nil } return err } // Check if this Ingress is for us if !c.isIngressForUs(ingress) { logger.V(4).Info("Skipping Ingress: not for this controller") // TODO: If we *used* to own it, we should delete the HTTPRoutes // and reconcile the Gateway. For now, we assume GC handles routes. return nil } // === 1. Ensure Namespace Gateway Exists & is Up-to-Date === // This reconciles TLS secrets from ALL Ingresses in the namespace. gateway, err := c.reconcileNamespaceGateway(ctx, namespace) if err != nil { return fmt.Errorf("failed to reconcile Gateway %s/%s: %w", namespace, GatewayName, err) } // === 2. Reconcile HTTPRoutes (1-to-Many) === logger.V(4).Info("Reconciling HTTPRoutes for Ingress") // Generate the desired state desiredRoutes, err := c.generateDesiredHTTPRoutes(ingress, gateway.Name, gateway.Namespace) if err != nil { return fmt.Errorf("failed to generate desired HTTPRoutes: %w", err) } // Get the actual state allRoutes, err := c.httpRouteLister.HTTPRoutes(namespace).List(labels.Everything()) if err != nil { return fmt.Errorf("failed to list existing HTTPRoutes: %w", err) } existingRoutes := make(map[string]*gatewayv1.HTTPRoute) for _, route := range allRoutes { if metav1.IsControlledBy(route, ingress) { existingRoutes[route.Name] = route } } // Reconcile: Create/Update for routeName, desiredRoute := range desiredRoutes { logger = logger.WithValues("httpRoute", klog.KRef(desiredRoute.Namespace, desiredRoute.Name)) existingRoute, exists := existingRoutes[routeName] if !exists { // Create logger.V(2).Info("Creating HTTPRoute") _, createErr := c.gwClientset.GatewayV1().HTTPRoutes(namespace).Create(ctx, desiredRoute, metav1.CreateOptions{}) if createErr != nil { logger.Error(createErr, "Failed to create HTTPRoute") return fmt.Errorf("failed to create HTTPRoute: %w", createErr) } } else if !reflect.DeepEqual(existingRoute.Spec, desiredRoute.Spec) || !reflect.DeepEqual(existingRoute.OwnerReferences, desiredRoute.OwnerReferences) { logger.V(2).Info("Updating HTTPRoute") routeCopy := existingRoute.DeepCopy() routeCopy.Spec = desiredRoute.Spec routeCopy.OwnerReferences = desiredRoute.OwnerReferences _, updateErr := c.gwClientset.GatewayV1().HTTPRoutes(namespace).Update(ctx, routeCopy, metav1.UpdateOptions{}) if updateErr != nil { logger.Error(updateErr, "Failed to update HTTPRoute") return fmt.Errorf("failed to update HTTPRoute: %w", updateErr) } } // Remove from map so we can find stale routes delete(existingRoutes, routeName) } // Reconcile: Delete (stale routes) for routeName, routeToDelete := range existingRoutes { logger = logger.WithValues("httpRoute", klog.KRef(routeToDelete.Namespace, routeName)) logger.V(2).Info("Deleting stale HTTPRoute") deleteErr := c.gwClientset.GatewayV1().HTTPRoutes(namespace).Delete(ctx, routeName, metav1.DeleteOptions{}) if deleteErr != nil && !errors.IsNotFound(deleteErr) { logger.Error(deleteErr, "Failed to delete stale HTTPRoute") return fmt.Errorf("failed to delete stale HTTPRoute: %w", deleteErr) } } // === 3. Update Ingress Status === // We use the Gateway object we found/created. Its status might be stale // from the lister if it was just created. We grab the latest. latestGateway, err := c.gwClientset.GatewayV1().Gateways(gateway.Namespace).Get(ctx, gateway.Name, metav1.GetOptions{}) if err != nil { return fmt.Errorf("failed to get latest Gateway %s/%s for status update: %w", gateway.Namespace, gateway.Name, err) } return c.updateIngressStatus(ctx, ingress, latestGateway) } // reconcileNamespaceGateway ensures the namespace Gateway exists and // its TLS configuration is in sync with ALL Ingresses in the namespace. func (c *Controller) reconcileNamespaceGateway(ctx context.Context, namespace string) (*gatewayv1.Gateway, error) { // 1. Aggregate all unique secret names from all Ingresses in this namespace ingresses, err := c.ingressLister.Ingresses(namespace).List(labels.Everything()) if err != nil { return nil, fmt.Errorf("failed to list Ingresses in namespace %s: %w", namespace, err) } secretNames := make(map[string]struct{}) for _, ing := range ingresses { if !c.isIngressForUs(ing) { continue } for _, tls := range ing.Spec.TLS { if tls.SecretName != "" { secretNames[tls.SecretName] = struct{}{} } } } // 2. Build the list of SecretObjectReferences certRefs := []gatewayv1.SecretObjectReference{} for secretName := range secretNames { certRefs = append(certRefs, gatewayv1.SecretObjectReference{ Name: gatewayv1.ObjectName(secretName), }) } // Sort the slice to prevent flapping updates sort.Slice(certRefs, func(i, j int) bool { return certRefs[i].Name < certRefs[j].Name }) desiredListeners := []gatewayv1.Listener{ { Name: "http", Port: 80, Protocol: gatewayv1.HTTPProtocolType, AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{ From: ptr.To(gatewayv1.NamespacesFromSame), // Only allow HTTPRoutes from the same namespace }, }, }, } // 3. Define the desired listeners tlsMode := gatewayv1.TLSModeTerminate // always use Terminate for Ingress TLS var finalCertRefs []gatewayv1.SecretObjectReference if len(certRefs) > 0 { finalCertRefs = certRefs desiredListeners = append(desiredListeners, gatewayv1.Listener{ Name: "https", Port: 443, Protocol: gatewayv1.HTTPSProtocolType, AllowedRoutes: &gatewayv1.AllowedRoutes{ Namespaces: &gatewayv1.RouteNamespaces{ From: ptr.To(gatewayv1.NamespacesFromSame), // Only allow HTTPRoutes from the same namespace }, }, TLS: &gatewayv1.ListenerTLSConfig{ Mode: &tlsMode, CertificateRefs: finalCertRefs, // Set the aggregated certs }, }) } logger := klog.FromContext(ctx).WithValues( "gateway", klog.KRef(namespace, GatewayName), "gatewayClass", c.gatewayClassName, ) // 4. Get or Create the Gateway gw, err := c.gatewayLister.Gateways(namespace).Get(GatewayName) if err != nil { if !errors.IsNotFound(err) { return nil, fmt.Errorf("failed to get Gateway %s/%s: %w", namespace, GatewayName, err) } // Not found, create it logger.V(2).Info("Creating Gateway for class") newGw := &gatewayv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: GatewayName, Namespace: namespace, }, Spec: gatewayv1.GatewaySpec{ GatewayClassName: gatewayv1.ObjectName(c.gatewayClassName), Listeners: desiredListeners, }, } return c.gwClientset.GatewayV1().Gateways(namespace).Create(ctx, newGw, metav1.CreateOptions{}) } // 5. Gateway exists, check if update is needed if !reflect.DeepEqual(gw.Spec.Listeners, desiredListeners) { logger.V(2).Info("Updating Gateway with new listener configuration") // This avoids conflicts with the gateway-controller updating status. patch := map[string]interface{}{ "spec": map[string]interface{}{ "listeners": desiredListeners, }, } patchBytes, err := json.Marshal(patch) if err != nil { return nil, fmt.Errorf("failed to marshal patch for Gateway %s/%s: %w", namespace, GatewayName, err) } return c.gwClientset.GatewayV1().Gateways(namespace).Patch(ctx, gw.Name, types.MergePatchType, patchBytes, metav1.PatchOptions{}) } // No update needed return gw, nil } // generateRouteName creates a stable, DNS-1123 compliant name for an HTTPRoute // based on its parent Ingress and the host rule. func generateRouteName(ingressName, host string) string { if host == "" { // This is for the default backend return fmt.Sprintf("%s-default-backend", ingressName) } // Use a hash of the host to ensure stability and DNS-1123 compliance hash := sha256.Sum256([]byte(host)) // Truncate hash to 10 chars for brevity return fmt.Sprintf("%s-%s", ingressName, hex.EncodeToString(hash[:])[:10]) } // resolveBackendPort resolves the port number for a given Ingress service backend func (c *Controller) resolveBackendPort(ns string, svcBackend *networkingv1.IngressServiceBackend) (int32, error) { switch svcPort := svcBackend.Port; { case svcPort.Number != 0: return svcPort.Number, nil case svcPort.Name != "": // We need to look up the port number from the Service svc, err := c.serviceLister.Services(ns).Get(svcBackend.Name) if err != nil { return 0, fmt.Errorf("failed to get service %s/%s for port name %s: %w", ns, svcBackend.Name, svcPort.Name, err) } // Find the port for _, port := range svc.Spec.Ports { if port.Name == svcPort.Name { return port.Port, nil } } return 0, fmt.Errorf("port name %s not found in service %s/%s", svcPort.Name, ns, svcBackend.Name) default: return 0, fmt.Errorf("backend service %s has no port defined", svcBackend.Name) } } // translateIngressPaths translates Ingress paths to HTTPRouteRules, // ensuring Ingress precedence (Exact > Prefix, Longest > Shortest). func (c *Controller) translateIngressPaths(ns string, paths []networkingv1.HTTPIngressPath) ([]gatewayv1.HTTPRouteRule, error) { var rules []gatewayv1.HTTPRouteRule // Create a copy to sort pathCopy := make([]networkingv1.HTTPIngressPath, len(paths)) copy(pathCopy, paths) // Sort paths to respect Ingress precedence sort.Slice(pathCopy, func(i, j int) bool { pathI := pathCopy[i] pathJ := pathCopy[j] typeI := ptr.Deref(pathI.PathType, networkingv1.PathTypePrefix) typeJ := ptr.Deref(pathJ.PathType, networkingv1.PathTypePrefix) // 1. Exact comes before Prefix if typeI == networkingv1.PathTypeExact && typeJ != networkingv1.PathTypeExact { return true } if typeI != networkingv1.PathTypeExact && typeJ == networkingv1.PathTypeExact { return false } // 2. If both are same type, longest path wins return len(pathI.Path) > len(pathJ.Path) }) for _, ingressPath := range pathCopy { // Determine Path Match Type var pathMatch gatewayv1.HTTPPathMatch pathType := ptr.Deref(ingressPath.PathType, networkingv1.PathTypePrefix) switch pathType { case networkingv1.PathTypePrefix: pathMatch.Type = ptr.To(gatewayv1.PathMatchPathPrefix) pathMatch.Value = &ingressPath.Path case networkingv1.PathTypeExact: pathMatch.Type = ptr.To(gatewayv1.PathMatchExact) pathMatch.Value = &ingressPath.Path case networkingv1.PathTypeImplementationSpecific: // Fallback (e.g., ImplementationSpecific) pathMatch.Type = ptr.To(gatewayv1.PathMatchPathPrefix) pathMatch.Value = &ingressPath.Path default: return nil, fmt.Errorf("unsupported path type: %s", pathType) } // Determine Backend Port port, err := c.resolveBackendPort(ns, ingressPath.Backend.Service) if err != nil { // This error will cause the Ingress to be requeued return nil, fmt.Errorf("error resolving port for path %s: %w", ingressPath.Path, err) } portPtr := port // Take address of a copy // Create BackendRef backendRef := gatewayv1.HTTPBackendRef{ BackendRef: gatewayv1.BackendRef{ BackendObjectReference: gatewayv1.BackendObjectReference{ Name: gatewayv1.ObjectName(ingressPath.Backend.Service.Name), Kind: ptr.To(gatewayv1.Kind("Service")), Group: ptr.To(gatewayv1.Group("")), Port: &portPtr, }, Weight: ptr.To(int32(1)), }, } // Create HTTPRouteRule httpRule := gatewayv1.HTTPRouteRule{ Matches: []gatewayv1.HTTPRouteMatch{ { Path: &pathMatch, }, }, BackendRefs: []gatewayv1.HTTPBackendRef{backendRef}, } rules = append(rules, httpRule) } return rules, nil } // generateDesiredHTTPRoutes generates a map of all HTTPRoutes that should // exist for a given Ingress. func (c *Controller) generateDesiredHTTPRoutes(ingress *networkingv1.Ingress, gatewayName string, gatewayNamespace string) (map[string]*gatewayv1.HTTPRoute, error) { desiredRoutes := make(map[string]*gatewayv1.HTTPRoute) // Set OwnerReference ownerRef := metav1.NewControllerRef(ingress, networkingv1.SchemeGroupVersion.WithKind("Ingress")) // Base ParentReference parentRef := gatewayv1.ParentReference{ Name: gatewayv1.ObjectName(gatewayName), Namespace: ptr.To(gatewayv1.Namespace(gatewayNamespace)), Kind: ptr.To(gatewayv1.Kind("Gateway")), Group: ptr.To(gatewayv1.Group(gatewayv1.GroupName)), } var defaultPaths []networkingv1.HTTPIngressPath var hasDefaultRule bool // 1. Separate per-host rules from default (host: "") rules for _, ingressRule := range ingress.Spec.Rules { if ingressRule.Host == "" { // This is a default rule. Collect its paths. if ingressRule.HTTP != nil { defaultPaths = append(defaultPaths, ingressRule.HTTP.Paths...) hasDefaultRule = true } continue // Skip per-host route creation } if ingressRule.HTTP == nil { continue } routeName := generateRouteName(ingress.Name, ingressRule.Host) hostnames := []gatewayv1.Hostname{gatewayv1.Hostname(ingressRule.Host)} // Translate paths for this rule rules, err := c.translateIngressPaths(ingress.Namespace, ingressRule.HTTP.Paths) if err != nil { // Propagate error (e.g., service port not found) return nil, fmt.Errorf("failed to translate paths for host %s: %w", ingressRule.Host, err) } // Construct the per-host HTTPRoute httpRoute := &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Name: routeName, Namespace: ingress.Namespace, OwnerReferences: []metav1.OwnerReference{*ownerRef}, }, Spec: gatewayv1.HTTPRouteSpec{ CommonRouteSpec: gatewayv1.CommonRouteSpec{ ParentRefs: []gatewayv1.ParentReference{parentRef}, }, Hostnames: hostnames, // This is correct, as host is not empty Rules: rules, }, } desiredRoutes[routeName] = httpRoute } // 2. Handle DefaultBackend (either from explicit rule or spec.defaultBackend) var defaultRules []gatewayv1.HTTPRouteRule var err error if hasDefaultRule { //nolint:gocritic // Case 1: A rule with host: "" exists. This takes precedence. defaultRules, err = c.translateIngressPaths(ingress.Namespace, defaultPaths) if err != nil { return nil, fmt.Errorf("failed to translate paths for default rule: %w", err) } } else if ingress.Spec.DefaultBackend != nil { // Case 2: No host: "" rule, but spec.defaultBackend exists. port, err := c.resolveBackendPort(ingress.Namespace, ingress.Spec.DefaultBackend.Service) if err != nil { return nil, fmt.Errorf("failed to resolve port for default backend: %w", err) } portPtr := port // Take address of a copy defaultRules = []gatewayv1.HTTPRouteRule{ { // No matches means it's the default BackendRefs: []gatewayv1.HTTPBackendRef{ { BackendRef: gatewayv1.BackendRef{ BackendObjectReference: gatewayv1.BackendObjectReference{ Name: gatewayv1.ObjectName(ingress.Spec.DefaultBackend.Service.Name), Kind: ptr.To(gatewayv1.Kind("Service")), Group: ptr.To(gatewayv1.Group("")), Port: &portPtr, }, Weight: ptr.To(int32(1)), }, }, }, }, } } else { // Case 3: No default backend rules and no spec.defaultBackend. // We are done. return desiredRoutes, nil } // 3. Create the single default HTTPRoute routeName := generateRouteName(ingress.Name, "") // Name is the same for both default cases httpRoute := &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Name: routeName, Namespace: ingress.Namespace, OwnerReferences: []metav1.OwnerReference{*ownerRef}, }, Spec: gatewayv1.HTTPRouteSpec{ CommonRouteSpec: gatewayv1.CommonRouteSpec{ ParentRefs: []gatewayv1.ParentReference{parentRef}, }, // Hostnames field is OMITTED (nil), which correctly // tells the Gateway to match all hosts. Rules: defaultRules, }, } desiredRoutes[routeName] = httpRoute return desiredRoutes, nil } // updateIngressStatus updates the Ingress status with the Gateway's IP func (c *Controller) updateIngressStatus(ctx context.Context, ingress *networkingv1.Ingress, gateway *gatewayv1.Gateway) error { logger := klog.FromContext(ctx).WithValues("ingress", klog.KObj(ingress)) // Get the *latest* version of the Ingress to avoid update conflicts latestIngress, err := c.clientset.NetworkingV1().Ingresses(ingress.Namespace).Get(ctx, ingress.Name, metav1.GetOptions{}) if err != nil { if errors.IsNotFound(err) { // Ingress was deleted, nothing to update return nil } return fmt.Errorf("failed to get latest Ingress %s/%s: %w", ingress.Namespace, ingress.Name, err) } // Find an IP address in the Gateway status ips := []string{} hostnames := []string{} for _, addr := range gateway.Status.Addresses { if addr.Type != nil && *addr.Type == gatewayv1.HostnameAddressType { hostnames = append(hostnames, addr.Value) } if addr.Type != nil && (*addr.Type == gatewayv1.IPAddressType) { ips = append(ips, addr.Value) } } if len(ips) == 0 && len(hostnames) == 0 { // No IP address found yet. The Gateway controller hasn't finished. // Return an error to trigger a rate-limited requeue. return fmt.Errorf("gateway %s/%s has no IP or Hostname address in status yet", gateway.Namespace, gateway.Name) } // Construct the new status lbStatus := &networkingv1.IngressLoadBalancerStatus{} for _, ip := range ips { lbStatus.Ingress = append(lbStatus.Ingress, networkingv1.IngressLoadBalancerIngress{IP: ip}) } for _, hostname := range hostnames { lbStatus.Ingress = append(lbStatus.Ingress, networkingv1.IngressLoadBalancerIngress{Hostname: hostname}) } // Check if status is already up-to-date if reflect.DeepEqual(latestIngress.Status.LoadBalancer, *lbStatus) { logger.V(4).Info("Ingress status already up to date.") return nil } ingressCopy := latestIngress.DeepCopy() ingressCopy.Status.LoadBalancer = *lbStatus _, err = c.clientset.NetworkingV1().Ingresses(ingress.Namespace).UpdateStatus(ctx, ingressCopy, metav1.UpdateOptions{}) if err != nil { return fmt.Errorf("failed to update ingress status: %w", err) } logger.V(2).Info("Successfully updated status for Ingress", "IPs", ips, "hostNames", hostnames) return nil } // enqueueIngress takes an Ingress resource and converts it into a // namespace/name string which is then put onto the work queue. func (c *Controller) enqueueIngress(obj interface{}) { var key string var err error if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { runtime.HandleError(err) return } klog.V(4).Infof("Enqueuing Ingress %s", key) c.workqueue.Add(key) } // enqueueIngressFromRoute finds the owning Ingress for an HTTPRoute and enqueues it func (c *Controller) enqueueIngressFromRoute(obj interface{}) { route, ok := obj.(*gatewayv1.HTTPRoute) if !ok { tombstone, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { runtime.HandleError(fmt.Errorf("error decoding object, invalid type")) return } route, ok = tombstone.Obj.(*gatewayv1.HTTPRoute) if !ok { runtime.HandleError(fmt.Errorf("error decoding object tombstone, invalid type")) return } } // Find the Ingress owner ownerRef := metav1.GetControllerOf(route) if ownerRef == nil { return } // Check if the owner is an Ingress if ownerRef.APIVersion == networkingv1.SchemeGroupVersion.String() && ownerRef.Kind == "Ingress" { // Enqueue the Ingress key := route.Namespace + "/" + ownerRef.Name klog.V(4).Infof("Enqueuing Ingress %s due to change in HTTPRoute %s", key, route.Name) c.workqueue.Add(key) } } // handleIngressClass checks if we are the default class func (c *Controller) handleIngressClass(obj interface{}) { class, ok := obj.(*networkingv1.IngressClass) if !ok { return } if class.Name != IngressClassName { return } // Check if this is the default class isDefault := false val, ok := class.Annotations[networkingv1.AnnotationIsDefaultIngressClass] if ok && val == "true" { isDefault = true } if config.DefaultConfig.IngressDefault && !isDefault { klog.Infof("'%s' is now the default IngressClass", IngressClassName) _, err := c.clientset.NetworkingV1().IngressClasses().Patch(context.TODO(), IngressClassName, types.MergePatchType, []byte(`{"metadata":{"annotations":{"`+networkingv1.AnnotationIsDefaultIngressClass+`":"true"}}}`), metav1.PatchOptions{}) if err != nil { klog.Errorf("Failed to patch IngressClass %s: %v", IngressClassName, err) } isDefault = true } if isDefault != c.isDefaultClass.Load() { { if isDefault { klog.Infof("'%s' is now the default IngressClass", IngressClassName) } else { klog.Infof("'%s' is no longer the default IngressClass", IngressClassName) } c.isDefaultClass.Store(isDefault) // Re-enqueue all Ingresses that might be affected by this change c.enqueueAllIngresses() } } } // handleObject will take any resource implementing metav1.Object // and find any Ingress that references it, adding that Ingress to the // work queue. func (c *Controller) handleObject(obj interface{}) { var object metav1.Object var ok bool if object, ok = obj.(metav1.Object); !ok { // handle delete tombstones tombstone, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { runtime.HandleError(fmt.Errorf("error decoding object, invalid type")) return } object, ok = tombstone.Obj.(metav1.Object) if !ok { runtime.HandleError(fmt.Errorf("error decoding object tombstone, invalid type")) return } } // List all ingresses ingresses, err := c.ingressLister.List(labels.Everything()) if err != nil { klog.Errorf("Failed to list Ingresses: %v", err) return } for _, ingress := range ingresses { if !c.isIngressForUs(ingress) { continue } // Check if this ingress references the object switch obj.(type) { case *corev1.Service: if c.ingressReferencesService(ingress, object.GetNamespace(), object.GetName()) { klog.V(4).Infof("Enqueuing Ingress %s/%s due to change in Service %s/%s", ingress.Namespace, ingress.Name, object.GetNamespace(), object.GetName()) c.enqueueIngress(ingress) } case *corev1.Secret: if c.ingressReferencesSecret(ingress, object.GetNamespace(), object.GetName()) { klog.V(4).Infof("Enqueuing Ingress %s/%s due to change in Secret %s/%s", ingress.Namespace, ingress.Name, object.GetNamespace(), object.GetName()) // When a secret changes, we must re-enqueue ALL ingresses in that namespace // to re-calculate the Gateway's aggregated certificate list. c.enqueueAllIngressesInNamespace(object.GetNamespace()) } } } } // handleGateway re-enqueues all Ingresses in a namespace when its Gateway changes func (c *Controller) handleGateway(obj interface{}) { gw, ok := obj.(*gatewayv1.Gateway) if !ok { tombstone, ok := obj.(cache.DeletedFinalStateUnknown) if !ok { runtime.HandleError(fmt.Errorf("error decoding object, invalid type")) return } gw, ok = tombstone.Obj.(*gatewayv1.Gateway) if !ok { runtime.HandleError(fmt.Errorf("error decoding object tombstone, invalid type")) return } } // If this is one of our managed Gateways, re-enqueue all Ingresses in that namespace // This is critical for updating Ingress status when the Gateway gets an IP if gw.Name == GatewayName { klog.V(4).Infof("Gateway %s/%s changed, re-enqueuing all Ingresses in namespace %s", gw.Namespace, gw.Name, gw.Namespace) c.enqueueAllIngressesInNamespace(gw.Namespace) } } // enqueueAllIngressesInNamespace enqueues all Ingresses for a specific namespace func (c *Controller) enqueueAllIngressesInNamespace(namespace string) { ingresses, err := c.ingressLister.Ingresses(namespace).List(labels.Everything()) if err != nil { klog.Errorf("Failed to list Ingresses in namespace %s: %v", namespace, err) return } for _, ingress := range ingresses { c.enqueueIngress(ingress) } } func (c *Controller) ingressReferencesService(ingress *networkingv1.Ingress, ns, name string) bool { if ingress.Namespace != ns { return false } if ingress.Spec.DefaultBackend != nil && ingress.Spec.DefaultBackend.Service.Name == name { return true } for _, rule := range ingress.Spec.Rules { if rule.HTTP == nil { continue } for _, path := range rule.HTTP.Paths { if path.Backend.Service.Name == name { return true } } } return false } func (c *Controller) ingressReferencesSecret(ingress *networkingv1.Ingress, ns, name string) bool { if ingress.Namespace != ns { return false } for _, tls := range ingress.Spec.TLS { if tls.SecretName == name { return true } } return false } // isIngressForUs checks if an Ingress belongs to this controller func (c *Controller) isIngressForUs(ingress *networkingv1.Ingress) bool { // Case 1: Ingress specifies IngressClassName if ingress.Spec.IngressClassName != nil { return *ingress.Spec.IngressClassName == IngressClassName } // Case 2: No IngressClassName, check if we are default return c.isDefaultClass.Load() } func (c *Controller) enqueueAllIngresses() { ingresses, err := c.ingressLister.List(labels.Everything()) if err != nil { klog.Errorf("Failed to list all Ingresses: %v", err) return } klog.Info("Enqueuing all Ingresses due to IngressClass change") for _, ingress := range ingresses { c.enqueueIngress(ingress) } } ================================================ FILE: pkg/ingress/controller_test.go ================================================ package ingress import ( "context" // New import // New import "fmt" "reflect" // Import reflect for DeepEqual "strings" // Import standard library strings package "testing" "time" corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/wait" kubeinformers "k8s.io/client-go/informers" fakekube "k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/tools/cache" "k8s.io/utils/ptr" // Added for pointer helpers gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" fakegateway "sigs.k8s.io/gateway-api/pkg/client/clientset/versioned/fake" gatewayinformers "sigs.k8s.io/gateway-api/pkg/client/informers/externalversions" ) const ( testGatewayClassName = "test-gw-class" testNamespace = "test-ns" testIngressName = "test-ingress" testServiceName = "test-service" testServicePortName = "http" testServicePortNum = 8080 ) // testFixture holds all the components needed for a test type testFixture struct { t *testing.T ctx context.Context cancel context.CancelFunc controller *Controller kubeClient *fakekube.Clientset gwClient *fakegateway.Clientset kubeObjects []runtime.Object gwObjects []runtime.Object } // newTestFixture creates a new test harness func newTestFixture(t *testing.T, kubeObjects []runtime.Object, gwObjects []runtime.Object) *testFixture { ctx, cancel := context.WithCancel(context.Background()) kubeClient := fakekube.NewSimpleClientset(kubeObjects...) gwClient := fakegateway.NewSimpleClientset(gwObjects...) kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeClient, 0) gwInformerFactory := gatewayinformers.NewSharedInformerFactory(gwClient, 0) // Get informers ingInformer := kubeInformerFactory.Networking().V1().Ingresses() classInformer := kubeInformerFactory.Networking().V1().IngressClasses() svcInformer := kubeInformerFactory.Core().V1().Services() secretInformer := kubeInformerFactory.Core().V1().Secrets() routeInformer := gwInformerFactory.Gateway().V1().HTTPRoutes() gwInformer := gwInformerFactory.Gateway().V1().Gateways() // Create controller controller, err := NewController( kubeClient, gwClient, testGatewayClassName, ingInformer, classInformer, svcInformer, secretInformer, routeInformer, gwInformer, ) if err != nil { t.Fatalf("Failed to create controller: %v", err) } // Start informers kubeInformerFactory.Start(ctx.Done()) gwInformerFactory.Start(ctx.Done()) // Wait for caches to sync ok := cache.WaitForCacheSync(ctx.Done(), ingInformer.Informer().HasSynced, classInformer.Informer().HasSynced, svcInformer.Informer().HasSynced, secretInformer.Informer().HasSynced, routeInformer.Informer().HasSynced, gwInformer.Informer().HasSynced, ) if !ok { t.Fatalf("Failed to sync caches") } return &testFixture{ t: t, ctx: ctx, cancel: cancel, controller: controller, kubeClient: kubeClient, gwClient: gwClient, kubeObjects: kubeObjects, gwObjects: gwObjects, } } // --- Helper Objects --- func newTestIngressClass() *networkingv1.IngressClass { return &networkingv1.IngressClass{ ObjectMeta: metav1.ObjectMeta{ Name: IngressClassName, }, Spec: networkingv1.IngressClassSpec{ Controller: IngressClassController, }, } } func newTestService(name string) *corev1.Service { return &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: testNamespace, }, Spec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ { Name: testServicePortName, Port: int32(testServicePortNum), }, { Name: "other-port", Port: 8181, }, }, }, } } func newTestIngress(name string) *networkingv1.Ingress { pathType := networkingv1.PathTypePrefix return &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: testNamespace, }, Spec: networkingv1.IngressSpec{ IngressClassName: ptr.To(IngressClassName), Rules: []networkingv1.IngressRule{ { Host: "example.com", IngressRuleValue: networkingv1.IngressRuleValue{ HTTP: &networkingv1.HTTPIngressRuleValue{ Paths: []networkingv1.HTTPIngressPath{ { Path: "/", PathType: &pathType, Backend: networkingv1.IngressBackend{ Service: &networkingv1.IngressServiceBackend{ Name: testServiceName, Port: networkingv1.ServiceBackendPort{ Name: testServicePortName, // Test port name translation }, }, }, }, }, }, }, }, }, }, } } // --- Test Cases --- func TestSyncHandler_Create(t *testing.T) { // 1. Setup ingress := newTestIngress(testIngressName) service := newTestService(testServiceName) ingressClass := newTestIngressClass() f := newTestFixture(t, []runtime.Object{ingress, service, ingressClass}, // kube objects []runtime.Object{}, // gateway objects ) defer f.cancel() key := fmt.Sprintf("%s/%s", testNamespace, testIngressName) // 2. First Sync: Create Gateway, HTTPRoute. Fails on status update. err := f.controller.syncHandler(f.ctx, key) if err == nil { t.Fatalf("First sync should fail waiting for Gateway IP, but got nil error") } if !strings.Contains(err.Error(), "has no IP or Hostname address in status yet") { t.Errorf("Error message should indicate missing IP, but got: %v", err) } // 3. Assert Gateway was created gw, err := f.gwClient.GatewayV1().Gateways(testNamespace).Get(f.ctx, GatewayName, metav1.GetOptions{}) if err != nil { t.Fatalf("Failed to get created Gateway: %v", err) } if string(gw.Spec.GatewayClassName) != testGatewayClassName { t.Errorf("Gateway Class: expected %q, got %q", testGatewayClassName, gw.Spec.GatewayClassName) } // With no Ingress TLS, Gateway should only have 1 listener (http) if len(gw.Spec.Listeners) != 1 { t.Errorf("Gateway Listeners: expected %d, got %d", 1, len(gw.Spec.Listeners)) } if string(gw.Spec.Listeners[0].Name) != "http" { t.Errorf("Listener 0: expected %q, got %q", "http", gw.Spec.Listeners[0].Name) } // 4. Assert HTTPRoute was created (with the new name) expectedRouteName := generateRouteName(testIngressName, "example.com") route, err := f.gwClient.GatewayV1().HTTPRoutes(testNamespace).Get(f.ctx, expectedRouteName, metav1.GetOptions{}) if err != nil { t.Fatalf("Failed to get created HTTPRoute: %v", err) } if route.Name != expectedRouteName { t.Errorf("HTTPRoute Name: expected %q, got %q", expectedRouteName, route.Name) } // Check Hostname if len(route.Spec.Hostnames) != 1 || route.Spec.Hostnames[0] != "example.com" { t.Errorf("HTTPRoute Hostnames: expected [\"example.com\"], got %v", route.Spec.Hostnames) } // Check ParentRef if len(route.Spec.ParentRefs) != 1 { t.Fatalf("HTTPRoute ParentRefs: expected %d, got %d", 1, len(route.Spec.ParentRefs)) } if string(route.Spec.ParentRefs[0].Name) != GatewayName { t.Errorf("ParentRef Name: expected %q, got %q", GatewayName, route.Spec.ParentRefs[0].Name) } if route.Spec.ParentRefs[0].Namespace == nil || string(*route.Spec.ParentRefs[0].Namespace) != testNamespace { t.Errorf("ParentRef Namespace: expected %q, got %v", testNamespace, route.Spec.ParentRefs[0].Namespace) } // Check BackendRef (port name was translated to number) if len(route.Spec.Rules) != 1 { t.Fatalf("HTTPRoute Rules: expected %d, got %d", 1, len(route.Spec.Rules)) } if len(route.Spec.Rules[0].BackendRefs) != 1 { t.Fatalf("HTTPRoute BackendRefs: expected %d, got %d", 1, len(route.Spec.Rules[0].BackendRefs)) } if route.Spec.Rules[0].BackendRefs[0].Port == nil || int32(*route.Spec.Rules[0].BackendRefs[0].Port) != int32(testServicePortNum) { t.Errorf("BackendRef Port: expected %d, got %v", testServicePortNum, route.Spec.Rules[0].BackendRefs[0].Port) } // 5. Fake the Gateway Controller: Update Gateway status with a Hostname gw.Status.Addresses = []gatewayv1.GatewayStatusAddress{ { Type: ptr.To(gatewayv1.HostnameAddressType), Value: "my-test-lb.com", }, } _, err = f.gwClient.GatewayV1().Gateways(testNamespace).UpdateStatus(f.ctx, gw, metav1.UpdateOptions{}) if err != nil { t.Fatalf("Failed to update Gateway status: %v", err) } // 6. Second Sync: Update Ingress Status // We must use a poller here because the controller's informer needs to see the status update. // The handleGateway function will re-enqueue the Ingress. err = wait.PollUntilContextTimeout(f.ctx, 100*time.Millisecond, 5*time.Second, false, func(ctx context.Context) (bool, error) { // We call processNextWorkItem to simulate the worker picking up the key // that handleGateway enqueued. if f.controller.workqueue.Len() > 0 { f.controller.processNextWorkItem(ctx) } // Check if ingress status is updated ing, err := f.kubeClient.NetworkingV1().Ingresses(testNamespace).Get(ctx, testIngressName, metav1.GetOptions{}) if err != nil { return false, err } if ing.Status.LoadBalancer.Ingress != nil && len(ing.Status.LoadBalancer.Ingress) > 0 { return ing.Status.LoadBalancer.Ingress[0].Hostname == "my-test-lb.com", nil } return false, nil }) if err != nil { t.Fatalf("Failed to update Ingress status with Gateway Hostname: %v", err) } } func TestSyncHandler_Delete(t *testing.T) { // 1. Setup: Start with an existing Gateway with TLS // This test verifies that when an Ingress is deleted, the Gateway is // reconciled (e.g., to remove TLS certs). // HTTPRoute deletion is handled by GC, so we don't test that here. // This Gateway has an HTTPS listener that should be removed existingGateway := &gatewayv1.Gateway{ ObjectMeta: metav1.ObjectMeta{ Name: GatewayName, Namespace: testNamespace, }, Spec: gatewayv1.GatewaySpec{ GatewayClassName: testGatewayClassName, Listeners: []gatewayv1.Listener{ {Name: "http", Port: 80, Protocol: gatewayv1.HTTPProtocolType}, { // This listener should be removed Name: "https", Port: 443, Protocol: gatewayv1.HTTPSProtocolType, TLS: &gatewayv1.ListenerTLSConfig{ Mode: ptr.To(gatewayv1.TLSModeTerminate), CertificateRefs: []gatewayv1.SecretObjectReference{ {Name: "old-secret"}, }, }, }, }, }, } ingressClass := newTestIngressClass() f := newTestFixture(t, []runtime.Object{ingressClass}, // kube objects []runtime.Object{existingGateway}, // gateway objects ) defer f.cancel() key := fmt.Sprintf("%s/%s", testNamespace, testIngressName) // 2. Sync: Ingress is not found, so controller should reconcile the Gateway err := f.controller.syncHandler(f.ctx, key) if err != nil { t.Fatalf("SyncHandler should not error on delete, but got: %v", err) } // 3. Assert Gateway was updated to remove HTTPS listener gw, err := f.gwClient.GatewayV1().Gateways(testNamespace).Get(f.ctx, GatewayName, metav1.GetOptions{}) if err != nil { t.Fatalf("Failed to get Gateway: %v", err) } if len(gw.Spec.Listeners) != 1 { t.Errorf("Gateway Listeners: expected 1 (http only), got %d", len(gw.Spec.Listeners)) } if gw.Spec.Listeners[0].Name != "http" { t.Errorf("Gateway listener should be http, got %s", gw.Spec.Listeners[0].Name) } } func TestSyncHandler_TranslationError_ServicePort(t *testing.T) { // 1. Setup: Ingress references a port name that doesn't exist on the service ingress := newTestIngress(testIngressName) service := newTestService(testServiceName) // Modify ingress to point to a bad port name ingress.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Port.Name = "bad-port-name" ingress.Spec.Rules[0].HTTP.Paths[0].Backend.Service.Port.Number = 0 ingressClass := newTestIngressClass() f := newTestFixture(t, []runtime.Object{ingress, service, ingressClass}, []runtime.Object{}, ) defer f.cancel() key := fmt.Sprintf("%s/%s", testNamespace, testIngressName) // 2. Sync: Translation should fail err := f.controller.syncHandler(f.ctx, key) if err == nil { t.Fatalf("SyncHandler should error on bad port name, but got nil error") } if !strings.Contains(err.Error(), "port name bad-port-name not found in service") { t.Errorf("Error message should indicate missing port, but got: %v", err) } // 3. Assert HTTPRoute was NOT created expectedRouteName := generateRouteName(testIngressName, "example.com") _, err = f.gwClient.GatewayV1().HTTPRoutes(testNamespace).Get(f.ctx, expectedRouteName, metav1.GetOptions{}) if !errors.IsNotFound(err) { t.Errorf("HTTPRoute should not be created (IsNotFound), but got: %v", err) } } func TestSyncHandler_TLS_Aggregation(t *testing.T) { // 1. Setup service := newTestService(testServiceName) ingressClass := newTestIngressClass() // Ingress A specifies secret-a ingressA := newTestIngress("ingress-a") ingressA.Spec.TLS = []networkingv1.IngressTLS{ {SecretName: "secret-a"}, } // Ingress B specifies secret-b and a duplicate secret-a ingressB := newTestIngress("ingress-b") ingressB.Spec.Rules[0].Host = "b.example.com" ingressB.Spec.TLS = []networkingv1.IngressTLS{ {SecretName: "secret-b"}, {SecretName: "secret-a"}, // Duplicate } // Ingress C is not managed by us ingressC := newTestIngress("ingress-c") ingressC.Spec.Rules[0].Host = "c.example.com" ingressC.Spec.IngressClassName = ptr.To("other-class") // Different class ingressC.Spec.TLS = []networkingv1.IngressTLS{ {SecretName: "secret-c"}, } f := newTestFixture(t, []runtime.Object{service, ingressClass, ingressA, ingressB, ingressC}, // kube objects []runtime.Object{}, // gateway objects ) defer f.cancel() keyA := fmt.Sprintf("%s/%s", testNamespace, "ingress-a") keyB := fmt.Sprintf("%s/%s", testNamespace, "ingress-b") // 2. Sync Ingress A // This sync will fail on status update, but that's OK. // We only care about the Gateway reconciliation. // The controller's lister will see ALL ingresses in the namespace. _ = f.controller.syncHandler(f.ctx, keyA) // 3. Assert Gateway is created with secret-a AND secret-b gw, err := f.gwClient.GatewayV1().Gateways(testNamespace).Get(f.ctx, GatewayName, metav1.GetOptions{}) if err != nil { t.Fatalf("Failed to get created Gateway: %v", err) } // Gateway should now have 2 listeners if len(gw.Spec.Listeners) != 2 { t.Fatalf("Gateway Listeners: expected 2, got %d", len(gw.Spec.Listeners)) } httpsListener := gw.Spec.Listeners[1] if httpsListener.Name != "https" { t.Fatal("Listener 1 is not 'https'") } if httpsListener.TLS == nil || *httpsListener.TLS.Mode != gatewayv1.TLSModeTerminate { t.Errorf("TLS Mode: expected %q, got %v", gatewayv1.TLSModeTerminate, httpsListener.TLS) } // Secrets are sorted: secret-a, secret-b. secret-c is ignored. expectedCerts := []gatewayv1.SecretObjectReference{ {Name: "secret-a"}, {Name: "secret-b"}, } if !reflect.DeepEqual(httpsListener.TLS.CertificateRefs, expectedCerts) { t.Errorf("Gateway certs: expected %v, got %v", expectedCerts, httpsListener.TLS.CertificateRefs) } // 4. Sync Ingress B // This sync should be a no-op for the Gateway, as state is already correct. _ = f.controller.syncHandler(f.ctx, keyB) gw, err = f.gwClient.GatewayV1().Gateways(testNamespace).Get(f.ctx, GatewayName, metav1.GetOptions{}) if err != nil { t.Fatalf("Failed to get updated Gateway: %v", err) } if !reflect.DeepEqual(gw.Spec.Listeners[1].TLS.CertificateRefs, expectedCerts) { t.Errorf("Gateway certs should be unchanged, but they were: %v", gw.Spec.Listeners[1].TLS.CertificateRefs) } // 5. Update Ingress A to remove its TLS spec ingressAUpdate, err := f.kubeClient.NetworkingV1().Ingresses(testNamespace).Get(f.ctx, "ingress-a", metav1.GetOptions{}) if err != nil { t.Fatalf("Failed to get ingress-a for update: %v", err) } ingressAUpdate.Spec.TLS = nil _, err = f.kubeClient.NetworkingV1().Ingresses(testNamespace).Update(f.ctx, ingressAUpdate, metav1.UpdateOptions{}) if err != nil { t.Fatalf("Failed to update ingress-a to remove TLS: %v", err) } // Wait for the informer to see the update time.Sleep(100 * time.Millisecond) // 6. Re-sync Ingress A (keyA) // This sync will now see only Ingress B has TLS. _ = f.controller.syncHandler(f.ctx, keyA) // 7. Assert Gateway is UPDATED, now only with secret-a and secret-b (from Ingress B) gw, err = f.gwClient.GatewayV1().Gateways(testNamespace).Get(f.ctx, GatewayName, metav1.GetOptions{}) if err != nil { t.Fatalf("Failed to get updated Gateway: %v", err) } // Ingress B still holds both "secret-a" and "secret-b" expectedCerts = []gatewayv1.SecretObjectReference{ {Name: "secret-a"}, {Name: "secret-b"}, } if len(gw.Spec.Listeners) != 2 { // Still 2 listeners, cert list is unchanged t.Errorf("Gateway listener count: expected 2, got %d", len(gw.Spec.Listeners)) } if !reflect.DeepEqual(gw.Spec.Listeners[1].TLS.CertificateRefs, expectedCerts) { t.Errorf("Gateway certs: expected %v, got %v", expectedCerts, gw.Spec.Listeners[1].TLS.CertificateRefs) } // 8. Update Ingress B to remove its TLS ingressBUpdate, err := f.kubeClient.NetworkingV1().Ingresses(testNamespace).Get(f.ctx, "ingress-b", metav1.GetOptions{}) if err != nil { t.Fatalf("Failed to get ingress-b for update: %v", err) } ingressBUpdate.Spec.TLS = nil _, err = f.kubeClient.NetworkingV1().Ingresses(testNamespace).Update(f.ctx, ingressBUpdate, metav1.UpdateOptions{}) if err != nil { t.Fatalf("Failed to update ingress-b to remove TLS: %v", err) } // Wait for the informer to see the update time.Sleep(100 * time.Millisecond) // 9. Re-sync Ingress B (keyB) // This sync will now see *zero* ingresses with TLS. _ = f.controller.syncHandler(f.ctx, keyB) // 10. Assert Gateway is UPDATED again, now with *no* HTTPS listener gw, err = f.gwClient.GatewayV1().Gateways(testNamespace).Get(f.ctx, GatewayName, metav1.GetOptions{}) if err != nil { t.Fatalf("Failed to get updated Gateway: %v", err) } if len(gw.Spec.Listeners) != 1 { t.Errorf("Gateway listener count: expected 1 (http only), got %d", len(gw.Spec.Listeners)) } if gw.Spec.Listeners[0].Name != "http" { t.Errorf("Gateway listener should be http, got %s", gw.Spec.Listeners[0].Name) } } // === NEW TEST === func TestSyncHandler_DefaultBackend(t *testing.T) { // 1. Setup: Ingress with ONLY spec.defaultBackend ingress := newTestIngress(testIngressName) ingress.Spec.Rules = nil // No rules ingress.Spec.DefaultBackend = &networkingv1.IngressBackend{ Service: &networkingv1.IngressServiceBackend{ Name: testServiceName, Port: networkingv1.ServiceBackendPort{Number: int32(testServicePortNum)}, }, } service := newTestService(testServiceName) ingressClass := newTestIngressClass() f := newTestFixture(t, []runtime.Object{ingress, service, ingressClass}, []runtime.Object{}, ) defer f.cancel() key := fmt.Sprintf("%s/%s", testNamespace, testIngressName) // 2. Sync // This will fail on status update, which is fine _ = f.controller.syncHandler(f.ctx, key) // 3. Assert HTTPRoute was created for the default backend expectedRouteName := generateRouteName(testIngressName, "") // "" host route, err := f.gwClient.GatewayV1().HTTPRoutes(testNamespace).Get(f.ctx, expectedRouteName, metav1.GetOptions{}) if err != nil { t.Fatalf("Failed to get created HTTPRoute for default backend: %v", err) } // 4. Assert Hostnames is nil (meaning "all hosts") if route.Spec.Hostnames != nil { t.Errorf("Default backend route should have nil Hostnames, got %v", route.Spec.Hostnames) } // 5. Assert Rule is correct if len(route.Spec.Rules) != 1 { t.Fatalf("Default backend route Rules: expected 1, got %d", len(route.Spec.Rules)) } if route.Spec.Rules[0].Matches != nil { t.Errorf("Default backend rule Matches: expected nil, got %v", route.Spec.Rules[0].Matches) } if len(route.Spec.Rules[0].BackendRefs) != 1 { t.Fatalf("Default backend rule BackendRefs: expected 1, got %d", len(route.Spec.Rules[0].BackendRefs)) } backendRef := route.Spec.Rules[0].BackendRefs[0] if string(backendRef.Name) != testServiceName { t.Errorf("BackendRef Name: expected %s, got %s", testServiceName, backendRef.Name) } if *backendRef.Port != int32(testServicePortNum) { t.Errorf("BackendRef Port: expected %d, got %d", testServicePortNum, *backendRef.Port) } } // === NEW TEST === func TestSyncHandler_DefaultRulePrecedence(t *testing.T) { // 1. Setup: Ingress with BOTH spec.defaultBackend and a host: "" rule. // The host: "" rule should win. serviceA := newTestService("service-a") serviceB := newTestService("service-b") ingress := newTestIngress(testIngressName) ingress.Spec.Rules = []networkingv1.IngressRule{ { Host: "", // Default rule IngressRuleValue: networkingv1.IngressRuleValue{ HTTP: &networkingv1.HTTPIngressRuleValue{ Paths: []networkingv1.HTTPIngressPath{ { Path: "/", PathType: ptr.To(networkingv1.PathTypePrefix), Backend: networkingv1.IngressBackend{ Service: &networkingv1.IngressServiceBackend{ Name: "service-b", // This one should be used Port: networkingv1.ServiceBackendPort{Name: testServicePortName}, }, }, }, }, }, }, }, } ingress.Spec.DefaultBackend = &networkingv1.IngressBackend{ Service: &networkingv1.IngressServiceBackend{ Name: "service-a", // This one should be ignored Port: networkingv1.ServiceBackendPort{Number: int32(testServicePortNum)}, }, } ingressClass := newTestIngressClass() f := newTestFixture(t, []runtime.Object{ingress, serviceA, serviceB, ingressClass}, []runtime.Object{}, ) defer f.cancel() key := fmt.Sprintf("%s/%s", testNamespace, testIngressName) // 2. Sync _ = f.controller.syncHandler(f.ctx, key) // 3. Assert HTTPRoute was created expectedRouteName := generateRouteName(testIngressName, "") // "" host route, err := f.gwClient.GatewayV1().HTTPRoutes(testNamespace).Get(f.ctx, expectedRouteName, metav1.GetOptions{}) if err != nil { t.Fatalf("Failed to get created HTTPRoute for default backend: %v", err) } // 4. Assert Hostnames is nil if route.Spec.Hostnames != nil { t.Errorf("Default backend route should have nil Hostnames, got %v", route.Spec.Hostnames) } // 5. Assert Rule points to service-b (proving precedence) if len(route.Spec.Rules) != 1 { t.Fatalf("Default backend route Rules: expected 1, got %d", len(route.Spec.Rules)) } backendRef := route.Spec.Rules[0].BackendRefs[0] if string(backendRef.Name) != "service-b" { t.Errorf("BackendRef Name: expected 'service-b', got %s", backendRef.Name) } } // === NEW TEST === func TestSyncHandler_MultipleHostRules(t *testing.T) { // 1. Setup: One Ingress, two host rules. serviceA := newTestService("service-a") serviceB := newTestService("service-b") ingressClass := newTestIngressClass() ingress := newTestIngress(testIngressName) ingress.Spec.Rules = []networkingv1.IngressRule{ { Host: "a.example.com", IngressRuleValue: networkingv1.IngressRuleValue{ HTTP: &networkingv1.HTTPIngressRuleValue{ Paths: []networkingv1.HTTPIngressPath{ { Path: "/", PathType: ptr.To(networkingv1.PathTypePrefix), Backend: networkingv1.IngressBackend{ Service: &networkingv1.IngressServiceBackend{ Name: "service-a", Port: networkingv1.ServiceBackendPort{Name: testServicePortName}, }, }, }, }, }, }, }, { Host: "b.example.com", IngressRuleValue: networkingv1.IngressRuleValue{ HTTP: &networkingv1.HTTPIngressRuleValue{ Paths: []networkingv1.HTTPIngressPath{ { Path: "/", PathType: ptr.To(networkingv1.PathTypePrefix), Backend: networkingv1.IngressBackend{ Service: &networkingv1.IngressServiceBackend{ Name: "service-b", Port: networkingv1.ServiceBackendPort{Name: testServicePortName}, }, }, }, }, }, }, }, } f := newTestFixture(t, []runtime.Object{ingress, serviceA, serviceB, ingressClass}, []runtime.Object{}, ) defer f.cancel() key := fmt.Sprintf("%s/%s", testNamespace, testIngressName) // 2. Sync _ = f.controller.syncHandler(f.ctx, key) // 3. Assert Route A was created routeNameA := generateRouteName(testIngressName, "a.example.com") routeA, err := f.gwClient.GatewayV1().HTTPRoutes(testNamespace).Get(f.ctx, routeNameA, metav1.GetOptions{}) if err != nil { t.Fatalf("Failed to get route for a.example.com: %v", err) } if len(routeA.Spec.Hostnames) != 1 || routeA.Spec.Hostnames[0] != "a.example.com" { t.Errorf("Route A Hostnames: expected [\"a.example.com\"], got %v", routeA.Spec.Hostnames) } if string(routeA.Spec.Rules[0].BackendRefs[0].Name) != "service-a" { t.Errorf("Route A Backend: expected 'service-a', got %s", routeA.Spec.Rules[0].BackendRefs[0].Name) } // 4. Assert Route B was created routeNameB := generateRouteName(testIngressName, "b.example.com") routeB, err := f.gwClient.GatewayV1().HTTPRoutes(testNamespace).Get(f.ctx, routeNameB, metav1.GetOptions{}) if err != nil { t.Fatalf("Failed to get route for b.example.com: %v", err) } if len(routeB.Spec.Hostnames) != 1 || routeB.Spec.Hostnames[0] != "b.example.com" { t.Errorf("Route B Hostnames: expected [\"b.example.com\"], got %v", routeB.Spec.Hostnames) } if string(routeB.Spec.Rules[0].BackendRefs[0].Name) != "service-b" { t.Errorf("Route B Backend: expected 'service-b', got %s", routeB.Spec.Rules[0].BackendRefs[0].Name) } } // === NEW TEST === func TestSyncHandler_Update_StaleRouteDeletion(t *testing.T) { // 1. Setup: // - An Ingress for "a.example.com" // - A pre-existing, stale HTTPRoute for "b.example.com" that is *owned* // by this Ingress (simulating a removed host rule). ingress := newTestIngress(testIngressName) ingress.Spec.Rules = []networkingv1.IngressRule{ { Host: "a.example.com", // The ONLY desired host IngressRuleValue: networkingv1.IngressRuleValue{ HTTP: &networkingv1.HTTPIngressRuleValue{ Paths: []networkingv1.HTTPIngressPath{ { Path: "/", PathType: ptr.To(networkingv1.PathTypePrefix), Backend: networkingv1.IngressBackend{ Service: &networkingv1.IngressServiceBackend{ Name: testServiceName, Port: networkingv1.ServiceBackendPort{Name: testServicePortName}, }, }, }, }, }, }, }, } staleRouteName := generateRouteName(testIngressName, "b.example.com") staleRoute := &gatewayv1.HTTPRoute{ ObjectMeta: metav1.ObjectMeta{ Name: staleRouteName, Namespace: testNamespace, OwnerReferences: []metav1.OwnerReference{ *metav1.NewControllerRef(ingress, networkingv1.SchemeGroupVersion.WithKind("Ingress")), }, }, Spec: gatewayv1.HTTPRouteSpec{ Hostnames: []gatewayv1.Hostname{"b.example.com"}, }, } service := newTestService(testServiceName) ingressClass := newTestIngressClass() f := newTestFixture(t, []runtime.Object{ingress, service, ingressClass}, []runtime.Object{staleRoute}, // Pre-create the stale route ) defer f.cancel() key := fmt.Sprintf("%s/%s", testNamespace, testIngressName) // 2. Sync _ = f.controller.syncHandler(f.ctx, key) // 3. Assert desired route ("a") was created desiredRouteName := generateRouteName(testIngressName, "a.example.com") _, err := f.gwClient.GatewayV1().HTTPRoutes(testNamespace).Get(f.ctx, desiredRouteName, metav1.GetOptions{}) if err != nil { t.Fatalf("Failed to get desired route: %v", err) } // 4. Assert stale route ("b") was deleted _, err = f.gwClient.GatewayV1().HTTPRoutes(testNamespace).Get(f.ctx, staleRouteName, metav1.GetOptions{}) if !errors.IsNotFound(err) { t.Errorf("Stale HTTPRoute should be deleted (IsNotFound), but got: %v", err) } } ================================================ FILE: pkg/loadbalancer/proxy.go ================================================ package loadbalancer import ( "bytes" "context" "fmt" "io" "net" "net/http" "strconv" "strings" "text/template" "time" "github.com/pkg/errors" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/klog/v2" netutils "k8s.io/utils/net" "sigs.k8s.io/cloud-provider-kind/pkg/config" "sigs.k8s.io/cloud-provider-kind/pkg/container" ) // keep in sync with dynamicFilesystemConfig const ( proxyConfigPath = "/home/envoy/envoy.yaml" proxyConfigPathCDS = "/home/envoy/cds.yaml" proxyConfigPathLDS = "/home/envoy/lds.yaml" envoyAdminPort = 10000 ) // start Envoy with dynamic configuration by using files that implement the xDS protocol. // https://www.envoyproxy.io/docs/envoy/latest/start/quick-start/configuration-dynamic-filesystem const dynamicFilesystemConfig = `node: cluster: cloud-provider-kind id: cloud-provider-kind-id dynamic_resources: cds_config: resource_api_version: V3 path: /home/envoy/cds.yaml lds_config: resource_api_version: V3 path: /home/envoy/lds.yaml admin: access_log_path: /dev/stdout address: socket_address: address: 0.0.0.0 port_value: 10000 ` // proxyConfigData is supplied to the loadbalancer config template type proxyConfigData struct { HealthCheckPort int // is the same for all ServicePorts ServicePorts map[string]servicePort // key is the IP family and Port and Protocol to support MultiPort services SessionAffinity string SourceRanges []sourceRange } type sourceRange struct { Prefix string Length int } type servicePort struct { // frontend Listener endpoint // backend Cluster []endpoint } type endpoint struct { Address string Port int Protocol string } // proxyLDSConfigTemplate is the loadbalancer config template for listeners const proxyLDSConfigTemplate = ` resources: {{- range $index, $servicePort := .ServicePorts }} - "@type": type.googleapis.com/envoy.config.listener.v3.Listener name: listener_{{$index}} address: socket_address: address: {{ $servicePort.Listener.Address }} port_value: {{ $servicePort.Listener.Port }} protocol: {{ $servicePort.Listener.Protocol }} {{- if eq $servicePort.Listener.Protocol "UDP"}} udp_listener_config: downstream_socket_config: max_rx_datagram_size: 9000 listener_filters: - name: envoy.filters.udp_listener.udp_proxy typed_config: '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.UdpProxyConfig access_log: - name: envoy.file_access_log typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog stat_prefix: udp_proxy matcher: on_no_match: action: name: route typed_config: '@type': type.googleapis.com/envoy.extensions.filters.udp.udp_proxy.v3.Route cluster: cluster_{{$index}} {{- if eq $.SessionAffinity "ClientIP"}} hash_policies: source_ip: true {{- end}} upstream_socket_config: max_rx_datagram_size: 9000 {{- else }} filter_chains: - filters: - name: envoy.filters.network.tcp_proxy typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy access_log: - name: envoy.file_access_log typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog stat_prefix: tcp_proxy cluster: cluster_{{$index}} {{- if eq $.SessionAffinity "ClientIP"}} hash_policy: source_ip: {} {{- end}} {{- if len $.SourceRanges }} filter_chain_match: source_prefix_ranges: {{- range $sr := $.SourceRanges }} - address_prefix: "{{ $sr.Prefix }}" prefix_len: {{ $sr.Length }} {{- end }} {{- end }} {{- end}} {{- end }} ` // proxyCDSConfigTemplate is the loadbalancer config template for clusters // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/core/v3/health_check.proto#envoy-v3-api-msg-config-core-v3-healthcheck-httphealthcheck const proxyCDSConfigTemplate = ` resources: {{- range $index, $servicePort := .ServicePorts }} - "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster name: cluster_{{$index}} connect_timeout: 3s type: STATIC common_lb_config: healthy_panic_threshold: value: 0 {{- if eq $.SessionAffinity "ClientIP"}} lb_policy: RING_HASH {{- else}} lb_policy: RANDOM {{- end}} health_checks: - timeout: 3s interval: 2s unhealthy_threshold: 2 healthy_threshold: 1 initial_jitter: 0s no_traffic_interval: 3s always_log_health_check_failures: true always_log_health_check_success: true event_log_path: /dev/stdout http_health_check: path: /healthz load_assignment: cluster_name: cluster_{{$index}} endpoints: {{- range $address := $servicePort.Cluster }} - lb_endpoints: - endpoint: health_check_config: port_value: {{ $.HealthCheckPort }} address: socket_address: address: {{ $address.Address }} port_value: {{ $address.Port }} protocol: {{ $address.Protocol }} {{- end}} {{- end }} ` // proxyConfig returns a kubeadm config generated from config data, in particular // the kubernetes version func proxyConfig(configTemplate string, data *proxyConfigData) (config string, err error) { t, err := template.New("loadbalancer-config").Parse(configTemplate) if err != nil { return "", errors.Wrap(err, "failed to parse config template") } // execute the template var buff bytes.Buffer err = t.Execute(&buff, data) if err != nil { return "", errors.Wrap(err, "error executing config template") } return buff.String(), nil } func generateConfig(service *v1.Service, nodes []*v1.Node) *proxyConfigData { if service == nil { return nil } hcPort := 10256 // kube-proxy default port if service.Spec.ExternalTrafficPolicy == v1.ServiceExternalTrafficPolicyTypeLocal { hcPort = int(service.Spec.HealthCheckNodePort) } lbConfig := &proxyConfigData{ HealthCheckPort: hcPort, SessionAffinity: string(service.Spec.SessionAffinity), } servicePortConfig := map[string]servicePort{} for _, ipFamily := range service.Spec.IPFamilies { for _, port := range service.Spec.Ports { if port.Protocol != v1.ProtocolTCP && port.Protocol != v1.ProtocolUDP { klog.Infof("service port protocol %s not supported", port.Protocol) continue } key := fmt.Sprintf("%s_%d_%s", ipFamily, port.Port, port.Protocol) bind := `0.0.0.0` if ipFamily == v1.IPv6Protocol { bind = `"::"` } backends := []endpoint{} for _, n := range nodes { for _, addr := range n.Status.Addresses { // only internal IPs supported if addr.Type != v1.NodeInternalIP { klog.V(2).Infof("address type %s, only %s supported", addr.Type, v1.NodeInternalIP) continue } // only addresses that match the Service IP family if (netutils.IsIPv4String(addr.Address) && ipFamily != v1.IPv4Protocol) || (netutils.IsIPv6String(addr.Address) && ipFamily != v1.IPv6Protocol) { continue } backends = append(backends, endpoint{Address: addr.Address, Port: int(port.NodePort), Protocol: string(port.Protocol)}) } } servicePortConfig[key] = servicePort{ Listener: endpoint{Address: bind, Port: int(port.Port), Protocol: string(port.Protocol)}, Cluster: backends, } } } lbConfig.ServicePorts = servicePortConfig for _, sr := range service.Spec.LoadBalancerSourceRanges { // This is validated (though the validation mistakenly allows whitespace) // so we don't bother dealing with parse failures. _, cidr, _ := netutils.ParseCIDRSloppy(strings.TrimSpace(sr)) if cidr != nil { len, _ := cidr.Mask.Size() lbConfig.SourceRanges = append(lbConfig.SourceRanges, sourceRange{ Prefix: cidr.IP.String(), Length: len, }, ) } } klog.V(2).Infof("envoy config info: %+v", lbConfig) return lbConfig } // TODO: move to xDS via GRPC instead of having to deal with files func proxyUpdateLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) error { if service == nil { return nil } var stdout, stderr bytes.Buffer name := loadBalancerName(clusterName, service) config := generateConfig(service, nodes) // create loadbalancer config data ldsConfig, err := proxyConfig(proxyLDSConfigTemplate, config) if err != nil { return errors.Wrap(err, "failed to generate loadbalancer config data") } klog.V(2).Infof("updating loadbalancer with config %s", ldsConfig) err = container.Exec(name, []string{"cp", "/dev/stdin", proxyConfigPathLDS + ".tmp"}, strings.NewReader(ldsConfig), &stdout, &stderr) if err != nil { return err } cdsConfig, err := proxyConfig(proxyCDSConfigTemplate, config) if err != nil { return errors.Wrap(err, "failed to generate loadbalancer config data") } klog.V(2).Infof("updating loadbalancer with config %s", cdsConfig) err = container.Exec(name, []string{"cp", "/dev/stdin", proxyConfigPathCDS + ".tmp"}, strings.NewReader(cdsConfig), &stdout, &stderr) if err != nil { return err } // envoy has an initialization process until starts to forward traffic // https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/operations/init#arch-overview-initialization // also wait for the healthchecks and "no_traffic_interval" cmd := fmt.Sprintf(`chmod a+rw /home/envoy/* && mv %s %s && mv %s %s`, proxyConfigPathCDS+".tmp", proxyConfigPathCDS, proxyConfigPathLDS+".tmp", proxyConfigPathLDS) err = container.Exec(name, []string{"bash", "-c", cmd}, nil, &stdout, &stderr) if err != nil { return fmt.Errorf("error updating configuration Stdout: %s Stderr: %s : %w", stdout.String(), stderr.String(), err) } return waitLoadBalancerReady(ctx, name, 30*time.Second) } func waitLoadBalancerReady(ctx context.Context, name string, timeout time.Duration) error { portmaps, err := container.PortMaps(name) if err != nil { return err } var authority string if config.DefaultConfig.ControlPlaneConnectivity == config.Direct { ipv4, _, err := container.IPs(name) if err != nil { return err } authority = net.JoinHostPort(ipv4, strconv.Itoa(envoyAdminPort)) } else { port, ok := portmaps[strconv.Itoa(envoyAdminPort)+"/tcp"] if !ok { return fmt.Errorf("envoy admin port %d not found, got %v", envoyAdminPort, portmaps) } authority = net.JoinHostPort("127.0.0.1", port) } httpClient := http.DefaultClient err = wait.PollUntilContextTimeout(ctx, 1*time.Second, timeout, true, func(ctx context.Context) (done bool, err error) { // iptables port forwarding on localhost only works for IPv4 resp, err := httpClient.Get(fmt.Sprintf("http://%s/ready", authority)) if err != nil { klog.V(2).Infof("unexpected error trying to get load balancer %s readiness :%v", name, err) return false, nil } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { klog.V(2).Infof("unexpected status code from load balancer %s expected LIVE got %d", name, resp.StatusCode) return false, nil } body, err := io.ReadAll(resp.Body) if err != nil { klog.V(2).Infof("unexpected error trying to get load balancer %s readiness :%v", name, err) return false, nil } response := strings.TrimSpace(string(body)) if response != "LIVE" { klog.V(2).Infof("unexpected ready response from load balancer %s expected LIVE got %s", name, response) return false, nil } return true, nil }) return err } ================================================ FILE: pkg/loadbalancer/proxy_test.go ================================================ package loadbalancer import ( "reflect" "testing" "github.com/google/go-cmp/cmp" "github.com/lithammer/dedent" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/utils/ptr" ) func makeNode(name string, ip string) *v1.Node { return &v1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: name, }, Spec: v1.NodeSpec{}, Status: v1.NodeStatus{ Addresses: []v1.NodeAddress{ {Type: v1.NodeInternalIP, Address: ip}, }, }, } } func makeService(name string) *v1.Service { return &v1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: name, }, Spec: v1.ServiceSpec{ Type: v1.ServiceTypeClusterIP, Ports: []v1.ServicePort{ {Port: 80}, }, }, } } func Test_generateConfig(t *testing.T) { tests := []struct { name string service *v1.Service nodes []*v1.Node want *proxyConfigData }{ { name: "empty", }, { name: "simple service", service: &v1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, Spec: v1.ServiceSpec{ Type: v1.ServiceTypeLoadBalancer, ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal, IPFamilies: []v1.IPFamily{v1.IPv4Protocol}, Ports: []v1.ServicePort{ { Port: 80, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}, NodePort: 30000, Protocol: v1.ProtocolTCP, }, }, HealthCheckNodePort: 32000, }, }, nodes: []*v1.Node{ makeNode("a", "10.0.0.1"), makeNode("b", "10.0.0.2"), }, want: &proxyConfigData{ HealthCheckPort: 32000, ServicePorts: map[string]servicePort{ "IPv4_80_TCP": servicePort{ Listener: endpoint{Address: "0.0.0.0", Port: 80, Protocol: string(v1.ProtocolTCP)}, Cluster: []endpoint{{"10.0.0.1", 30000, string(v1.ProtocolTCP)}, {"10.0.0.2", 30000, string(v1.ProtocolTCP)}}, }, }, }, }, { name: "multiport service", service: &v1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, Spec: v1.ServiceSpec{ Type: v1.ServiceTypeLoadBalancer, ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal, IPFamilies: []v1.IPFamily{v1.IPv4Protocol}, Ports: []v1.ServicePort{ { Port: 80, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}, NodePort: 30000, Protocol: v1.ProtocolTCP, }, { Port: 443, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}, NodePort: 31000, Protocol: v1.ProtocolTCP, }, }, HealthCheckNodePort: 32000, }, }, nodes: []*v1.Node{ makeNode("a", "10.0.0.1"), makeNode("b", "10.0.0.2"), }, want: &proxyConfigData{ HealthCheckPort: 32000, ServicePorts: map[string]servicePort{ "IPv4_80_TCP": servicePort{ Listener: endpoint{Address: "0.0.0.0", Port: 80, Protocol: string(v1.ProtocolTCP)}, Cluster: []endpoint{{"10.0.0.1", 30000, string(v1.ProtocolTCP)}, {"10.0.0.2", 30000, string(v1.ProtocolTCP)}}, }, "IPv4_443_TCP": servicePort{ Listener: endpoint{Address: "0.0.0.0", Port: 443, Protocol: string(v1.ProtocolTCP)}, Cluster: []endpoint{{"10.0.0.1", 31000, string(v1.ProtocolTCP)}, {"10.0.0.2", 31000, string(v1.ProtocolTCP)}}, }, }, }, }, { name: "multiport different protocol service", service: &v1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, Spec: v1.ServiceSpec{ Type: v1.ServiceTypeLoadBalancer, ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal, IPFamilies: []v1.IPFamily{v1.IPv4Protocol}, Ports: []v1.ServicePort{ { Port: 80, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}, NodePort: 30000, Protocol: v1.ProtocolTCP, }, { Port: 80, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}, NodePort: 31000, Protocol: v1.ProtocolUDP, }, }, HealthCheckNodePort: 32000, }, }, nodes: []*v1.Node{ makeNode("a", "10.0.0.1"), makeNode("b", "10.0.0.2"), }, want: &proxyConfigData{ HealthCheckPort: 32000, ServicePorts: map[string]servicePort{ "IPv4_80_TCP": servicePort{ Listener: endpoint{Address: "0.0.0.0", Port: 80, Protocol: string(v1.ProtocolTCP)}, Cluster: []endpoint{{"10.0.0.1", 30000, string(v1.ProtocolTCP)}, {"10.0.0.2", 30000, string(v1.ProtocolTCP)}}, }, "IPv4_80_UDP": servicePort{ Listener: endpoint{Address: "0.0.0.0", Port: 80, Protocol: string(v1.ProtocolUDP)}, Cluster: []endpoint{{"10.0.0.1", 31000, string(v1.ProtocolUDP)}, {"10.0.0.2", 31000, string(v1.ProtocolUDP)}}, }, }, }, }, { name: "multiport service ipv6", service: &v1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, Spec: v1.ServiceSpec{ Type: v1.ServiceTypeLoadBalancer, ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyLocal, IPFamilies: []v1.IPFamily{v1.IPv6Protocol}, Ports: []v1.ServicePort{ { Port: 80, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}, NodePort: 30000, Protocol: v1.ProtocolTCP, }, { Port: 443, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}, NodePort: 31000, Protocol: v1.ProtocolTCP, }, }, HealthCheckNodePort: 32000, }, }, nodes: []*v1.Node{ makeNode("a", "2001:db2::3"), makeNode("b", "2001:db2::4"), }, want: &proxyConfigData{ HealthCheckPort: 32000, ServicePorts: map[string]servicePort{ "IPv6_80_TCP": servicePort{ Listener: endpoint{Address: `"::"`, Port: 80, Protocol: string(v1.ProtocolTCP)}, Cluster: []endpoint{{"2001:db2::3", 30000, string(v1.ProtocolTCP)}, {"2001:db2::4", 30000, string(v1.ProtocolTCP)}}, }, "IPv6_443_TCP": servicePort{ Listener: endpoint{Address: `"::"`, Port: 443, Protocol: string(v1.ProtocolTCP)}, Cluster: []endpoint{{"2001:db2::3", 31000, string(v1.ProtocolTCP)}, {"2001:db2::4", 31000, string(v1.ProtocolTCP)}}, }, }, }, }, { name: "session affinity", service: &v1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, Spec: v1.ServiceSpec{ Type: v1.ServiceTypeLoadBalancer, ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyCluster, IPFamilies: []v1.IPFamily{v1.IPv4Protocol}, Ports: []v1.ServicePort{ { Port: 80, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}, NodePort: 30000, Protocol: v1.ProtocolTCP, }, }, SessionAffinity: v1.ServiceAffinityClientIP, SessionAffinityConfig: &v1.SessionAffinityConfig{ ClientIP: &v1.ClientIPConfig{ // FIXME: This is currently ignored TimeoutSeconds: ptr.To[int32](60), }, }, }, }, nodes: []*v1.Node{ makeNode("a", "10.0.0.1"), }, want: &proxyConfigData{ HealthCheckPort: 10256, ServicePorts: map[string]servicePort{ "IPv4_80_TCP": servicePort{ Listener: endpoint{Address: "0.0.0.0", Port: 80, Protocol: string(v1.ProtocolTCP)}, Cluster: []endpoint{{"10.0.0.1", 30000, string(v1.ProtocolTCP)}}, }, }, SessionAffinity: "ClientIP", }, }, { name: "source ranges", service: &v1.Service{ ObjectMeta: metav1.ObjectMeta{ Name: "test", }, Spec: v1.ServiceSpec{ Type: v1.ServiceTypeLoadBalancer, ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyCluster, IPFamilies: []v1.IPFamily{v1.IPv4Protocol}, Ports: []v1.ServicePort{ { Port: 80, TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 8080}, NodePort: 30000, Protocol: v1.ProtocolTCP, }, }, LoadBalancerSourceRanges: []string{ "10.0.0.0/8", // This is "valid". " 192.168.0.0/16 ", }, }, }, nodes: []*v1.Node{ makeNode("a", "10.0.0.1"), }, want: &proxyConfigData{ HealthCheckPort: 10256, ServicePorts: map[string]servicePort{ "IPv4_80_TCP": servicePort{ Listener: endpoint{Address: "0.0.0.0", Port: 80, Protocol: string(v1.ProtocolTCP)}, Cluster: []endpoint{{"10.0.0.1", 30000, string(v1.ProtocolTCP)}}, }, }, SourceRanges: []sourceRange{ {Prefix: "10.0.0.0", Length: 8}, {Prefix: "192.168.0.0", Length: 16}, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := generateConfig(tt.service, tt.nodes); !reflect.DeepEqual(got, tt.want) { t.Logf("diff %+v", cmp.Diff(got, tt.want)) t.Errorf("generateConfig() = %+v,\n want %+v", got, tt.want) } }) } } func Test_proxyConfig(t *testing.T) { tests := []struct { name string template string data *proxyConfigData wantConfig string }{ { name: "ipv4 CDS", template: proxyCDSConfigTemplate, data: &proxyConfigData{ HealthCheckPort: 32764, ServicePorts: map[string]servicePort{ "IPv4_80": servicePort{ Listener: endpoint{Address: "0.0.0.0", Port: 80, Protocol: string(v1.ProtocolTCP)}, Cluster: []endpoint{{"192.168.8.2", 30497, string(v1.ProtocolTCP)}, {"192.168.8.3", 30497, string(v1.ProtocolTCP)}}, }, "IPv4_443": servicePort{ Listener: endpoint{Address: "0.0.0.0", Port: 443, Protocol: string(v1.ProtocolTCP)}, Cluster: []endpoint{{"192.168.8.2", 31497, string(v1.ProtocolTCP)}, {"192.168.8.3", 31497, string(v1.ProtocolTCP)}}, }, }, }, wantConfig: ` resources: - "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster name: cluster_IPv4_443 connect_timeout: 3s type: STATIC common_lb_config: healthy_panic_threshold: value: 0 lb_policy: RANDOM health_checks: - timeout: 3s interval: 2s unhealthy_threshold: 2 healthy_threshold: 1 initial_jitter: 0s no_traffic_interval: 3s always_log_health_check_failures: true always_log_health_check_success: true event_log_path: /dev/stdout http_health_check: path: /healthz load_assignment: cluster_name: cluster_IPv4_443 endpoints: - lb_endpoints: - endpoint: health_check_config: port_value: 32764 address: socket_address: address: 192.168.8.2 port_value: 31497 protocol: TCP - lb_endpoints: - endpoint: health_check_config: port_value: 32764 address: socket_address: address: 192.168.8.3 port_value: 31497 protocol: TCP - "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster name: cluster_IPv4_80 connect_timeout: 3s type: STATIC common_lb_config: healthy_panic_threshold: value: 0 lb_policy: RANDOM health_checks: - timeout: 3s interval: 2s unhealthy_threshold: 2 healthy_threshold: 1 initial_jitter: 0s no_traffic_interval: 3s always_log_health_check_failures: true always_log_health_check_success: true event_log_path: /dev/stdout http_health_check: path: /healthz load_assignment: cluster_name: cluster_IPv4_80 endpoints: - lb_endpoints: - endpoint: health_check_config: port_value: 32764 address: socket_address: address: 192.168.8.2 port_value: 30497 protocol: TCP - lb_endpoints: - endpoint: health_check_config: port_value: 32764 address: socket_address: address: 192.168.8.3 port_value: 30497 protocol: TCP `, }, { name: "ipv4 LDS", template: proxyLDSConfigTemplate, data: &proxyConfigData{ HealthCheckPort: 32764, ServicePorts: map[string]servicePort{ "IPv4_80": servicePort{ Listener: endpoint{Address: "0.0.0.0", Port: 80, Protocol: string(v1.ProtocolTCP)}, Cluster: []endpoint{{"192.168.8.2", 30497, string(v1.ProtocolTCP)}, {"192.168.8.3", 30497, string(v1.ProtocolTCP)}}, }, "IPv4_443": servicePort{ Listener: endpoint{Address: "0.0.0.0", Port: 443, Protocol: string(v1.ProtocolTCP)}, Cluster: []endpoint{{"192.168.8.2", 31497, string(v1.ProtocolTCP)}, {"192.168.8.3", 31497, string(v1.ProtocolTCP)}}, }, }, }, wantConfig: ` resources: - "@type": type.googleapis.com/envoy.config.listener.v3.Listener name: listener_IPv4_443 address: socket_address: address: 0.0.0.0 port_value: 443 protocol: TCP filter_chains: - filters: - name: envoy.filters.network.tcp_proxy typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy access_log: - name: envoy.file_access_log typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog stat_prefix: tcp_proxy cluster: cluster_IPv4_443 - "@type": type.googleapis.com/envoy.config.listener.v3.Listener name: listener_IPv4_80 address: socket_address: address: 0.0.0.0 port_value: 80 protocol: TCP filter_chains: - filters: - name: envoy.filters.network.tcp_proxy typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy access_log: - name: envoy.file_access_log typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog stat_prefix: tcp_proxy cluster: cluster_IPv4_80 `, }, { name: "ipv4 CDS with affinity", template: proxyCDSConfigTemplate, data: &proxyConfigData{ HealthCheckPort: 32764, ServicePorts: map[string]servicePort{ "IPv4_80": servicePort{ Listener: endpoint{Address: "0.0.0.0", Port: 80, Protocol: string(v1.ProtocolTCP)}, Cluster: []endpoint{{"192.168.8.2", 30497, string(v1.ProtocolTCP)}, {"192.168.8.3", 30497, string(v1.ProtocolTCP)}}, }, }, SessionAffinity: "ClientIP", }, wantConfig: ` resources: - "@type": type.googleapis.com/envoy.config.cluster.v3.Cluster name: cluster_IPv4_80 connect_timeout: 3s type: STATIC common_lb_config: healthy_panic_threshold: value: 0 lb_policy: RING_HASH health_checks: - timeout: 3s interval: 2s unhealthy_threshold: 2 healthy_threshold: 1 initial_jitter: 0s no_traffic_interval: 3s always_log_health_check_failures: true always_log_health_check_success: true event_log_path: /dev/stdout http_health_check: path: /healthz load_assignment: cluster_name: cluster_IPv4_80 endpoints: - lb_endpoints: - endpoint: health_check_config: port_value: 32764 address: socket_address: address: 192.168.8.2 port_value: 30497 protocol: TCP - lb_endpoints: - endpoint: health_check_config: port_value: 32764 address: socket_address: address: 192.168.8.3 port_value: 30497 protocol: TCP `, }, { name: "ipv4 LDS with affinity", template: proxyLDSConfigTemplate, data: &proxyConfigData{ HealthCheckPort: 32764, ServicePorts: map[string]servicePort{ "IPv4_80": servicePort{ Listener: endpoint{Address: "0.0.0.0", Port: 80, Protocol: string(v1.ProtocolTCP)}, Cluster: []endpoint{{"192.168.8.2", 30497, string(v1.ProtocolTCP)}, {"192.168.8.3", 30497, string(v1.ProtocolTCP)}}, }, }, SessionAffinity: "ClientIP", }, wantConfig: ` resources: - "@type": type.googleapis.com/envoy.config.listener.v3.Listener name: listener_IPv4_80 address: socket_address: address: 0.0.0.0 port_value: 80 protocol: TCP filter_chains: - filters: - name: envoy.filters.network.tcp_proxy typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy access_log: - name: envoy.file_access_log typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog stat_prefix: tcp_proxy cluster: cluster_IPv4_80 hash_policy: source_ip: {} `, }, { name: "ipv4 LDS with source ranges", template: proxyLDSConfigTemplate, data: &proxyConfigData{ HealthCheckPort: 32764, ServicePorts: map[string]servicePort{ "IPv4_80": servicePort{ Listener: endpoint{Address: "0.0.0.0", Port: 80, Protocol: string(v1.ProtocolTCP)}, Cluster: []endpoint{{"192.168.8.2", 30497, string(v1.ProtocolTCP)}, {"192.168.8.3", 30497, string(v1.ProtocolTCP)}}, }, }, SourceRanges: []sourceRange{ {Prefix: "10.0.0.0", Length: 8}, {Prefix: "192.168.0.0", Length: 16}, }, }, wantConfig: ` resources: - "@type": type.googleapis.com/envoy.config.listener.v3.Listener name: listener_IPv4_80 address: socket_address: address: 0.0.0.0 port_value: 80 protocol: TCP filter_chains: - filters: - name: envoy.filters.network.tcp_proxy typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy access_log: - name: envoy.file_access_log typed_config: "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog stat_prefix: tcp_proxy cluster: cluster_IPv4_80 filter_chain_match: source_prefix_ranges: - address_prefix: "10.0.0.0" prefix_len: 8 - address_prefix: "192.168.0.0" prefix_len: 16 `, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotConfig, err := proxyConfig(tt.template, tt.data) if err != nil { t.Errorf("proxyConfig() error = %v", err) return } wantConfig := dedent.Dedent(tt.wantConfig) if gotConfig != wantConfig { t.Logf("%s", gotConfig) t.Errorf("proxyConfig() not expected\n%v", cmp.Diff(gotConfig, wantConfig)) } }) } } ================================================ FILE: pkg/loadbalancer/server.go ================================================ package loadbalancer import ( "context" "crypto/sha256" "errors" "fmt" "io" "net" "os" "path" "strings" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" cloudprovider "k8s.io/cloud-provider" "k8s.io/klog/v2" "k8s.io/utils/ptr" "sigs.k8s.io/cloud-provider-kind/pkg/config" "sigs.k8s.io/cloud-provider-kind/pkg/constants" "sigs.k8s.io/cloud-provider-kind/pkg/container" "sigs.k8s.io/cloud-provider-kind/pkg/tunnels" ) type Server struct { tunnelManager *tunnels.TunnelManager } var _ cloudprovider.LoadBalancer = &Server{} func NewServer() cloudprovider.LoadBalancer { s := &Server{} if config.DefaultConfig.LoadBalancerConnectivity == config.Tunnel { s.tunnelManager = tunnels.NewTunnelManager() } return s } func (s *Server) GetLoadBalancer(ctx context.Context, clusterName string, service *v1.Service) (*v1.LoadBalancerStatus, bool, error) { // report status name := loadBalancerName(clusterName, service) ipv4, ipv6, err := container.IPs(name) if err != nil { if strings.Contains(err.Error(), "failed to get container details") { return nil, false, nil } return nil, false, err } status := &v1.LoadBalancerStatus{} // process Ports portStatus := []v1.PortStatus{} for _, port := range service.Spec.Ports { portStatus = append(portStatus, v1.PortStatus{ Port: port.Port, Protocol: port.Protocol, }) } // process IPs svcIPv4 := false svcIPv6 := false for _, family := range service.Spec.IPFamilies { if family == v1.IPv4Protocol { svcIPv4 = true } if family == v1.IPv6Protocol { svcIPv6 = true } } if ipv4 != "" && svcIPv4 { status.Ingress = append(status.Ingress, v1.LoadBalancerIngress{ IP: ipv4, IPMode: ptr.To(v1.LoadBalancerIPModeProxy), Ports: portStatus, }) } if ipv6 != "" && svcIPv6 { status.Ingress = append(status.Ingress, v1.LoadBalancerIngress{ IP: ipv6, IPMode: ptr.To(v1.LoadBalancerIPModeProxy), Ports: portStatus, }) } return status, true, nil } func (s *Server) GetLoadBalancerName(ctx context.Context, clusterName string, service *v1.Service) string { return loadBalancerName(clusterName, service) } func (s *Server) EnsureLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) (*v1.LoadBalancerStatus, error) { name := loadBalancerName(clusterName, service) if !container.IsRunning(name) { klog.Infof("container %s for loadbalancer is not running", name) if container.Exist(name) { err := container.Delete(name) if err != nil { return nil, err } } } if !container.Exist(name) { klog.V(2).Infof("creating container for loadbalancer") err := s.createLoadBalancer(clusterName, service, config.DefaultConfig.ProxyImage) if err != nil { return nil, err } } // update loadbalancer klog.V(2).Infof("updating loadbalancer") err := s.UpdateLoadBalancer(ctx, clusterName, service, nodes) if err != nil { return nil, err } // on some platforms that run containers in VMs forward from userspace if s.tunnelManager != nil { klog.V(2).Infof("updating loadbalancer tunnels on userspace") err = s.tunnelManager.SetupTunnels(loadBalancerName(clusterName, service)) if err != nil { klog.ErrorS(err, "error setting up tunnels") } } // get loadbalancer Status klog.V(2).Infof("get loadbalancer status") status, ok, err := s.GetLoadBalancer(ctx, clusterName, service) if !ok { return nil, fmt.Errorf("loadbalancer %s not found", name) } if err != nil { return nil, err } return status, nil } func (s *Server) UpdateLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) error { return proxyUpdateLoadBalancer(ctx, clusterName, service, nodes) } func (s *Server) EnsureLoadBalancerDeleted(ctx context.Context, clusterName string, service *v1.Service) error { containerName := loadBalancerName(clusterName, service) var err1, err2 error if s.tunnelManager != nil { err1 = s.tunnelManager.RemoveTunnels(containerName) } // Before deleting the load balancer store the logs if required if config.DefaultConfig.EnableLogDump { fileName := path.Join(config.DefaultConfig.LogDir, service.Namespace+"_"+service.Name+".log") klog.V(2).Infof("storing logs for loadbalancer %s on %s", containerName, fileName) if err := container.LogDump(containerName, fileName); err != nil { klog.Infof("error trying to store logs for load balancer %s : %v", containerName, err) } } err2 = container.Delete(containerName) return errors.Join(err1, err2) } // loadbalancer name is a unique name for the loadbalancer container func loadBalancerName(clusterName string, service *v1.Service) string { h := sha256.New() _, err := io.WriteString(h, loadBalancerSimpleName(clusterName, service)) if err != nil { panic(err) } hash := h.Sum(nil) return fmt.Sprintf("%s-%x", constants.ContainerPrefix, hash[:6]) } func loadBalancerSimpleName(clusterName string, service *v1.Service) string { return clusterName + "/" + service.Namespace + "/" + service.Name } func ServiceFromLoadBalancerSimpleName(s string) (clusterName string, service *v1.Service) { slices := strings.Split(s, "/") if len(slices) != 3 { return } clusterName = slices[0] service = &v1.Service{ObjectMeta: metav1.ObjectMeta{Namespace: slices[1], Name: slices[2]}} return } // createLoadBalancer create a docker container with a loadbalancer func (s *Server) createLoadBalancer(clusterName string, service *v1.Service, image string) error { name := loadBalancerName(clusterName, service) networkName := constants.FixedNetworkName if n := os.Getenv("KIND_EXPERIMENTAL_DOCKER_NETWORK"); n != "" { networkName = n } args := []string{ "--detach", // run the container detached "--tty", // allocate a tty for entrypoint logs // label the node with the cluster ID "--label", fmt.Sprintf("%s=%s", constants.NodeCCMLabelKey, clusterName), // label the node with the load balancer name "--label", fmt.Sprintf("%s=%s", constants.LoadBalancerNameLabelKey, loadBalancerSimpleName(clusterName, service)), // user a user defined docker network so we get embedded DNS "--net", networkName, "--init=false", "--hostname", name, // make hostname match container name // label the node with the role ID // running containers in a container requires privileged // NOTE: we could try to replicate this with --cap-add, and use less // privileges, but this flag also changes some mounts that are necessary // including some ones docker would otherwise do by default. // for now this is what we want. in the future we may revisit this. "--privileged", "--restart=on-failure", // to deal with the crash casued by https://github.com/envoyproxy/envoy/issues/34195 "--sysctl=net.ipv4.ip_forward=1", // allow ip forwarding "--sysctl=net.ipv4.conf.all.rp_filter=0", // disable rp filter "--sysctl=net.ipv4.ip_unprivileged_port_start=1", // Allow lower port numbers for podman (see https://github.com/containers/podman/blob/main/rootless.md for more info) } if isIPv6Service(service) { args = append(args, []string{ "--sysctl=net.ipv6.conf.all.disable_ipv6=0", // enable IPv6 "--sysctl=net.ipv6.conf.all.forwarding=1", // allow ipv6 forwarding}) }...) } if s.tunnelManager != nil || config.DefaultConfig.LoadBalancerConnectivity == config.Portmap { // Forward the Service Ports to the host so they are accessible on Mac and Windows. // For single IP-family services, explicitly bind on the matching listen address to avoid // dual-stack host bindings that cause connection resets in environments where IPv6 is // advertised but not functional end-to-end (e.g. some GitHub Actions runners). // For dual-stack or unspecified services, publish without an explicit address. // See https://github.com/kubernetes-sigs/cloud-provider-kind/issues/387 listenAddress := listenAddressForService(service) for _, port := range service.Spec.Ports { if port.Protocol != v1.ProtocolTCP && port.Protocol != v1.ProtocolUDP { continue } if listenAddress != "" { hostPortBinding := net.JoinHostPort(listenAddress, fmt.Sprintf("%d", port.Port)) args = append(args, fmt.Sprintf("--publish=%s:%d/%s", hostPortBinding, port.Port, port.Protocol)) } else { args = append(args, fmt.Sprintf("--publish=%d/%s", port.Port, port.Protocol)) } } } // publish the admin endpoint args = append(args, fmt.Sprintf("--publish=%d/%s", envoyAdminPort, v1.ProtocolTCP)) // Publish all ports in the host in random ports args = append(args, "--publish-all") if service.Spec.LoadBalancerIP != "" { args = append(args, "--ip", service.Spec.LoadBalancerIP) } args = append(args, image) // we need to override the default envoy configuration // https://www.envoyproxy.io/docs/envoy/latest/start/quick-start/configuration-dynamic-filesystem // envoy crashes in some circumstances, causing the container to restart, the problem is that the container // may come with a different IP and we don't update the status, we may do it, but applications does not use // to handle that the assigned LoadBalancerIP changes. // https://github.com/envoyproxy/envoy/issues/34195 cmd := []string{"bash", "-c", fmt.Sprintf(`echo -en '%s' > %s && touch %s && touch %s && while true; do envoy -c %s && break; sleep 1; done`, dynamicFilesystemConfig, proxyConfigPath, proxyConfigPathCDS, proxyConfigPathLDS, proxyConfigPath)} args = append(args, cmd...) klog.V(2).Infof("creating loadbalancer with parameters: %v", args) err := container.Create(name, args) if err != nil { return fmt.Errorf("failed to create continers %s %v: %w", name, args, err) } return nil } func isIPv6Service(service *v1.Service) bool { if service == nil { return false } for _, family := range service.Spec.IPFamilies { if family == v1.IPv6Protocol { return true } } return false } // listenAddressForService returns the host listen address to use when publishing // ports for a service. For single IP-family services it returns the corresponding // wildcard address ("0.0.0.0" or "::") so that Docker does not create spurious // dual-stack bindings. For dual-stack or unspecified services it returns "" so // the caller falls back to publishing without an explicit address. func listenAddressForService(service *v1.Service) string { var hasIPv4, hasIPv6 bool for _, family := range service.Spec.IPFamilies { switch family { case v1.IPv4Protocol: hasIPv4 = true case v1.IPv6Protocol: hasIPv6 = true } } if hasIPv4 && !hasIPv6 { return "0.0.0.0" } if hasIPv6 && !hasIPv4 { return "::" } return "" } ================================================ FILE: pkg/loadbalancer/server_test.go ================================================ package loadbalancer import ( "testing" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/cloud-provider-kind/pkg/constants" ) func TestIsIPv6Service(t *testing.T) { tests := []struct { name string service *v1.Service expected bool }{ { name: "nil service", service: nil, expected: false, }, { name: "IPv4-only service", service: &v1.Service{ Spec: v1.ServiceSpec{ IPFamilies: []v1.IPFamily{v1.IPv4Protocol}, }, }, expected: false, }, { name: "IPv6-only service", service: &v1.Service{ Spec: v1.ServiceSpec{ IPFamilies: []v1.IPFamily{v1.IPv6Protocol}, }, }, expected: true, }, { name: "dual-stack service", service: &v1.Service{ Spec: v1.ServiceSpec{ IPFamilies: []v1.IPFamily{v1.IPv4Protocol, v1.IPv6Protocol}, }, }, expected: true, }, { name: "no IPFamilies set", service: &v1.Service{}, expected: false, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { actual := isIPv6Service(test.service) if actual != test.expected { t.Errorf("expected %v, got %v", test.expected, actual) } }) } } func TestListenAddressForService(t *testing.T) { tests := []struct { name string service *v1.Service expected string }{ { name: "no IPFamilies set", service: &v1.Service{}, expected: "", }, { name: "IPv4-only service", service: &v1.Service{ Spec: v1.ServiceSpec{ IPFamilies: []v1.IPFamily{v1.IPv4Protocol}, }, }, expected: "0.0.0.0", }, { name: "IPv6-only service", service: &v1.Service{ Spec: v1.ServiceSpec{ IPFamilies: []v1.IPFamily{v1.IPv6Protocol}, }, }, expected: "::", }, { name: "dual-stack service", service: &v1.Service{ Spec: v1.ServiceSpec{ IPFamilies: []v1.IPFamily{v1.IPv4Protocol, v1.IPv6Protocol}, }, }, expected: "", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { actual := listenAddressForService(test.service) if actual != test.expected { t.Errorf("expected %q, got %q", test.expected, actual) } }) } } func TestLoadBalancerName(t *testing.T) { tests := []struct { name string cluster string service *v1.Service expected string expectedLen int }{ { name: "simple", cluster: "test-cluster", service: &v1.Service{ObjectMeta: metav1.ObjectMeta{Namespace: "test-namespace", Name: "test-service"}}, expected: constants.ContainerPrefix + "-11ab7482a104", expectedLen: 20, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { actual := loadBalancerName(test.cluster, test.service) if actual != test.expected { t.Errorf("expected %q, got %q", test.expected, actual) } if len(actual) != test.expectedLen { t.Errorf("expected length %d, got %d", test.expectedLen, len(actual)) } }) } } ================================================ FILE: pkg/provider/cloud.go ================================================ package provider import ( "sigs.k8s.io/cloud-provider-kind/pkg/constants" "sigs.k8s.io/cloud-provider-kind/pkg/loadbalancer" cloudprovider "k8s.io/cloud-provider" "sigs.k8s.io/kind/pkg/cluster" ) func New(clusterName string, kindClient *cluster.Provider) cloudprovider.Interface { return &cloud{ clusterName: clusterName, kindClient: kindClient, lbController: loadbalancer.NewServer(), } } var _ cloudprovider.Interface = (*cloud)(nil) // controller is the KIND implementation of the cloud provider interface type cloud struct { clusterName string // name of the kind cluster kindClient *cluster.Provider lbController cloudprovider.LoadBalancer } // Initialize passes a Kubernetes clientBuilder interface to the cloud provider func (c *cloud) Initialize(clientBuilder cloudprovider.ControllerClientBuilder, stopCh <-chan struct{}) { // noop } // Clusters returns the list of clusters. func (c *cloud) Clusters() (cloudprovider.Clusters, bool) { return c, true } // ProviderName returns the cloud provider ID. func (c *cloud) ProviderName() string { return constants.ProviderName } func (c *cloud) LoadBalancer() (cloudprovider.LoadBalancer, bool) { return c, true } func (c *cloud) Instances() (cloudprovider.Instances, bool) { return nil, false } func (c *cloud) Zones() (cloudprovider.Zones, bool) { return nil, false } func (c *cloud) Routes() (cloudprovider.Routes, bool) { return nil, false } func (c *cloud) HasClusterID() bool { return len(c.clusterName) > 0 } func (c *cloud) InstancesV2() (cloudprovider.InstancesV2, bool) { return c, true } ================================================ FILE: pkg/provider/clusters.go ================================================ package provider import ( "context" "fmt" cloudprovider "k8s.io/cloud-provider" "k8s.io/klog/v2" ) var _ cloudprovider.Clusters = &cloud{} // ListClusters lists the names of the available clusters. func (c *cloud) ListClusters(ctx context.Context) ([]string, error) { klog.V(2).Infof("List clusters") return c.kindClient.List() } // Master gets back the address (either DNS name or IP address) of the master node for the cluster. func (c *cloud) Master(ctx context.Context, clusterName string) (string, error) { klog.V(2).Infof("Get master for %s", clusterName) clusters, err := c.kindClient.List() if err != nil { return "", err } for _, cluster := range clusters { if cluster == clusterName { return clusterName + "-control-plane", nil } } return "", fmt.Errorf("cluster %s node found", clusterName) } ================================================ FILE: pkg/provider/instances.go ================================================ package provider import ( "context" "errors" "fmt" v1 "k8s.io/api/core/v1" cloudprovider "k8s.io/cloud-provider" "k8s.io/klog/v2" "sigs.k8s.io/kind/pkg/cluster/nodes" ) var _ cloudprovider.InstancesV2 = (*cloud)(nil) var errNodeNotFound = errors.New("node not found") // InstanceExists returns true if the instance for the given node exists according to the cloud provider. func (c *cloud) InstanceExists(ctx context.Context, node *v1.Node) (bool, error) { klog.V(2).Infof("Check if instance %s exists", node.Name) _, err := c.findNodeByName(node.Name) if err == nil { return true, nil } if errors.Is(err, errNodeNotFound) { return false, nil } return false, err } // InstanceShutdown returns true of the container doesn't exist func (c *cloud) InstanceShutdown(ctx context.Context, node *v1.Node) (bool, error) { klog.V(2).Infof("Check if instance %s is shutdown", node.Name) _, err := c.findNodeByName(node.Name) if err == nil { return false, nil } if errors.Is(err, errNodeNotFound) { return true, nil } return false, err } // InstanceMetadata returns the instance's metadata. The values returned in InstanceMetadata are // translated into specific fields and labels in the Node object on registration. func (c *cloud) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloudprovider.InstanceMetadata, error) { klog.V(2).Infof("Check instance metadata for %s", node.Name) n, err := c.findNodeByName(node.Name) if err != nil { return nil, err } m := &cloudprovider.InstanceMetadata{ // TODO: podman support ProviderID: fmt.Sprintf("kind://%s/kind/%s", c.clusterName, n.String()), // providerID: kind:///kind/ InstanceType: "kind-node", NodeAddresses: []v1.NodeAddress{ { Type: v1.NodeHostName, Address: n.String(), }, }, Zone: "", Region: "", } ipv4, ipv6, err := n.IP() if err != nil { return nil, err } if ipv4 != "" { m.NodeAddresses = append(m.NodeAddresses, v1.NodeAddress{Type: v1.NodeInternalIP, Address: ipv4}) } if ipv6 != "" { m.NodeAddresses = append(m.NodeAddresses, v1.NodeAddress{Type: v1.NodeInternalIP, Address: ipv6}) } klog.V(2).Infof("instance metadata for %s: %#v", node.Name, m) return m, nil } func (c *cloud) findNodeByName(name string) (nodes.Node, error) { nodes, err := c.kindClient.ListNodes(c.clusterName) if err != nil { return nil, fmt.Errorf("no nodes founds") } for _, n := range nodes { if n.String() == name { return n, nil } } return nil, fmt.Errorf("node with name %s does not exist on cluster %s", name, c.clusterName) } ================================================ FILE: pkg/provider/loadbalancer.go ================================================ package provider import ( "context" v1 "k8s.io/api/core/v1" cloudprovider "k8s.io/cloud-provider" "k8s.io/klog/v2" ) var _ cloudprovider.LoadBalancer = &cloud{} // GetLoadBalancer returns whether the specified load balancer exists, and if so, what its status is. // Parameter 'clusterName' is the name of the cluster as presented to kube-controller-manager func (c *cloud) GetLoadBalancer(ctx context.Context, clusterName string, service *v1.Service) (status *v1.LoadBalancerStatus, exists bool, err error) { klog.V(2).Infof("Get LoadBalancer cluster: %s service: %s", clusterName, service.Name) return c.lbController.GetLoadBalancer(ctx, clusterName, service) } // GetLoadBalancerName returns the name of the load balancer. func (c *cloud) GetLoadBalancerName(ctx context.Context, clusterName string, service *v1.Service) string { klog.V(2).Infof("Get LoadBalancerNmae cluster: %s service: %s", clusterName, service.Name) return c.lbController.GetLoadBalancerName(ctx, clusterName, service) } // EnsureLoadBalancer creates a new load balancer 'name', or updates the existing one. Returns the status of the balancer func (c *cloud) EnsureLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) (*v1.LoadBalancerStatus, error) { klog.V(2).Infof("Ensure LoadBalancer cluster: %s service: %s", clusterName, service.Name) return c.lbController.EnsureLoadBalancer(ctx, clusterName, service, nodes) } // UpdateLoadBalancer updates hosts under the specified load balancer. func (c *cloud) UpdateLoadBalancer(ctx context.Context, clusterName string, service *v1.Service, nodes []*v1.Node) error { klog.V(2).Infof("Update LoadBalancer cluster: %s service: %s", clusterName, service.Name) return c.lbController.UpdateLoadBalancer(ctx, clusterName, service, nodes) } // EnsureLoadBalancerDeleted deletes the specified load balancer if it // exists, returning nil if the load balancer specified either didn't exist or // was successfully deleted. func (c *cloud) EnsureLoadBalancerDeleted(ctx context.Context, clusterName string, service *v1.Service) error { klog.V(2).Infof("Ensure LoadBalancer deleted cluster: %s service: %s", clusterName, service.Name) return c.lbController.EnsureLoadBalancerDeleted(ctx, clusterName, service) } ================================================ FILE: pkg/tunnels/address_darwin.go ================================================ //go:build darwin package tunnels import ( "os/exec" ) func AddIPToLocalInterface(ip string) (string, error) { // TODO: IPv6 output, err := exec.Command("ifconfig", "lo0", "alias", ip, "netmask", "255.255.255.255").CombinedOutput() return string(output), err } func RemoveIPFromLocalInterface(ip string) (string, error) { // delete the IP address output, err := exec.Command("ifconfig", "lo0", "-alias", ip).CombinedOutput() return string(output), err } ================================================ FILE: pkg/tunnels/address_other.go ================================================ //go:build !windows && !darwin package tunnels import ( "os/exec" ) func AddIPToLocalInterface(ip string) (string, error) { output, err := exec.Command("ip", "addr", "add", ip, "dev", "lo").CombinedOutput() return string(output), err } func RemoveIPFromLocalInterface(ip string) (string, error) { output, err := exec.Command("ip", "addr", "del", ip, "dev", "lo").CombinedOutput() return string(output), err } ================================================ FILE: pkg/tunnels/address_windows.go ================================================ //go:build windows package tunnels import ( "os/exec" ) func AddIPToLocalInterface(ip string) (string, error) { output, err := exec.Command("netsh", "interface", "ip", "add", "address", "loopback", ip, "255.255.255.255").CombinedOutput() return string(output), err } func RemoveIPFromLocalInterface(ip string) (string, error) { output, err := exec.Command("netsh", "interface", "ip", "delete", "address", "loopback", ip, "255.255.255.255").CombinedOutput() return string(output), err } ================================================ FILE: pkg/tunnels/tunnel.go ================================================ package tunnels import ( "fmt" "io" "net" "strings" "sync" "time" "k8s.io/klog/v2" "sigs.k8s.io/cloud-provider-kind/pkg/container" ) type TunnelManager struct { mu sync.Mutex tunnels map[string]map[string]*tunnel // first key is the service namespace/name second key is the servicePort } func NewTunnelManager() *TunnelManager { t := &TunnelManager{ tunnels: map[string]map[string]*tunnel{}, } return t } func (t *TunnelManager) SetupTunnels(containerName string) error { // get the portmapping from the container and its internal IPs and forward them // 1. Create the fake IP on the tunnel interface // 2. Capture the traffic directed to that IP port and forward to the exposed port in the host portmaps, err := container.PortMaps(containerName) if err != nil { return err } klog.V(0).Infof("found port maps %v associated to container %s", portmaps, containerName) ipv4, _, err := container.IPs(containerName) if err != nil { return err } klog.V(0).Infof("setting IPv4 address %s associated to container %s", ipv4, containerName) // check if the IP is already assigned to the local interface if !ipOnHost(ipv4) { output, err := AddIPToLocalInterface(ipv4) if err != nil { return fmt.Errorf("error adding IP to local interface: %w - %s", err, output) } } // create tunnel from the ip:svcport to the localhost:portmap t.mu.Lock() defer t.mu.Unlock() _, ok := t.tunnels[containerName] if !ok { t.tunnels[containerName] = map[string]*tunnel{} } // Reconcile: Remove tunnels that are not in portmaps for containerPort, tun := range t.tunnels[containerName] { if _, ok := portmaps[containerPort]; !ok { klog.V(0).Infof("removing tunnel for %s %s as it is no longer in portmaps", containerName, containerPort) tun.Stop() // nolint: errcheck delete(t.tunnels[containerName], containerPort) } } // There is one IP per Service and a tunnel per Service Port for containerPort, hostPort := range portmaps { parts := strings.Split(containerPort, "/") if len(parts) != 2 { return fmt.Errorf("expected format port/protocol for container port, got %s", containerPort) } if _, ok := t.tunnels[containerName][containerPort]; ok { klog.V(2).Infof("tunnel for %s %s already exists", containerName, containerPort) continue } tun := NewTunnel(ipv4, parts[0], parts[1], "localhost", hostPort) // TODO check if we can leak tunnels err = tun.Start() if err != nil { return err } t.tunnels[containerName][containerPort] = tun } return nil } func (t *TunnelManager) RemoveTunnels(containerName string) error { klog.V(0).Infof("stopping tunnels on containers %s", containerName) t.mu.Lock() defer t.mu.Unlock() tunnels, ok := t.tunnels[containerName] if !ok { return nil } // all tunnels in the same container share the same local IP on the host var tunnelIP string for _, tunnel := range tunnels { if tunnelIP == "" { tunnelIP = tunnel.localIP } tunnel.Stop() // nolint: errcheck } delete(t.tunnels, containerName) klog.V(0).Infof("Removing IPv4 address %s associated to local interface", tunnelIP) output, err := RemoveIPFromLocalInterface(tunnelIP) if err != nil { return fmt.Errorf("error removing IP from local interface: %w - %s", err, output) } return nil } // tunnel listens on localIP:localPort and proxies the connection to remoteIP:remotePort type tunnel struct { listener net.Listener udpConn *net.UDPConn udpDone chan struct{} localIP string localPort string protocol string remoteIP string // address:Port remotePort string } func NewTunnel(localIP, localPort, protocol, remoteIP, remotePort string) *tunnel { return &tunnel{ localIP: localIP, localPort: localPort, protocol: protocol, remoteIP: remoteIP, remotePort: remotePort, } } func (t *tunnel) Start() error { klog.Infof("Starting tunnel on %s %s", net.JoinHostPort(t.localIP, t.localPort), t.protocol) switch t.protocol { case "udp": localAddrStr := net.JoinHostPort(t.localIP, t.localPort) udpAddr, err := net.ResolveUDPAddr("udp4", localAddrStr) if err != nil { return err } conn, err := net.ListenUDP("udp4", udpAddr) if err != nil { return err } // use a channel for signaling that the UDP connection has been closed t.udpDone = make(chan struct{}) t.udpConn = conn go func() { for { select { case <-t.udpDone: return default: } err = t.handleUDPConnection(conn) if err != nil { klog.Infof("unexpected error on connection: %v", err) } } }() case "tcp", "tcp4", "tcp6": ln, err := net.Listen(t.protocol, net.JoinHostPort(t.localIP, t.localPort)) if err != nil { return err } t.listener = ln go func() { for { conn, err := ln.Accept() if err != nil { klog.Infof("unexpected error listening: %v", err) return } tcpConn, ok := conn.(*net.TCPConn) if !ok { klog.Errorf("unexpected connection type %T, expected *net.TCPConn", conn) conn.Close() // nolint: errcheck continue } go func() { err := t.handleTCPConnection(tcpConn) if err != nil { klog.Infof("unexpected error on connection: %v", err) } }() } }() default: return fmt.Errorf("unsupported protocol %s", t.protocol) } return nil } func (t *tunnel) Stop() error { if t.listener != nil { _ = t.listener.Close() } if t.udpConn != nil { // Let the UDP handling code know that it can give up select { case <-t.udpDone: default: close(t.udpDone) } _ = t.udpConn.Close() } return nil } func (t *tunnel) handleTCPConnection(local *net.TCPConn) error { tcpAddr, err := net.ResolveTCPAddr(t.protocol, net.JoinHostPort(t.remoteIP, t.remotePort)) if err != nil { return fmt.Errorf("can't resolve remote address %q: %v", net.JoinHostPort(t.remoteIP, t.remotePort), err) } remote, err := net.DialTCP(t.protocol, nil, tcpAddr) if err != nil { return fmt.Errorf("can't connect to server %q: %v", net.JoinHostPort(t.remoteIP, t.remotePort), err) } // Fully close both connections on return. defer remote.Close() defer local.Close() wg := &sync.WaitGroup{} wg.Add(2) go func() { defer wg.Done() io.Copy(local, remote) // nolint: errcheck // Half-close the remote to local path. local.CloseWrite() // nolint: errcheck remote.CloseRead() // nolint: errcheck }() go func() { defer wg.Done() io.Copy(remote, local) // nolint: errcheck // Half-close the local to remote path. remote.CloseWrite() // nolint: errcheck local.CloseRead() // nolint: errcheck }() wg.Wait() return nil } func (t *tunnel) handleUDPConnection(conn *net.UDPConn) error { buf := make([]byte, 1500) nRead, srcAddr, err := conn.ReadFromUDP(buf) if err != nil { return err } klog.V(4).Infof("Read %d bytes from %s", nRead, srcAddr.String()) klog.V(4).Infof("Connecting to %s", net.JoinHostPort(t.remoteIP, t.remotePort)) remoteConn, err := net.Dial("udp4", net.JoinHostPort(t.remoteIP, t.remotePort)) if err != nil { return fmt.Errorf("can't connect to server %q: %v", net.JoinHostPort(t.remoteIP, t.remotePort), err) } defer remoteConn.Close() nWrite, err := remoteConn.Write(buf[:nRead]) if err != nil { return fmt.Errorf("fail to write to remote %s: %s", remoteConn.RemoteAddr(), err) } else if nWrite < nRead { klog.V(2).Infof("Buffer underflow %d < %d to remote %s", nWrite, nRead, remoteConn.RemoteAddr()) } klog.V(4).Infof("Wrote %d bytes to to %s", nWrite, remoteConn.RemoteAddr().String()) buf = make([]byte, 1500) err = remoteConn.SetReadDeadline(time.Now().Add(5 * time.Second)) // Add deadline to ensure it doesn't block forever if err != nil { return fmt.Errorf("can not set read deadline: %v", err) } nRead, err = remoteConn.Read(buf) if err != nil { return fmt.Errorf("fail to read from remote %s: %s", remoteConn.RemoteAddr(), err) } klog.V(4).Infof("Read %d bytes from %s", nRead, remoteConn.RemoteAddr().String()) _, err = conn.WriteToUDP(buf[:nRead], srcAddr) return err } func ipOnHost(ip string) bool { ifaces, err := net.Interfaces() if err != nil { return false } for _, i := range ifaces { addrs, err := i.Addrs() if err != nil { continue } for _, addr := range addrs { var ipAddr net.IP switch v := addr.(type) { case *net.IPNet: ipAddr = v.IP case *net.IPAddr: ipAddr = v.IP } if ipAddr != nil && ipAddr.String() == ip { return true } } } return false } ================================================ FILE: tests/README.md ================================================ # Integration tests 1. Install `bats` https://bats-core.readthedocs.io/en/stable/installation.html 2. Install `kind` https://kind.sigs.k8s.io/ 3. Run the tests: ``` bats tests/ bats tests/custom-network ``` ================================================ FILE: tests/custom-network/setup_suite.bash ================================================ #!/bin/bash set -eu function setup_suite { export BATS_TEST_TIMEOUT=120 # Define the name of the kind cluster export CLUSTER_NAME="ccm-kind-custom-network" export ARTIFACTS_DIR="$BATS_TEST_DIRNAME"/../../_artifacts-custom-network mkdir -p "$ARTIFACTS_DIR" rm -rf "$ARTIFACTS_DIR"/* # create custom docker network export KIND_EXPERIMENTAL_DOCKER_NETWORK=kind-static docker network create \ --driver=bridge \ --subnet=172.20.0.0/16 $KIND_EXPERIMENTAL_DOCKER_NETWORK # create cluster kind create cluster \ --name $CLUSTER_NAME \ -v7 --wait 1m --retain --config="$BATS_TEST_DIRNAME/../kind.yaml" # build & run cloud-provider-kind cd "$BATS_TEST_DIRNAME"/../.. && make nohup "$BATS_TEST_DIRNAME"/../../bin/cloud-provider-kind -v 2 --enable-log-dumping --logs-dir "$ARTIFACTS_DIR" > "$ARTIFACTS_DIR"/ccm-kind.log 2>&1 & export CCM_PID=$! # test depend on external connectivity that can be very flaky sleep 5 } function teardown_suite { kill "$CCM_PID" kind export logs "$ARTIFACTS_DIR" --name "$CLUSTER_NAME" kind delete cluster --name "$CLUSTER_NAME" docker network rm "$KIND_EXPERIMENTAL_DOCKER_NETWORK" } ================================================ FILE: tests/custom-network/tests.bats ================================================ #!/usr/bin/env bats @test "Static LoadBalancerIP" { # the static IP must be part of the custom docker network - calculate the .100 IP from the custom kind IPv4 network SUBNET=$(docker network inspect $KIND_EXPERIMENTAL_DOCKER_NETWORK --format '{{range .IPAM.Config}}{{.Subnet}}{{"\n"}}{{end}}' | grep -E '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+') STATIC_IP=$(echo "$SUBNET" | awk -F'[./]' '{printf "%s.%s.%s.100", $1, $2, $3}') echo "Using static IP: $STATIC_IP" TMP_YAML=$(mktemp) sed "s/REPLACE_WITH_STATIC_IP/$STATIC_IP/g" "$BATS_TEST_DIRNAME"/../../examples/loadbalancer_static_ip.yaml > "$TMP_YAML" kubectl apply -f "$TMP_YAML" kubectl wait --for=condition=ready pods -l app=static-ip --timeout=60s for i in {1..5} do IP=$(kubectl get services static-ip --output jsonpath='{.status.loadBalancer.ingress[0].ip}') [[ ! -z "$IP" ]] && break || sleep 1 done echo "Assigned IP: $IP" [ "$IP" = "$STATIC_IP" ] POD=$(kubectl get pod -l app=static-ip -o jsonpath='{.items[0].metadata.name}') echo "Pod $POD" for i in {1..5} do HOSTNAME=$(curl -s http://${IP}:80/hostname || true) [[ ! -z "$HOSTNAME" ]] && break || sleep 1 done echo "Hostname via TCP: $HOSTNAME" [ "$HOSTNAME" = "$POD" ] kubectl delete -f "$TMP_YAML" rm "$TMP_YAML" } ================================================ FILE: tests/kind.yaml ================================================ kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 nodes: - role: control-plane - role: worker - role: worker ================================================ FILE: tests/setup_suite.bash ================================================ #!/bin/bash set -eu function setup_suite { export BATS_TEST_TIMEOUT=120 # Define the name of the kind cluster export CLUSTER_NAME="ccm-kind" export ARTIFACTS_DIR="$BATS_TEST_DIRNAME"/../_artifacts mkdir -p "$ARTIFACTS_DIR" rm -rf "$ARTIFACTS_DIR"/* # create cluster kind create cluster --name $CLUSTER_NAME -v7 --wait 1m --retain --config="$BATS_TEST_DIRNAME/kind.yaml" cd "$BATS_TEST_DIRNAME"/.. && make nohup "$BATS_TEST_DIRNAME"/../bin/cloud-provider-kind -v 2 --gateway-channel=standard --enable-log-dumping --logs-dir "$ARTIFACTS_DIR" > "$ARTIFACTS_DIR"/ccm-kind.log 2>&1 & export CCM_PID=$! # test depend on external connectivity that can be very flaky sleep 5 } function teardown_suite { kill "$CCM_PID" kind export logs "$ARTIFACTS_DIR" --name "$CLUSTER_NAME" kind delete cluster --name "$CLUSTER_NAME" } ================================================ FILE: tests/tests.bats ================================================ #!/usr/bin/env bats @test "ExternalTrafficPolicy: Local" { kubectl apply -f "$BATS_TEST_DIRNAME"/../examples/loadbalancer_etp_local.yaml kubectl wait --for=condition=ready pods -l app=MyLocalApp for i in {1..5} do IP=$(kubectl get services lb-service-local --output jsonpath='{.status.loadBalancer.ingress[0].ip}') [[ ! -z "$IP" ]] && break || sleep 1 done echo "IP: $IP" POD=$(kubectl get pod -l app=MyLocalApp -o jsonpath='{.items[0].metadata.name}') echo "Pod $POD" for i in {1..5} do HOSTNAME=$(curl -s http://${IP}:80/hostname || true) [[ ! -z "$HOSTNAME" ]] && break || sleep 1 done echo "Hostname via TCP: $HOSTNAME" [ "$HOSTNAME" = "$POD" ] kubectl delete -f "$BATS_TEST_DIRNAME"/../examples/loadbalancer_etp_local.yaml } @test "ExternalTrafficPolicy: Cluster" { kubectl apply -f "$BATS_TEST_DIRNAME"/../examples/loadbalancer_etp_cluster.yaml kubectl wait --for=condition=ready pods -l app=MyClusterApp for i in {1..5} do IP=$(kubectl get services lb-service-cluster --output jsonpath='{.status.loadBalancer.ingress[0].ip}') [[ ! -z "$IP" ]] && break || sleep 1 done echo "IP: $IP" POD=$(kubectl get pod -l app=MyClusterApp -o jsonpath='{.items[0].metadata.name}') echo "Pod $POD" for i in {1..5} do HOSTNAME=$(curl -s http://${IP}:80/hostname || true) [[ ! -z "$HOSTNAME" ]] && break || sleep 1 done echo "Hostname via TCP: $HOSTNAME" [ "$HOSTNAME" = "$POD" ] kubectl delete -f "$BATS_TEST_DIRNAME"/../examples/loadbalancer_etp_cluster.yaml } @test "Multiple Protocols: UDP and TCP" { kubectl apply -f "$BATS_TEST_DIRNAME"/../examples/loadbalancer_udp_tcp.yaml kubectl wait --for=condition=ready pods -l app=multiprotocol for i in {1..5} do IP=$(kubectl get services multiprotocol --output jsonpath='{.status.loadBalancer.ingress[0].ip}') [[ ! -z "$IP" ]] && break || sleep 1 done echo "IP: $IP" POD=$(kubectl get pod -l app=multiprotocol -o jsonpath='{.items[0].metadata.name}') echo "Pod $POD" for i in {1..5} do HOSTNAME=$(curl -s http://${IP}:80/hostname || true) [[ ! -z "$HOSTNAME" ]] && break || sleep 1 done echo "Hostname via TCP: $HOSTNAME" [ "$HOSTNAME" = "$POD" ] for i in {1..5} do HOSTNAME=$(echo hostname | nc -u -w 3 ${IP} 80 || true) [[ ! -z "$HOSTNAME" ]] && break || sleep 1 done echo "Hostname via UDP: $HOSTNAME" [[ ! -z "$HOSTNAME" ]] && [ "$HOSTNAME" = "$POD" ] kubectl delete -f "$BATS_TEST_DIRNAME"/../examples/loadbalancer_udp_tcp.yaml } @test "Simple Gateway" { # Apply the Gateway and HTTPRoute manifests kubectl apply -f "$BATS_TEST_DIRNAME"/../examples/gateway_httproute_simple.yaml # Wait for the backend application pod to be ready kubectl wait --for=condition=ready pods -l app=MyApp --timeout=60s # Retry loop to get the Gateway's external IP address for i in {1..10} do # Fetch the IP address assigned by the load balancer to the Gateway IP=$(kubectl get gateway prod-web --output jsonpath='{.status.addresses[0].value}' 2>/dev/null) # Check if IP is not empty and break the loop if found [[ ! -z "$IP" ]] && break || sleep 1 done # Fail the test if IP is still empty after retries if [[ -z "$IP" ]]; then echo "Failed to get Gateway IP address" return 1 fi echo "Gateway IP: $IP" # Get the name of the backend pod POD=$(kubectl get pod -l app=MyApp -o jsonpath='{.items[0].metadata.name}') echo "Backend Pod: $POD" # Retry loop to curl the backend service through the Gateway IP for i in {1..10} do # Curl the /hostname endpoint via the Gateway IP, ignore failures temporarily HOSTNAME=$(curl -s --connect-timeout 5 http://${IP}:80/hostname || true) # Check if HOSTNAME is not empty and break the loop if successful [[ ! -z "$HOSTNAME" ]] && break || sleep 1 done # Fail the test if HOSTNAME is still empty after retries if [[ -z "$HOSTNAME" ]]; then echo "Failed to get hostname via Gateway" return 1 fi echo "Hostname via Gateway (TCP): $HOSTNAME" # Assert that the hostname returned by the service matches the actual pod name [ "$HOSTNAME" = "$POD" ] # Cleanup: Delete the applied manifests kubectl delete --ignore-not-found -f "$BATS_TEST_DIRNAME"/../examples/gateway_httproute_simple.yaml } @test "Ingress to Gateway Migration and X-Forwarded-For Header" { # Apply the Gateway and HTTPRoute manifests kubectl apply -f "$BATS_TEST_DIRNAME"/../examples/ingress_foo_bar.yaml # Wait for the backend application pod to be ready kubectl wait --for=condition=ready pods -l app=foo --timeout=60s kubectl wait --for=condition=ready pods -l app=foo --timeout=60s # Give the controller time to reconcile echo "Waiting for reconciliation..." sleep 5 echo "Finding Ingress Loadbalancer IP ..." run kubectl get ingress example-ingress -o jsonpath='{.status.loadBalancer.ingress[0].ip}' [ "$status" -eq 0 ] export INGRESS_SVC_IP="$output" echo "Ingress LoadBalancer IP: $INGRESS_SVC_IP" # Test /foo prefix echo "Testing /foo prefix (should match foo-app)..." run kubectl exec curl-pod -- curl -H "Host: foo.example.com" -s "http://$INGRESS_SVC_IP/hostname" [ "$status" -eq 0 ] [[ "$output" == "foo-app" ]] # Test /bar prefix echo "Testing /bar prefix (should match bar-app)..." run kubectl exec curl-pod -- curl -H "Host: bar.example.com" -s "http://$INGRESS_SVC_IP/hostname" [ "$status" -eq 0 ] [[ "$output" == "bar-app" ]] # Test X-Forwarded-For header echo "Testing X-Forwarded-For header..." run kubectl exec curl-pod -- curl -H "Host: foo.example.com" -s "http://$INGRESS_SVC_IP/header?key=X-Forwarded-For" [ "$status" -eq 0 ] echo "X-Forwarded-For header value: $output" [[ ! -z "$output" ]] # Cleanup: Delete the applied manifests kubectl delete --ignore-not-found -f "$BATS_TEST_DIRNAME"/../examples/ingress_foo_bar.yaml } @test "Ingress WebSocket Support" { # 1. Deploy a WebSocket-capable echo server cat <