diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..540f380e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,36 @@
+---
+name: Bug report
+about: Create a report to help us improve STUNner
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+### Description
+
+[Description of the problem]
+
+### Steps to Reproduce
+
+[Brief description of the steps you took to encounter the problem, if applicable]
+
+**Expected behavior:** [What you expected to happen]
+
+**Actual behavior:** [What actually happened]
+
+### Versions
+
+[Which version of STUNner you are using]
+
+### Info
+
+[Please copy-paste the output of the below commands and make sure to remove all sensitive information, like usernames, passwords, IP addresses, etc.]
+
+#### Gateway API status
+
+[Output of `kubectl get gateways,gatewayconfigs,gatewayclasses,udproutes.stunner.l7mp.io --all-namespaces -o yaml`]
+
+#### Operator logs
+
+[Output of `kubectl -n stunner-system logs $(kubectl get pods -l control-plane=stunner-gateway-operator-controller-manager --all-namespaces -o jsonpath='{.items[0].metadata.name}')`]
diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test-dev.yaml
similarity index 64%
rename from .github/workflows/e2e-test.yml
rename to .github/workflows/e2e-test-dev.yaml
index 546b0d10..cc14c0ad 100644
--- a/.github/workflows/e2e-test.yml
+++ b/.github/workflows/e2e-test-dev.yaml
@@ -1,20 +1,20 @@
-name: Run End-to-End Test
+name: Run End-to-End Test (dev)
on:
workflow_dispatch:
schedule:
- - cron: '0 11 * * 1'
+ - cron: '0 12 * * 1'
jobs:
e2e_test:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Set up Go
- uses: actions/setup-go@v3
+ uses: actions/setup-go@v5
with:
- go-version: 1.19
+ go-version: '1.23'
- name: Download modules
run: go mod download
@@ -29,22 +29,22 @@ jobs:
driver: docker
container-runtime: containerd
wait: all
+ cpus: max
cache: false
- name: Start minikube tunnel
run: minikube tunnel &>mktunnel.log &
- name: Set up Helm
- uses: azure/setup-helm@v3
+ uses: azure/setup-helm@v4
with:
- version: v3.11.3
+ version: v3.16.2
- name: Install STUNner
run: |
helm repo add stunner https://l7mp.io/stunner
helm repo update
- helm install stunner-gateway-operator stunner/stunner-gateway-operator-dev --create-namespace --namespace=stunner
- helm install stunner stunner/stunner-dev --create-namespace --namespace=stunner
+ helm install stunner-gateway-operator stunner/stunner-gateway-operator-dev --create-namespace --namespace=stunner --set stunnerGatewayOperator.deployment.container.manager.resources.requests.cpu=200m --set stunnerGatewayOperator.dataplane.spec.resources.requests.cpu=100m
- name: Deploy iperf server
run: kubectl apply -f docs/examples/simple-tunnel/iperf-server.yaml
@@ -61,12 +61,16 @@ jobs:
- name: Wait for LoadBalancer IP
run: |
- while [[ -z $(kubectl get svc udp-gateway -n stunner -o jsonpath="{.status.loadBalancer.ingress[0].ip}") ]]; do echo "Waiting for LoadBalancer IP"; sleep 2; done
+ for n in {1..60}; do [[ ! -z $(kubectl get svc udp-gateway -n stunner -o jsonpath="{.status.loadBalancer.ingress[0].ip}") ]] && break; echo "Waiting for LoadBalancer IP"; sleep 2; done
+ echo "* wait for the deployment"
+ kubectl get all -A
+ kubectl wait -n stunner --for=condition=Available deployment udp-gateway --timeout 5m
+ echo "* EVERYTHING UP"
kubectl get all -A
- name: Start turncat
run: |
- ./turncat --log=all:INFO udp://127.0.0.1:5000 k8s://stunner/stunnerd-config:udp-listener udp://$(kubectl get svc iperf-server -o jsonpath="{.spec.clusterIP}"):5001 &>turncat.log &
+ ./turncat --log=all:INFO udp://127.0.0.1:5000 k8s://stunner/udp-gateway:udp-listener udp://$(kubectl get svc iperf-server -o jsonpath="{.spec.clusterIP}"):5001 &>turncat.log &
sleep 1
- name: Run iperf client
@@ -86,6 +90,8 @@ jobs:
cat turncat.log
echo "* STUNNER"
kubectl logs -n stunner $(kubectl get pods -n stunner -l app=stunner -o jsonpath='{.items[0].metadata.name}')
+ echo "* STUNNER-GATEWAY=OPERATOR"
+ kubectl logs -n stunner $(kubectl get pods -n stunner -l control-plane=stunner-gateway-operator-controller-manager -o jsonpath='{.items[0].metadata.name}')
- name: Check iperf conectivity
run: grep "Server Report" iperf.log
diff --git a/.github/workflows/e2e-test-stable.yml b/.github/workflows/e2e-test-stable.yml
new file mode 100644
index 00000000..e28b6ad5
--- /dev/null
+++ b/.github/workflows/e2e-test-stable.yml
@@ -0,0 +1,87 @@
+name: Run End-to-End Test (stable)
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: '0 11 1-7,15-21 * 2'
+
+jobs:
+ e2e_test:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Install turncat
+ run: |
+ URL=`wget -q -O - https://api.github.com/repos/l7mp/stunner/releases/latest | jq -r '.assets[] | select(.name | contains ("turncat")) | select(.name | contains ("linux")) | select(.name | contains ("amd64")) | .browser_download_url'`
+ wget $URL -O turncat
+ chmod a+x turncat
+
+ - name: Start minikube
+ uses: medyagh/setup-minikube@master
+ with:
+ driver: docker
+ container-runtime: containerd
+ wait: all
+ cache: false
+
+ - name: Start minikube tunnel
+ run: minikube tunnel &>mktunnel.log &
+
+ - name: Set up Helm
+ uses: azure/setup-helm@v4
+ with:
+ version: v3.16.2
+
+ - name: Install STUNner
+ run: |
+ helm repo add stunner https://l7mp.io/stunner
+ helm repo update
+ helm install stunner-gateway-operator stunner/stunner-gateway-operator --create-namespace --namespace=stunner --set stunnerGatewayOperator.dataplane.mode=managed --set stunnerGatewayOperator.deployment.container.manager.resources.requests.cpu=200m --set stunnerGatewayOperator.dataplane.spec.resources.requests.cpu=100m
+
+ - name: Deploy iperf server
+ run: kubectl apply -f docs/examples/simple-tunnel/iperf-server.yaml
+
+ - name: Configure STUNner
+ run: |
+ kubectl apply -f docs/examples/simple-tunnel/iperf-stunner.yaml
+ sleep 75
+
+ - name: Install iperf client
+ run: |
+ sudo apt-get update
+ sudo apt-get -y install iperf
+
+ - name: Wait for LoadBalancer IP
+ run: |
+ for n in {1..60}; do [[ ! -z $(kubectl get svc udp-gateway -n stunner -o jsonpath="{.status.loadBalancer.ingress[0].ip}") ]] && break; echo "Waiting for LoadBalancer IP"; sleep 2; done
+ kubectl wait -n stunner --for=condition=Available deployment udp-gateway --timeout 5m
+ kubectl get all -A
+
+ - name: Start turncat
+ run: |
+ ./turncat --log=all:INFO udp://127.0.0.1:5000 k8s://stunner/udp-gateway:udp-listener udp://$(kubectl get svc iperf-server -o jsonpath="{.spec.clusterIP}"):5001 &>turncat.log &
+ sleep 1
+
+ - name: Run iperf client
+ run: |
+ iperf -c 127.0.0.1 -p 5000 -u -l 100 -b 5M -t 5 | tee iperf.log
+
+ - name: Show logs
+ run: |
+ echo "* IPERF"
+ echo "** Client"
+ cat iperf.log
+ echo "** Server"
+ kubectl logs $(kubectl get pods -l app=iperf-server -o jsonpath='{.items[0].metadata.name}')
+ echo "* MINIKUBE TUNNEL"
+ cat mktunnel.log
+ echo "* TURNCAT"
+ cat turncat.log
+ echo "* STUNNER"
+ kubectl logs -n stunner $(kubectl get pods -n stunner -l app=stunner -o jsonpath='{.items[0].metadata.name}')
+ echo "* STUNNER-GATEWAY=OPERATOR"
+ kubectl logs -n stunner $(kubectl get pods -n stunner -l control-plane=stunner-gateway-operator-controller-manager -o jsonpath='{.items[0].metadata.name}')
+
+ - name: Check iperf conectivity
+ run: grep "Server Report" iperf.log
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index 45ac2fd1..2341c8ef 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -18,12 +18,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Install Go
- uses: actions/setup-go@v3
+ uses: actions/setup-go@v5
with:
- go-version: 1.19
+ go-version: '1.23'
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Run linters
- uses: golangci/golangci-lint-action@v3
+ uses: golangci/golangci-lint-action@v4
with:
- args: --timeout 3m --issues-exit-code=0
+ args: --timeout 5m
diff --git a/.github/workflows/publish--add-binaries.yml b/.github/workflows/publish--add-binaries.yml
new file mode 100644
index 00000000..733cdb97
--- /dev/null
+++ b/.github/workflows/publish--add-binaries.yml
@@ -0,0 +1,68 @@
+name: "publish: Add binaries to release assets"
+
+on:
+ workflow_call:
+
+jobs:
+ add_binaries:
+ name: Add binaries to release assets
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ include:
+ - os: linux
+ arch: amd64
+ file_end: ""
+ - os: linux
+ arch: arm64
+ file_end: ""
+
+ - os: darwin
+ arch: amd64
+ file_end: ""
+ - os: darwin
+ arch: arm64
+ file_end: ""
+
+ - os: windows
+ arch: amd64
+ file_end: ".exe"
+ - os: windows
+ arch: arm64
+ file_end: ".exe"
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Get version
+ id: vars
+ run: echo tag=$(echo ${GITHUB_REF:11}) >> $GITHUB_OUTPUT
+
+ - name: Install Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: '1.23'
+
+ - name: Build binaries
+ run: |
+ export CGO_ENABLED=0 GOOS=${{ matrix.os }} GOARCH=${{ matrix.arch }}
+ make build-bin
+ mv bin/turncat turncat-v${{ steps.vars.outputs.tag }}-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.file_end }}
+ mv bin/stunnerctl stunnerctl-v${{ steps.vars.outputs.tag }}-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.file_end }}
+
+ - name: Release turncat binary
+ uses: svenstaro/upload-release-action@v2
+ with:
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
+ file: turncat-v${{ steps.vars.outputs.tag }}-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.file_end }}
+ tag: ${{ github.ref_name }}
+ asset_name: turncat-v${{ steps.vars.outputs.tag }}-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.file_end }}
+
+ - name: Release stunnerctl binary
+ uses: svenstaro/upload-release-action@v2
+ with:
+ repo_token: ${{ secrets.GITHUB_TOKEN }}
+ file: stunnerctl-v${{ steps.vars.outputs.tag }}-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.file_end }}
+ tag: ${{ github.ref_name }}
+ asset_name: stunnerctl-v${{ steps.vars.outputs.tag }}-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.file_end }}
diff --git a/.github/workflows/publish--push-charts.yml b/.github/workflows/publish--push-charts.yml
new file mode 100644
index 00000000..f39fdf7e
--- /dev/null
+++ b/.github/workflows/publish--push-charts.yml
@@ -0,0 +1,39 @@
+name: "publish: Push Helm charts to web"
+
+on:
+ workflow_call:
+ inputs:
+ dev:
+ description: Whether to release a dev version
+ required: true
+ type: boolean
+
+jobs:
+ push_charts:
+ name: Push Helm charts to web
+ runs-on: ubuntu-latest
+ steps:
+ - name: Get version for non-dev release
+ if: ${{ inputs.dev == false || inputs.dev == 'false' }}
+ id: vars
+ run: echo tag=$(echo ${GITHUB_REF:11}) >> $GITHUB_OUTPUT
+
+ - name: Trigger release workflow in the stunner-helm repo
+ if: ${{ inputs.dev == false || inputs.dev == 'false' }}
+ uses: convictional/trigger-workflow-and-wait@v1.6.5
+ with:
+ github_token: ${{ secrets.WEB_PAT_TOKEN }}
+ owner: l7mp
+ repo: stunner-helm
+ client_payload: '{"tag": "${{ steps.vars.outputs.tag }}", "type": "stunner"}'
+ workflow_file_name: publish.yaml
+
+ - name: Trigger release workflow in the stunner-helm repo
+ if: ${{ inputs.dev == true || inputs.dev == 'true' }}
+ uses: convictional/trigger-workflow-and-wait@v1.6.5
+ with:
+ github_token: ${{ secrets.WEB_PAT_TOKEN }}
+ owner: l7mp
+ repo: stunner-helm
+ client_payload: '{"tag": "dev", "type": "stunner"}'
+ workflow_file_name: publish.yaml
diff --git a/.github/workflows/publish-dev.yaml b/.github/workflows/publish-dev.yaml
index 800e1609..783d3cd0 100644
--- a/.github/workflows/publish-dev.yaml
+++ b/.github/workflows/publish-dev.yaml
@@ -1,4 +1,4 @@
-name: "release-dev"
+name: Release (dev)
on:
workflow_dispatch:
@@ -12,35 +12,41 @@ on:
- 'main'
jobs:
- push_to_registry:
- name: Push Docker image to DockerHub
+ run_tests:
+ name: Run tests
+ uses: l7mp/stunner/.github/workflows/test.yml@main
+
+ push_stunner_to_registry:
+ name: Push STUNner image to DockerHub
+ needs: run_tests
+ if: github.repository == 'l7mp/stunner'
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Docker meta
id: meta
- uses: docker/metadata-action@v4
+ uses: docker/metadata-action@v5
with:
images: l7mp/stunnerd
tags: |
type=raw,value=dev
- name: Set up QEMU
- uses: docker/setup-qemu-action@v2
+ uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
+ uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
- uses: docker/login-action@v2
+ uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Build and Push
- uses: docker/build-push-action@v3
+ uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
@@ -49,14 +55,49 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
push_chart:
- name: Push helm charts to the
+ name: Push helm charts to the repo
+ if: github.repository == 'l7mp/stunner'
+ needs: push_stunner_to_registry
+ uses: l7mp/stunner/.github/workflows/publish--push-charts.yml@main
+ with:
+ dev: true
+ secrets: inherit
+
+ push_icetester_to_registry:
+ name: Push icetester image to DockerHub
+ needs: run_tests
+ if: github.repository == 'l7mp/stunner'
runs-on: ubuntu-latest
steps:
- - name: Triggering release workflow in the stunner-helm repo
- uses: convictional/trigger-workflow-and-wait@v1.6.5
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Docker meta
+ id: meta
+ uses: docker/metadata-action@v5
with:
- github_token: ${{ secrets.WEB_PAT_TOKEN }}
- owner: l7mp
- repo: stunner-helm
- client_payload: '{"tag": "dev", "type": "stunner"}'
- workflow_file_name: publish.yaml
+ images: l7mp/icetester
+ tags: |
+ type=raw,value=dev
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Login to Docker Hub
+ uses: docker/login-action@v3
+ with:
+ username: ${{ secrets.DOCKER_USER }}
+ password: ${{ secrets.DOCKER_TOKEN }}
+
+ - name: Build and Push
+ uses: docker/build-push-action@v5
+ with:
+ file: Dockerfile.icetester
+ context: .
+ platforms: linux/amd64,linux/arm64
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index b2005e75..d63d42f0 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -1,4 +1,4 @@
-name: "release"
+name: Release
on:
push:
@@ -6,16 +6,21 @@ on:
- 'v[0-9]+.[0-9]+.0'
jobs:
- push_to_registry:
- name: Push Docker image to DockerHub
+ run_tests:
+ name: Run tests
+ uses: l7mp/stunner/.github/workflows/test.yml@main
+
+ push_stunner_to_registry:
+ name: Push STUNner image to DockerHub
+ needs: run_tests
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Docker meta
id: meta
- uses: docker/metadata-action@v4
+ uses: docker/metadata-action@v5
with:
images: l7mp/stunnerd
tags: |
@@ -23,19 +28,19 @@ jobs:
type=raw,value=latest
- name: Set up QEMU
- uses: docker/setup-qemu-action@v2
+ uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
+ uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
- uses: docker/login-action@v2
+ uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_TOKEN }}
- name: Build and Push
- uses: docker/build-push-action@v3
+ uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
@@ -44,19 +49,54 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
push_chart:
- name: Push charts to the web
+ name: Push helm charts to the repo
+ needs: push_stunner_to_registry
+ uses: l7mp/stunner/.github/workflows/publish--push-charts.yml@main
+ with:
+ dev: false
+ secrets: inherit
+
+ push_icetester_to_registry:
+ name: Push icetester image to DockerHub
+ needs: run_tests
+ if: github.repository == 'l7mp/stunner'
runs-on: ubuntu-latest
steps:
+ - name: Checkout
+ uses: actions/checkout@v4
- - name: Get version
- id: vars
- run: echo tag=$(echo ${GITHUB_REF:11}) >> $GITHUB_OUTPUT
+ - name: Docker meta
+ id: meta
+ uses: docker/metadata-action@v5
+ with:
+ images: l7mp/icetester
+ tags: |
+ type=semver,pattern={{version}}
+ type=raw,value=latest
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@v3
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v3
- - name: Triggering release workflow in the stunner-helm repo
- uses: convictional/trigger-workflow-and-wait@v1.6.5
+ - name: Login to Docker Hub
+ uses: docker/login-action@v3
+ with:
+ username: ${{ secrets.DOCKER_USER }}
+ password: ${{ secrets.DOCKER_TOKEN }}
+
+ - name: Build and Push
+ uses: docker/build-push-action@v5
with:
- github_token: ${{ secrets.WEB_PAT_TOKEN }}
- owner: l7mp
- repo: stunner-helm
- client_payload: '{"tag": "${{ steps.vars.outputs.tag }}", "type": "stunner"}'
- workflow_file_name: publish.yaml
+ file: Dockerfile.icetester
+ context: .
+ platforms: linux/amd64,linux/arm64
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
+
+ add_binaries:
+ name: Add binaries to release assets
+ uses: l7mp/stunner/.github/workflows/publish--add-binaries.yml@main
+ needs: run_tests
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 7ab2719c..156a8d7f 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -1,6 +1,8 @@
name: Tests
on:
+ workflow_call:
+ workflow_dispatch:
push:
paths:
- '**.go'
@@ -17,11 +19,11 @@ jobs:
test:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Set up Go
- uses: actions/setup-go@v3
+ uses: actions/setup-go@v5
with:
- go-version: 1.19
+ go-version: '1.23'
- name: Download modules
run: go mod download
- name: Go install
@@ -30,21 +32,22 @@ jobs:
run: go test -v -covermode=count
coverage:
runs-on: ubuntu-latest
+ if: github.repository == 'l7mp/stunner'
steps:
- name: Install Go
if: success()
- uses: actions/setup-go@v3
+ uses: actions/setup-go@v5
with:
- go-version: 1.19
+ go-version: '1.23'
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Calc coverage
run: |
go test -v -covermode=count -coverprofile=coverage.out
- name: Convert coverage.out to coverage.lcov
uses: jandelgado/gcov2lcov-action@v1
- name: Coveralls
- uses: coverallsapp/github-action@v1
+ uses: coverallsapp/github-action@v2
with:
github-token: ${{ secrets.github_token }}
path-to-lcov: coverage.lcov
diff --git a/.gitignore b/.gitignore
index c7339885..5d20408a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,4 +25,5 @@ go.work
# Our binaries
turncat
+stunnerctl
stunnerd
\ No newline at end of file
diff --git a/.readthedocs.yaml b/.readthedocs.yaml
index d9222bb5..37ceb77e 100644
--- a/.readthedocs.yaml
+++ b/.readthedocs.yaml
@@ -6,10 +6,10 @@
version: 2
# Set the version of Python and other tools you might need
-# build:
-# os: ubuntu-22.04
-# tools:
-# python: "3.10"
+build:
+ os: ubuntu-22.04
+ tools:
+ python: "3.12"
mkdocs:
configuration: mkdocs.yml
diff --git a/Dockerfile b/Dockerfile
index f1b6fd44..63d52e97 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,26 +1,30 @@
###########
# BUILD
-FROM golang:1.19-alpine as builder
+FROM golang:1.23-alpine as builder
WORKDIR /app
COPY go.mod ./
COPY go.sum ./
-RUN go mod download
COPY *.go ./
COPY internal/ internal/
COPY pkg/ pkg/
-COPY cmd/stunnerd/main.go cmd/stunnerd/
-COPY cmd/stunnerd/stunnerd.conf cmd/stunnerd/
+COPY cmd/ cmd/
+
+COPY .git ./
+COPY Makefile ./
+RUN apk add --no-cache git make
RUN apkArch="$(apk --print-arch)"; \
case "$apkArch" in \
aarch64) export GOARCH='arm64' ;; \
*) export GOARCH='amd64' ;; \
esac; \
- CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o stunnerd cmd/stunnerd/main.go
+ export CGO_ENABLED=0; \
+ export GOOS=linux; \
+ make build-bin
###########
# STUNNERD
@@ -28,7 +32,7 @@ FROM scratch
WORKDIR /app
-COPY --from=builder /app/stunnerd /usr/bin/
+COPY --from=builder /app/bin/stunnerd /usr/bin/
COPY --from=builder /app/cmd/stunnerd/stunnerd.conf /
EXPOSE 3478/udp
diff --git a/Dockerfile.icetester b/Dockerfile.icetester
new file mode 100644
index 00000000..ce243532
--- /dev/null
+++ b/Dockerfile.icetester
@@ -0,0 +1,39 @@
+###########
+# BUILD
+FROM docker.io/golang:1.23-alpine as builder
+
+WORKDIR /app
+
+COPY go.mod ./
+COPY go.sum ./
+
+COPY *.go ./
+COPY internal/ internal/
+COPY pkg/ pkg/
+
+COPY cmd/ cmd/
+
+COPY .git ./
+COPY Makefile ./
+RUN apk add --no-cache git make
+
+RUN apkArch="$(apk --print-arch)"; \
+ case "$apkArch" in \
+ aarch64) export GOARCH='arm64' ;; \
+ *) export GOARCH='amd64' ;; \
+ esac; \
+ export CGO_ENABLED=0; \
+ export GOOS=linux; \
+ make build-bin
+
+###########
+# STUNNERD
+FROM scratch
+
+WORKDIR /app
+
+COPY --from=builder /app/bin/icetester /usr/bin/
+
+EXPOSE 8089/tcp
+
+CMD [ "icetester", "-l", "all:INFO" ]
diff --git a/LICENSE b/LICENSE
index 621376f6..116904bc 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2023 l7mp
+Copyright (c) 2024 l7mp
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..15838cab
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,53 @@
+# Build variables
+PACKAGE = github.com/l7mp/stunner
+BUILD_DIR ?= bin/
+VERSION ?= $(shell (git describe --tags --abbrev=8 --always --long) | tr "/" "-")
+COMMIT_HASH ?= $(shell git rev-parse --short HEAD 2>/dev/null)
+BUILD_DATE ?= $(shell date +%FT%T%z)
+LDFLAGS += -s -w
+LDFLAGS += -X main.version=${VERSION} -X main.commitHash=${COMMIT_HASH} -X main.buildDate=${BUILD_DATE}
+GOARGS = -trimpath
+
+ifeq (${VERBOSE}, 1)
+ifeq ($(filter -v,${GOARGS}),)
+ GOARGS += -v
+endif
+endif
+
+.PHONY: all
+all: build
+
+.PHONY: generate
+generate: ## OpenAPI codegen
+ go generate ./pkg/config/...
+
+.PHONY: fmt
+fmt: ## Run go fmt against code.
+ go fmt ./...
+
+.PHONY: vet
+vet: ## Run go vet against code.
+ go vet ./...
+
+.PHONY: test
+test: generate fmt vet
+ go test ./... -v
+
+##@ Build
+
+.PHONY: build
+build: generate fmt vet build-bin
+
+.PHONY: build-bin
+bin: build-bin
+build-bin:
+ go build ${GOARGS} -ldflags "${LDFLAGS}" -o ${BUILD_DIR}/stunnerd cmd/stunnerd/main.go
+ go build ${GOARGS} -ldflags "${LDFLAGS}" -o ${BUILD_DIR}/turncat cmd/turncat/main.go
+ go build ${GOARGS} -ldflags "${LDFLAGS}" -o ${BUILD_DIR}/stunnerctl cmd/stunnerctl/*.go
+ go build ${GOARGS} -ldflags "${LDFLAGS}" -o ${BUILD_DIR}/icetester cmd/icetester/main.go
+
+.PHONY: clean
+clean:
+ # echo 'Use "make generate" to autogenerate server code' > pkg/server/server.go
+ # echo 'Use "make generate" to autogenerate client code' > pkg/client/client.go
+ # echo 'Use "make generate" to autogenerate client code' > pkg/types/types.go
diff --git a/README.md b/README.md
index 9585a46f..6346c382 100644
--- a/README.md
+++ b/README.md
@@ -32,12 +32,14 @@
+*Note: This page documents the latest development version of STUNner. See the documentation for the stable version [here](https://docs.l7mp.io/en/stable).*
+
# STUNner: A Kubernetes media gateway for WebRTC
Ever wondered how to [deploy your WebRTC infrastructure into the
cloud](https://webrtchacks.com/webrtc-media-servers-in-the-cloud)? Frightened away by the
complexities of Kubernetes container networking, and the surprising ways in which it may interact
-with your UDP/RTP media? Tried to read through the endless stream of [Stack
+with your UDP/RTP media? Read through the endless stream of [Stack
Overflow](https://stackoverflow.com/search?q=kubernetes+webrtc)
[questions](https://stackoverflow.com/questions/61140228/kubernetes-loadbalancer-open-a-wide-range-thousands-of-port)
[asking](https://stackoverflow.com/questions/64232853/how-to-use-webrtc-with-rtcpeerconnection-on-kubernetes)
@@ -52,19 +54,16 @@ Worry no more! STUNner allows you to deploy *any* WebRTC service into Kubernetes
integrating it into the [cloud-native ecosystem](https://landscape.cncf.io). STUNner exposes a
standards-compliant STUN/TURN gateway for clients to access your virtualized WebRTC infrastructure
running in Kubernetes, maintaining full browser compatibility and requiring minimal or no
-modification to your existing WebRTC codebase. STUNner implements the standard [Kubernetes Gateway
+modification to your existing WebRTC codebase. STUNner supports the [Kubernetes Gateway
API](https://gateway-api.sigs.k8s.io) so you can configure it in the familiar YAML-engineering
style via Kubernetes manifests.
-See the full documentation [here](https://docs.l7mp.io/en/latest).
-
## Table of Contents
1. [Description](#description)
1. [Features](#features)
1. [Getting started](#getting-started)
-1. [Tutorials](#tutorials)
+1. [Usage](#usage)
1. [Documentation](#documentation)
-1. [Caveats](#caveats)
1. [Milestones](#milestones)
## Description
@@ -83,21 +82,18 @@ features we have come to expect from modern network services. Worse yet, the ent
on a handful of [public](https://bloggeek.me/google-free-turn-server/) [STUN
servers](https://www.npmjs.com/package/freeice) and [hosted TURN
services](https://bloggeek.me/managed-webrtc-turn-speed) to connect clients behind a NAT/firewall,
-which may create a useless dependency on externally operated services, introduce a bottleneck,
-raise security concerns, and come with a non-trivial price tag.
+which may create a useless dependency on externally operated services, introduce a performance
+bottleneck, raise security concerns, and come with a non-trivial price tag.
The main goal of STUNner is to allow *anyone* to deploy their own WebRTC infrastructure into
Kubernetes, without relying on any external service other than the cloud-provider's standard hosted
-Kubernetes offering. This is achieved by STUNner acting as a gateway for ingesting WebRTC media
-traffic into the Kubernetes cluster, exposing a public-facing STUN/TURN server that WebRTC clients
-can connect to.
-
-STUNner can act as a STUN/TURN server that WebRTC clients and media servers can use as a scalable
-NAT traversal facility (headless model), or it can serve as a fully-fledged ingress gateway for
-clients to reach a media server deployed behind STUNner (media-plane model). This makes it possible
-to deploy WebRTC application servers and media servers into ordinary Kubernetes pods, taking
-advantage of Kubernetes's excellent tooling to manage, scale, monitor and troubleshoot the WebRTC
-infrastructure like any other cloud-bound workload.
+Kubernetes offering. STUNner can act as a standalone STUN/TURN server that WebRTC clients and media
+servers can use as a scalable NAT traversal facility (headless model), or it can act as a gateway
+for ingesting WebRTC media traffic into the Kubernetes cluster by exposing a public-facing
+STUN/TURN server that WebRTC clients can connect to (media-plane model). This makes it possible to
+deploy WebRTC application servers and media servers into ordinary Kubernetes pods, taking advantage
+of the full cloud native feature set to manage, scale, monitor and troubleshoot the WebRTC
+infrastructure like any other Kubernetes workload.
![STUNner media-plane deployment architecture](./docs/img/stunner_arch.svg)
@@ -130,7 +126,7 @@ way.
[hacks](https://kubernetes.io/docs/concepts/configuration/overview), like privileged pods and
`hostNetwork`/`hostPort` services, typically recommended as a prerequisite to containerizing your
WebRTC media plane. Using STUNner a WebRTC deployment needs only two public-facing ports, one
- HTTPS port for the application server and a *single* UDP port for *all* your media.
+ HTTPS port for signaling and a *single* UDP port for *all* your media.
* **No reliance on external services for NAT traversal.** Can't afford a [hosted TURN
service](https://bloggeek.me/webrtc-turn) for client-side NAT traversal? Can't get decent
@@ -139,453 +135,115 @@ way.
can connect to it directly without the use of *any* external STUN/TURN service whatsoever, apart
from STUNner itself.
-* **Easily scale your WebRTC infrastructure.** Tired of manually provisioning your WebRTC media
- servers? STUNner lets you deploy the entire WebRTC infrastructure into ordinary Kubernetes pods,
- thus [scaling the media plane](docs/SCALING.md) is as easy as issuing a `kubectl scale`
- command. Even better, use the built in Kubernetes horizontal autoscaler to *automatically* resize
- your workload based on demand.
+* **Scale your WebRTC infrastructure.** Tired of manually provisioning your WebRTC media servers?
+ STUNner lets you deploy the entire WebRTC infrastructure into ordinary Kubernetes pods, thus
+ [scaling the media plane](docs/SCALING.md) is as easy as issuing a `kubectl scale` command. Or
+ you can use the built in Kubernetes horizontal autoscaler to *automatically* resize your workload
+ based on demand.
+
+* **Minimal client-side configuration.** STUNner comes with a built-in [authentication
+ service](https://github.com/l7mp/stunner-auth-service) that can be used to generate time-windowed
+ per-user TURN credentials through a [standards
+ compliant](https://datatracker.ietf.org/doc/html/draft-uberti-behave-turn-rest-00) HTTP [REST
+ API](/docs/AUTH.md). Just set the generated [ICE
+ configuration](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection#configuration)
+ in the [`PeerConnection` JavaScript
+ API](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection) and
+ your clients will readily start using your Kubernetes-based WebRTC service via STUNner.
* **Secure perimeter defense.** No need to open thousands of UDP/TCP ports on your media server for
potentially malicious access; with STUNner *all* media is received through a single ingress port
that you can tightly monitor and control.
-
-
-
* **Simple code and extremely small size.** Written in pure Go using the battle-tested
[pion/webrtc](https://github.com/pion/webrtc) framework, STUNner is just a couple of hundred
lines of fully open-source code. The server is extremely lightweight: the typical STUNner
- container image size is only about 5 Mbytes.
+ container image size is only 15 Mbytes.
-## Getting Started
+The main uses of STUNner are [hosting a scalable STUN server
+pool](https://medium.com/l7mp-technologies/deploying-a-scalable-stun-service-in-kubernetes-c7b9726fa41d)
+in Kubernetes, as a public Kubernetes-based [TURN
+service](https://github.com/l7mp/stunner/blob/main/docs/DEPLOYMENT.md#headless-deployment-model),
+or as a fully-fledged [gateway
+service](https://github.com/l7mp/stunner/blob/main/docs/DEPLOYMENT.md#media-plane-deployment-model)
+for ingesting and load-balancing clients' media connections across a pool of WebRTC media servers
+hosted in ordinary Kubernetes pods.
-STUNner comes with a [Helm](https://helm.sh) chart to fire up a fully functional STUNner-based
-WebRTC media gateway in minutes. Note that the default installation does not contain an application
-server and a media server: STUNner is not a WebRTC service, it is merely an *enabler* for you to
-deploy your *own* WebRTC infrastructure into Kubernetes. Once installed, STUNner makes sure that
-your media servers are readily reachable to WebRTC clients, despite running with a private IP
-address inside a Kubernetes pod. See the [tutorials](#tutorials) for some ideas on how to deploy an
-actual WebRTC application behind STUNner.
+## Getting Started
With a minimal understanding of WebRTC and Kubernetes, deploying STUNner should take less than 5
-minutes.
+minutes, in five simple steps.
-* [Customize STUNner and deploy it](#installation) into your Kubernetes cluster.
-* Optionally [deploy a WebRTC media server](docs/examples/kurento-one2one-call).
-* [Set STUNner as the ICE server](#configuring-webrtc-clients) in your WebRTC clients.
+* [Customize STUNner and deploy it](/docs/INSTALL.md) into your Kubernetes cluster.
+* Optionally [deploy a WebRTC media server](/docs/README.md#media-plane-deployment-model).
+* [Set STUNner as the ICE server](/docs/AUTH.md) in your WebRTC clients.
* ...
* Profit!!
-### Installation
+Note that the default installation does not contain an application server and a media server:
+STUNner is not a WebRTC service in itself, it is merely an *enabler* for you to deploy your *own*
+WebRTC infrastructure into Kubernetes.
The simplest way to deploy STUNner is through [Helm](https://helm.sh). STUNner configuration
parameters are available for customization as [Helm
-Values](https://helm.sh/docs/chart_template_guide/values_files). We recommend deploying STUNner
-into a separate namespace and we usually name this namespace as `stunner`, so as to isolate it from
-the rest of the workload.
+Values](https://helm.sh/docs/chart_template_guide/values_files).
```console
helm repo add stunner https://l7mp.io/stunner
helm repo update
-helm install stunner-gateway-operator stunner/stunner-gateway-operator --create-namespace --namespace=stunner-system
-helm install stunner stunner/stunner --create-namespace --namespace=stunner
+helm install stunner-gateway-operator stunner/stunner-gateway-operator --create-namespace \
+ --namespace=stunner-system
```
Find out more about the charts in the [STUNner-helm repository](https://github.com/l7mp/stunner-helm).
-### Configuration
-
-The standard way to interact with STUNner is via the standard Kubernetes [Gateway
- API](https://gateway-api.sigs.k8s.io) version
- [v1alpha2](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec). This is much akin to the
- way you configure *all* Kubernetes workloads: specify your intents in YAML files and issue a
- `kubectl apply`, and the [STUNner gateway
- operator](https://github.com/l7mp/stunner-gateway-operator) will automatically reconcile the
- STUNner dataplane for the new configuration.
-
-1. Given a fresh STUNner install, the first step is to register STUNner with the Kubernetes Gateway
- API. This amounts to creating a
- [GatewayClass](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayClass),
- which serves as the [root level configuration](/docs/GATEWAY.md#gatewayclass) for your STUNner
- deployment.
-
- Each GatewayClass must specify a controller that will manage the Gateway objects created under
- the class hierarchy. This must be set to `stunner.l7mp.io/gateway-operator` in order for STUNner
- to pick up the GatewayClass. In addition, a GatewayClass can refer to further
- implementation-specific configuration via a reference called `parametersRef`; in our case, this
- will be a GatewayConfig object to be specified next.
-
- ``` console
- kubectl apply -f - < **Warning**
-STUNner deviates somewhat from the standard rules Kubernetes uses to handle ports in Services. In
-Kubernetes each Service is associated with one or more protocol-port pairs and connections via the
-Service can be made to only these specific protocol-port pairs. WebRTC media servers, however,
-usually open lots of different ports, typically one per each client connection, and it would be
-cumbersome to create a separate backend Service and UDPRoute for each port. In order to simplify
-this, STUNner **ignores the protocol and port specified in the backend service** and allows
-connections to the backend pods via *any* protocol-port pair. STUNner can therefore use only a
-*single* backend Service to reach any port exposed on a WebRTC media server.
-
-> Considering the above example: even if the `default/media-plane` Service was created for the TCP:80 port, STUNner will allow connections via any protocol-port pair, say, via UDP:10000 or any other UDP port for that matter. This hack remains our only viable way to support WebRTC workloads in Kubernetes until [support for port ranges is implemented in Kubernetes services](https://github.com/kubernetes/kubernetes/issues/23864). Note that this affects only the *internal* backend services: STUNner is still exposed *externally* via a *single* protocol-port, but it can demultiplex incoming client media connections to any *internal* backend ports via a single UDPRoute.
-
-And that's all. You don't need to worry about client-side NAT traversal and WebRTC media routing
-because STUNner has you covered! Even better, every time you change a Gateway API resource in
-Kubernetes, say, you update the GatewayConfig to reset your STUN/TURN credentials or change the
-protocol or port in one of your Gateways, the [STUNner gateway
-operator](https://github.com/l7mp/stunner-gateway-operator) will automatically pick up your
-modifications and update the underlying dataplane. Kubernetes is beautiful, isn't it?
-
-### Check your config
-
-The current STUNner dataplane configuration is always made available in a convenient ConfigMap
-called `stunnerd-config` (you can choose the name in the GatewayConfig). The STUNner dataplane pods
-themselves will use the very same ConfigMap to reconcile their internal state, so you can consider
-the content to be the ground truth.
-
-STUNner comes with a small utility to dump the running configuration in human readable format (you
-must have [`jq`](https://stedolan.github.io/jq) installed in your PATH to be able to use it). Chdir
-into the main STUNner directory and issue.
-
-```console
-cmd/stunnerctl/stunnerctl running-config stunner/stunnerd-config
-STUN/TURN authentication type: plaintext
-STUN/TURN username: user-1
-STUN/TURN password: pass-1
-Listener: udp-listener
-Protocol: UDP
-Public address: 34.118.36.108
-Public port: 3478
-```
-
-As it turns out, STUNner has successfully assigned a public IP and port to our Gateway and set the
-STUN/TURN credentials based on the GatewayConfig. You can use the below to dump the entire running
-configuration; `jq` is there just to pretty-print JSON.
-
-```console
-kubectl get cm -n stunner stunnerd-config -o jsonpath="{.data.stunnerd\.conf}" | jq .
-```
-
-### Testing
-
-We have successfully configured STUNner to route client connections to the `media-plane` service
-but at the moment there is no backend there that would respond. Below we use a simplistic UDP
-greeter service for testing: every time you send some input, the greeter service will respond with
-a heartwarming welcome message.
-
-1. Fire up the UDP greeter service.
-
- The below manifest spawns the service in the `default` namespace and wraps it in a Kubernetes
- service called `media-plane`. Recall, this is the target service STUNner will route connections
- to. Note that the type of the `media-plane` service is `ClusterIP`, which means that Kubernetes
- will *not* expose it to the Internet: the only way for clients to obtain a response is via
- STUNner.
-
- ```console
- kubectl apply -f deploy/manifests/udp-greeter.yaml
- ```
-
-1. We also need the ClusterIP assigned by Kubernetes to the `media-plane` service.
-
- ```console
- export PEER_IP=$(kubectl get svc media-plane -o jsonpath='{.spec.clusterIP}')
- ```
-
-1. We also need a STUN/TURN client to actually initiate a connection. STUNner comes with a handy
- STUN/TURN client called [`turncat`](cmd/turncat/README.md) for this purpose. Once
- [built](cmd/turncat/README.md#installation), you can fire up `turncat` to listen on the standard
- input and send everything it receives to STUNner. Type any input and press Enter, and you should
- see a nice greeting from your cluster!
-
- ```console
- ./turncat - k8s://stunner/stunnerd-config:udp-listener udp://${PEER_IP}:9001
- Hello STUNner
- Greetings from STUNner!
- ```
-
-Observe that we haven't specified the public IP address and port: `turncat` is clever enough to
-parse the running [STUNner configuration](#check-your-config) from Kubernetes directly. Just
-specify the special STUNner URI `k8s://stunner/stunnerd-config:udp-listener`, identifying the
-namespace (`stunner` here) and the name for the STUNner ConfigMap (`stunnerd-config`), plus the
-listener to connect to (`udp-listener`), and `turncat` will do the heavy lifting.
-
-Note that your actual WebRTC clients do *not* need to use `turncat` to reach the cluster: all
-modern Web browsers and WebRTC clients come with a STUN/TURN client built in. Here, `turncat` is
-used only to *simulate* what a real WebRTC client would do when trying to reach STUNner.
-
-### Reconcile
-
-Any time you see fit, you can update the STUNner configuration through the Gateway API: STUNner
-will automatically reconcile the dataplane for the new configuration.
-
-For instance, you may decide to open up your WebRTC infrastructure on TLS/TCP as well; say, because
-an enterprise NAT on the client network path has gone berserk and actively filters anything except
-TLS/443. The below steps will do just that: open another gateway on STUNner, this time on the
-TLS/TCP port 443, and reattach the UDPRoute to both Gateways so that no matter which protocol a
-client may choose the connection will be routed to the `media-plane` service (i.e., the UDP
-greeter) by STUNner.
-
-1. Store your TLS certificate in a Kubernetes Secret. Below we create a self-signed certificate for
- testing, make sure to substitute this with a valid certificate.
-
- ```console
- openssl genrsa -out ca.key 2048
- openssl req -x509 -new -nodes -days 365 -key ca.key -out ca.crt -subj "/CN=yourdomain.com"
- kubectl -n stunner create secret tls tls-secret --key ca.key --cert ca.crt
- ```
-
-1. Add the new TLS Gateway. Notice how the `tls-listener` now contains a `tls` object that refers
- the above Secret, this way assigning the TLS certificate to use with our TLS listener.
-
- ```console
- kubectl apply -f - <`) with the correct configuration
-from the running STUNner config; don't forget that `stunnerctl` is always there for you to help.
-
-```js
-var ICE_config = {
- iceServers: [
- {
- url: 'turn::?transport=udp',
- username: ,
- credential: ,
- },
- ],
-};
-var pc = new RTCPeerConnection(ICE_config);
-```
-
-Note that STUNner comes with a built-in [authentication
-service](https://github.com/l7mp/stunner-auth-service) that can be used to generate a complete ICE
-configuration for reaching STUNner through a [HTTP REST API](docs/AUTH.md).
-
-## Tutorials
-
-The below series of tutorials demonstrates how to leverage STUNner to deploy different WebRTC
-applications into Kubernetes.
-
-### Basics
-
-* [Opening a UDP tunnel via STUNner](/docs/examples/simple-tunnel/README.md): This introductory tutorial
- shows how to tunnel an external connection via STUNner to a UDP service deployed into
- Kubernetes. The demo can be used to quickly check and benchmark a STUNner installation.
-
-### Headless deployment mode
-
+## Usage
+
+STUNner comes with a wide selection of tutorials and demos that teach you how to deploy all kinds
+of WebRTC services into Kubernetes. The first couple of tutorials present the basic concepts,
+especially the use of the [Kubernetes Gateway API](https://gateway-api.sigs.k8s.io) to configure
+STUNner and the [`turncat`](/docs/cmd/turncat.md) utility to test it. Each subsequent demo
+showcases a specific WebRTC application, from desktop streaming and video-conferencing to
+cloud-gaming, and goes from a clean Kubernetes cluster to a working and usable publicly available
+WebRTC service in 5-10 minutes using a purely declarative configuration.
+
+* [Deploying a UDP echo service behind STUNner](/docs/examples/udp-echo/README.md): This
+ introductory tutorial shows how to deploy a simple UDP echo service into Kubernetes and expose it
+ via STUNner. If you read just one STUNner tutorial, this should be it.
+* [Opening a UDP tunnel via STUNner](/docs/examples/simple-tunnel/README.md): This tutorial shows
+ how to tunnel an external UDP client via STUNner to a standard iperf server deployed into
+ Kubernetes. The demo can be used to benchmark your STUNner installation.
* [Direct one to one video call via STUNner](/docs/examples/direct-one2one-call/README.md): This
tutorial showcases STUNner acting as a TURN server for two WebRTC clients to establish
connections between themselves, without the mediation of a media server.
-
-### Media-plane deployment model
-
-* [One to one video call with Kurento](/docs/examples/kurento-one2one-call/README.md): This tutorial
- shows how to use STUNner to connect WebRTC clients to a media server deployed into Kubernetes
- behind STUNner in the [media-plane deployment model](/docs/DEPLOYMENT.md). All this happens
- *without* modifying the media server code in any way, just by adding 5-10 lines of
- straightforward JavaScript to configure clients to use STUNner as the TURN server.
-* [Magic mirror with Kurento](/docs/examples/kurento-magic-mirror/README.md): This tutorial has been
- adopted from the [Kurento](https://www.kurento.org) [magic
- mirror](https://doc-kurento.readthedocs.io/en/stable/tutorials/node/tutorial-magicmirror.html)
- demo, deploying a basic WebRTC loopback server behind STUNner with some media processing
- added. In particular, the application uses computer vision and augmented reality techniques to
- add a funny hat on top of faces.
* [Video-conferencing with LiveKit](/docs/examples/livekit/README.md): This tutorial helps you deploy
the [LiveKit](https://livekit.io) WebRTC media server behind STUNner. The docs also show how to
obtain a valid TLS certificate to secure your signaling connections, courtesy of the
[cert-manager](https://cert-manager.io) project, [nip.io](https://nip.io) and [Let's
Encrypt](https://letsencrypt.org).
+* [Video-conferencing with Janus](/docs/examples/janus/README.md): This tutorial helps you deploy a
+ fully fledged [Janus](https://janus.conf.meetecho.com/) video-conferencing service into Kubernetes
+ behind STUNner. The docs also show how to obtain a valid TLS certificate to secure your signaling
+ connections, using [cert-manager](https://cert-manager.io), [nip.io](https://nip.io) and [Let's
+ Encrypt](https://letsencrypt.org).
+* [Video-conferencing with Elixir WebRTC](/docs/examples/elixir-webrtc/README.md): This tutorial helps
+ you deploy a fully fledged [Elixir WebRTC](https://elixir-webrtc.org/) video-conferencing room called
+ [Nexus](https://github.com/elixir-webrtc/apps/tree/master/nexus) into Kubernetes
+ behind STUNner. The docs also show how to obtain a valid TLS certificate to secure your signaling
+ connections, using [cert-manager](https://cert-manager.io), [nip.io](https://nip.io) and [Let's
+ Encrypt](https://letsencrypt.org).
* [Video-conferencing with Jitsi](/docs/examples/jitsi/README.md): This tutorial helps you deploy a
fully fledged [Jitsi](https://jitsi.org) video-conferencing service into Kubernetes behind
STUNner. The docs also show how to obtain a valid TLS certificate to secure your signaling
connections, using [cert-manager](https://cert-manager.io), [nip.io](https://nip.io) and [Let's
Encrypt](https://letsencrypt.org).
-* [Cloud-gaming with Cloudretro](/docs/examples/cloudretro/README.md): This tutorial lets you play Super
- Mario or Street Fighter in your browser, courtesy of the amazing
+* [Video-conferencing with mediasoup](/docs/examples/mediasoup/README.md): This tutorial helps you
+ deploy the [mediasoup](https://mediasoup.org/) WebRTC media server behind STUNner. The docs also
+ show how to obtain a valid TLS certificate to secure your signaling connections, courtesy of the
+ [cert-manager](https://cert-manager.io) project, [nip.io](https://nip.io) and [Let's
+ Encrypt](https://letsencrypt.org).
+* [Cloud-gaming with Cloudretro](/docs/examples/cloudretro/README.md): This tutorial lets you play
+ Super Mario or Street Fighter in your browser, courtesy of the amazing
[CloudRetro](https://cloudretro.io) project and, of course, STUNner. The demo also presents a
simple multi-cluster setup, where clients can reach the game-servers in their geographical
locality to minimize latency.
@@ -593,41 +251,38 @@ applications into Kubernetes.
providing an ingress gateway service to a remote desktop application. We use
[neko.io](https://neko.m1k1o.net) to run a browser in a secure container inside the Kubernetes
cluster, and stream the desktop to clients via STUNner.
+* [One to one video call with Kurento](/docs/examples/kurento-one2one-call/README.md): This tutorial
+ shows how to use STUNner to connect WebRTC clients to a media server deployed into Kubernetes
+ behind STUNner in the [media-plane deployment model](/docs/DEPLOYMENT.md). All this happens
+ *without* modifying the media server code in any way, just by adding 5-10 lines of
+ straightforward JavaScript to configure clients to use STUNner as the TURN server.
+* [Magic mirror with Kurento](/docs/examples/kurento-magic-mirror/README.md): This tutorial has been
+ adopted from the [Kurento](https://www.kurento.org) [magic
+ mirror](https://doc-kurento.readthedocs.io/en/stable/tutorials/node/tutorial-magicmirror.html)
+ demo, deploying a basic WebRTC loopback server behind STUNner with some media processing
+ added. In particular, the application uses computer vision and augmented reality techniques to
+ add a funny hat on top of faces.
## Documentation
-See the full documentation [here](/docs/README.md).
-
-## Caveats
-
-STUNner is a work-in-progress. Some features are missing, others may not work as expected. The
-notable limitations at this point are as follows.
-
-* STUNner targets only a *partial implementation of the Kubernetes Gateway API.* In particular,
- only GatewayClass, Gateway and UDPRoute resources are supported. This is intended: STUNner
- deliberately ignores some complexity in the [Gateway API](https://gateway-api.sigs.k8s.io) and
- deviates from the prescribed behavior in some cases, all in the name of simplifying the
- configuration process. The [STUNner Kubernetes gateway
- operator](https://github.com/l7mp/stunner-gateway-operator) docs contain a [detailed
- list](https://github.com/l7mp/stunner-gateway-operator#caveats) on the differences.
-* STUNner supports *multiple parallel GatewayClass hierarchies*, each deployed into a separate
- namespace with a separate GatewayClass an a separate dataplane. This mode can be useful for
- testing new STUNner versions or canary-upgrades and A/B testing of a new media server version. At
- the moment, however, this mode is not supported: it should work but we don' test it.
+The documentation of the stable release can be found [here](https://docs.l7mp.io/en/stable). The
+documentation for the latest development release can be found [here](/docs/README.md).
## Milestones
* v0.9: Demo release: STUNner basic UDP/TURN connectivity + helm chart + tutorials.
-* v0.10: Dataplane: Long-term STUN/TURN credentials and [STUN/TURN over
- TCP/TLS/DTLS](https://www.rfc-editor.org/rfc/rfc6062.txt) in standalone mode.
+* v0.10: Dataplane: Long-term STUN/TURN credentials and [STUN/TURN over TCP/TLS/DTLS](https://www.rfc-editor.org/rfc/rfc6062.txt) in standalone mode.
* v0.11: Control plane: Kubernetes gateway operator and dataplane reconciliation.
* v0.12: Security: Expose TLS/DTLS settings via the Gateway API.
* v0.13: Observability: Prometheus + Grafana dashboard.
-* v0.15: Performance: per-allocation CPU load-balancing for UDP
-* v0.16: Management: managed STUNner dataplane.
-* v0.17: Performance: eBPF TURN acceleration.
-* v1.0: GA
-* v2.0: Service mesh: adaptive scaling & resiliency
+* v0.15: Performance: Per-allocation CPU load-balancing for UDP
+* v0.16: Management: Managed STUNner dataplane.
+* v0.17: First release candidate: All Gateway and STUNner APIs move to v1.
+* v0.18: Stabilization: Second release candidate.
+* v0.19: The missing pieces: Third release candidate.
+* v0.20: Final stabilization: Fourth stable release candidate
+* v0.21: Towards v1: Fifth stable release candidate
+* v1.0: STUNner goes GA!
## Help
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..6f97cd62
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,9 @@
+# Reporting Security Issues
+
+If you encounter any security issues, please get in touch with us in any of the following ways:
+- open a ticket at [GitHub Security Advisories](https://github.com/l7mp/stunner/security/advisories),
+- e-mail core developers at [info@l7mp.io](mailto:info@l7mp.io).
+
+## Learning More About Security
+
+To learn more about securing a STUNner deployment, please see the [security documentation](docs/SECURITY.md).
diff --git a/bin/.gitkeep b/bin/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/cmd/getstunner/getstunner.sh b/cmd/getstunner/getstunner.sh
new file mode 100755
index 00000000..bffbc12b
--- /dev/null
+++ b/cmd/getstunner/getstunner.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+# STUNner tools downloader script
+#
+# inspired by https://raw.githubusercontent.com/istio/istio/master/release/downloadIstioCtl.sh
+
+
+REPO=l7mp/stunner
+
+# Determine OS
+OS="${TARGET_OS:-$(uname)}"
+if [ "${OS}" = "Darwin" ] ; then
+ OSEXT="darwin"
+else
+ OSEXT="linux"
+fi
+
+# Determine the latest STUNner version
+if [ "${STUNNER_VERSION}" = "" ] ; then
+ STUNNER_VERSION="$(curl -Lsf https://api.github.com/repos/${REPO}/releases/latest \
+ | grep -o '"tag_name": "v[0-9]*.[0-9]*.[0-9]*' \
+ | awk -F'"' '{print $4}')"
+ STUNNER_VERSION="${STUNNER_VERSION##*/}"
+fi
+
+# Determine build params
+if [ "${STUNNER_VERSION}" = "" ] ; then
+ printf "Unable to get latest Stunner version. Set STUNNER_VERSION env var and re-run. For example: export STUNNER_VERSION=0.18.0"
+ exit 1;
+fi
+
+LOCAL_ARCH=$(uname -m)
+if [ "${TARGET_ARCH}" ]; then
+ LOCAL_ARCH=${TARGET_ARCH}
+fi
+
+case "${LOCAL_ARCH}" in
+ x86_64|amd64)
+ STUNNER_ARCH=amd64
+ ;;
+ armv8*|aarch64*|arm64)
+ STUNNER_ARCH=arm64
+ ;;
+ *)
+ echo "This system's architecture, ${LOCAL_ARCH}, isn't supported"
+ exit 1
+ ;;
+esac
+
+# Download binaries
+progs="stunnerctl turncat"
+tmp=$(mktemp -d /tmp/stunner.XXXXXX)
+
+for prog in $progs; do
+ NAME="${prog}-${STUNNER_VERSION}"
+ URL="https://github.com/${REPO}/releases/download/${STUNNER_VERSION}/${prog}-${STUNNER_VERSION}-${OSEXT}-${STUNNER_ARCH}"
+ filename="${prog}-${STUNNER_VERSION}-${OSEXT}-${STUNNER_ARCH}"
+
+ printf "\nDownloading %s from %s ...\n" "${NAME}" "$URL"
+ if ! curl -o /dev/null -sIf "$URL"; then
+ printf "\n%s is not found, please specify a valid STUNNER_VERSION and TARGET_ARCH\n" "$URL"
+ exit 1
+ fi
+ curl -fsL -o "${tmp}/${filename}" "$URL"
+ printf "%s download complete!\n" "${filename}"
+
+ mkdir -p "$HOME/.l7mp/bin"
+ mv "${tmp}/${filename}" "$HOME/.l7mp/bin/${prog}"
+ chmod +x "$HOME/.l7mp/bin/${prog}"
+done
+
+rm -r "${tmp}"
+
+# Print final message
+printf "\n"
+printf "Add stunner tools to your path with:"
+printf "\n"
+printf " export PATH=\$HOME/.l7mp/bin:\$PATH \n"
+printf "\n"
+printf "Need more information? Visit https://docs.l7mp.io/en/${STUNNER_VERSION}/ \n"
diff --git a/cmd/icetester/README.md b/cmd/icetester/README.md
new file mode 100644
index 00000000..a43eb208
--- /dev/null
+++ b/cmd/icetester/README.md
@@ -0,0 +1,42 @@
+# icetester: Universal UDP echo service using WebRTC/ICE
+
+`icetester` is test server that can be used WebRTC/ICE connectivity. The tester serves a simple
+WebSocket/JSON API server that clients can use to create a WebRTC data channel. whatever is
+received by `icetester` on the data channel will be echoed back to the client over the data channel.
+
+While `icetester` can be used as a standalone too, the intended use is via `stunnerctl icetest`.
+
+## Installation
+
+Install `icetester` using the standard Go toolchain and add it to `$PATH`.
+
+```console
+go install github.com/l7mp/stunner/cmd/icetester@latest
+```
+
+Building from source is as easy as it usually gets with Go:
+
+```console
+cd stunner
+go build -o turncat cmd/icetester/main.go
+```
+
+The containerized version is available as `docker.io/l7mp/icester`.
+
+## Usage
+
+Deploy a STUNner gateway and test is via UDP and TCP through `stunnerctl`:
+
+```console
+stunnerctl icetest
+```
+
+## License
+
+Copyright 2021-2024 by its authors. Some rights reserved. See [AUTHORS](../../AUTHORS).
+
+MIT License - see [LICENSE](../../LICENSE) for full text.
+
+## Acknowledgments
+
+Initial code adopted from [pion/stun](https://github.com/pion/stun) and [pion/turn](https://github.com/pion/turn).
diff --git a/cmd/icetester/icetester_test.go b/cmd/icetester/icetester_test.go
new file mode 100644
index 00000000..2cc53bc3
--- /dev/null
+++ b/cmd/icetester/icetester_test.go
@@ -0,0 +1,84 @@
+package main
+
+import (
+ "context"
+ "net"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ "github.com/l7mp/stunner/pkg/logger"
+ "github.com/l7mp/stunner/pkg/whipconn"
+)
+
+var (
+ testerLogLevel = "all:WARN"
+ // testerLogLevel = "all:TRACE"
+ // testerLogLevel = "all:INFO"
+ defaultConfig = whipconn.Config{BearerToken: "whiptoken"}
+)
+
+func echoTest(t *testing.T, conn net.Conn, content string) {
+ t.Helper()
+
+ n, err := conn.Write([]byte(content))
+ assert.NoError(t, err)
+ assert.Equal(t, len(content), n)
+
+ buf := make([]byte, 2048)
+ n, err = conn.Read(buf)
+ assert.NoError(t, err)
+ assert.Equal(t, content, string(buf[:n]))
+}
+
+var testerTestCases = []struct {
+ name string
+ config *whipconn.Config
+ tester func(t *testing.T, ctx context.Context)
+}{
+ {
+ name: "Basic connectivity",
+ tester: func(t *testing.T, ctx context.Context) {
+ log.Debug("Creating dialer")
+ d := whipconn.NewDialer(defaultConfig, loggerFactory)
+ assert.NotNil(t, d)
+
+ log.Debug("Dialing")
+ clientConn, err := d.DialContext(ctx, defaultICETesterAddr)
+ assert.NoError(t, err)
+
+ log.Debug("Echo test round 1")
+ echoTest(t, clientConn, "test1")
+ log.Debug("Echo test round 2")
+ echoTest(t, clientConn, "test2")
+
+ assert.NoError(t, clientConn.Close(), "client conn close")
+ },
+ },
+}
+
+func TestICETesterConn(t *testing.T) {
+ loggerFactory = logger.NewLoggerFactory(testerLogLevel)
+ log = loggerFactory.NewLogger("icester")
+
+ for _, c := range testerTestCases {
+ t.Run(c.name, func(t *testing.T) {
+ log.Infof("--------------------- %s ----------------------", c.name)
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ config := defaultConfig
+ if c.config != nil {
+ config = *c.config
+ }
+
+ log.Debug("Running listener loop")
+ go func() {
+ err := runICETesterListener(ctx, defaultICETesterAddr, config)
+ assert.NoError(t, err)
+ }()
+
+ c.tester(t, ctx)
+ })
+ }
+}
diff --git a/cmd/icetester/main.go b/cmd/icetester/main.go
new file mode 100644
index 00000000..a40b9c51
--- /dev/null
+++ b/cmd/icetester/main.go
@@ -0,0 +1,166 @@
+package main
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net"
+ "net/http"
+ "os"
+ "os/signal"
+
+ "github.com/pion/logging"
+ "github.com/pion/webrtc/v4"
+ flag "github.com/spf13/pflag"
+
+ v1 "github.com/l7mp/stunner/pkg/apis/v1"
+ "github.com/l7mp/stunner/pkg/buildinfo"
+ "github.com/l7mp/stunner/pkg/logger"
+ "github.com/l7mp/stunner/pkg/whipconn"
+)
+
+const (
+ // Name of the environment variable specifying the list of ICE servers, default is no ICE servers.
+ EnvVarNameICEServers = "ICE_SERVERS"
+
+ // Name of the environment variable specifying the ICE transport policy (either "relay" or "all"), default is "all".
+ EnvVarNameICETransportPolicy = "ICE_TRANSPORT_POLICY"
+
+ // HIP bearer token for authenticating WHIP requests, default is no bearer token.
+ EnvVarNameBearerToken = "BEARER_TOKEN"
+
+ // WHIP API endpoint, default is "/whip". Must include the leading slash ("/").
+ EnvVarNameWHIPEndpoint = "WHIP_ENDPOINT"
+)
+
+var (
+ version = "dev"
+ commitHash = "n/a"
+ buildDate = ""
+
+ defaultICEServers = []webrtc.ICEServer{}
+ defaultICETransportPolicy = webrtc.NewICETransportPolicy("all")
+ defaultBearerToken = ""
+ defaultWHIPEndpoint = "/whip"
+ defaultICETesterAddr = fmt.Sprintf(":%d", v1.DefaultICETesterPort)
+
+ loggerFactory logging.LoggerFactory
+ log logging.LeveledLogger
+)
+
+func main() {
+ os.Args[0] = "icetester"
+ var whipServerAddr = flag.StringP("addr", "a", defaultICETesterAddr, "WHIP server listener address")
+ var level = flag.StringP("log", "l", "all:WARN", "Log level")
+ var verbose = flag.BoolP("verbose", "v", false, "Enable verbose logging, identical to -l all:DEBUG")
+
+ flag.Parse()
+
+ if *verbose {
+ *level = "all:DEBUG"
+ }
+
+ loggerFactory = logger.NewLoggerFactory(*level)
+ log = loggerFactory.NewLogger("icester")
+
+ buildInfo := buildinfo.BuildInfo{Version: version, CommitHash: commitHash, BuildDate: buildDate}
+ log.Debugf("Starting icetester %s", buildInfo.String())
+
+ iceServers := defaultICEServers
+ if os.Getenv(EnvVarNameICEServers) != "" {
+ s := []webrtc.ICEServer{}
+ if err := json.Unmarshal([]byte(os.Getenv(EnvVarNameICEServers)), &s); err != nil {
+ log.Errorf("Environment ICE_SERVERS is invalid: %s", err.Error())
+ os.Exit(1)
+ }
+ iceServers = s
+ }
+
+ iceTransportPolicy := defaultICETransportPolicy
+ if os.Getenv(EnvVarNameICETransportPolicy) != "" {
+ iceTransportPolicy = webrtc.NewICETransportPolicy(os.Getenv(EnvVarNameICETransportPolicy))
+ }
+
+ token := defaultBearerToken
+ if os.Getenv(EnvVarNameBearerToken) != "" {
+ token = os.Getenv(EnvVarNameBearerToken)
+ }
+
+ whipEndpoint := defaultWHIPEndpoint
+ if os.Getenv(EnvVarNameWHIPEndpoint) != "" {
+ endpoint := os.Getenv(EnvVarNameWHIPEndpoint)
+ if endpoint[0] != '/' {
+ log.Errorf("Environment WHIP_ENDPOINT is invalid: %s, expecting a leading slash '/'", endpoint)
+ os.Exit(1)
+ }
+ whipEndpoint = endpoint
+ }
+
+ whipServerConfig := whipconn.Config{
+ ICEServers: iceServers,
+ ICETransportPolicy: iceTransportPolicy,
+ BearerToken: token,
+ WHIPEndpoint: whipEndpoint,
+ }
+
+ ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
+ defer stop()
+
+ if err := runICETesterListener(ctx, *whipServerAddr, whipServerConfig); err != nil {
+ log.Errorf("Could not create WHIP server listener: %s", err.Error())
+ os.Exit(1)
+ }
+
+ os.Exit(0)
+}
+
+func runICETesterListener(ctx context.Context, addr string, config whipconn.Config) error {
+ log.Infof("Creating WHIP server listener with config %#v", config)
+ l, err := whipconn.NewListener(addr, config, loggerFactory)
+ if err != nil {
+ return fmt.Errorf("Could not create WHIP server listener: %s", err.Error())
+ }
+
+ log.Debug("Creating echo service")
+ go func() {
+ for {
+ conn, err := l.Accept()
+ if err != nil {
+ return
+ }
+
+ log.Debugf("Accepting WHIP server connection with resource ID: %s",
+ conn.(*whipconn.ListenerConn).ResourceUrl)
+
+ // readloop
+ go func() {
+ buf := make([]byte, 100)
+ for {
+ n, err := conn.Read(buf)
+ if err != nil {
+ return
+ }
+
+ _, err = conn.Write(buf[:n])
+ if err != nil {
+ return
+ }
+ }
+ }()
+ }
+ }()
+
+ <-ctx.Done()
+
+ for _, conn := range l.GetConns() {
+ if err := conn.Close(); err != nil && !errors.Is(err, net.ErrClosed) &&
+ !errors.Is(err, http.ErrServerClosed) {
+ return fmt.Errorf("WHIP connection close error: %s", err.Error())
+ }
+ }
+
+ l.Close()
+
+ return nil
+}
diff --git a/cmd/stunnerctl/README.md b/cmd/stunnerctl/README.md
index 87ba4c19..02079af1 100644
--- a/cmd/stunnerctl/README.md
+++ b/cmd/stunnerctl/README.md
@@ -1,22 +1,214 @@
# stunnerctl: Command line toolbox for STUNner
A CLI tool to simplify the interaction with STUNner.
+The prominent use of `stunnerctl` is to load or watch STUNner dataplane configurations from a Kubernetes cluster for debugging and troubleshooting, or just for checking whether everything is configured the way it should be.
+
+## Installation
+
+Install the `stunnerctl` binary using the standard Go toolchain and add it to `$PATH`.
+
+```console
+go install github.com/l7mp/stunner/cmd/stunnerctl@latest
+```
+
+You can also enforce a specific OS, CPU architecture, and STUNner version:
+
+```console
+GOOS=windows GOARCH=amd64 go install github.com/l7mp/stunner/cmd/stunnerctl@v0.17.5
+```
+
+Building from source is as easy as it usually gets with Go:
+
+```console
+cd stunner
+go build -o stunnerctl cmd/stunnerctl/main.go
+```
## Usage
-Dump the running config from a live STUNner deployment in human-readable format.
+Type `stunnerctl` to get a glimpse of the sub-commands and features provided.
+
+### Config
+
+The `config` sub-command is used to load or watch running dataplane configs from the STUNner config discovery service (CDS) running in a remote Kubernetes cluster. Usually the CDS server role is fulfilled by the [STUNner gateway operator](https://github.com/l7mp/stunner-gateway-operator) but you can choose any CDS service you want (see the `--cds-server-*` CLI flags in the help). The main use of this command is to check the active dataplane configuration for troubleshooting connectivity problems.
+
+- Dump a summary of the running config of the STUNner gateway called `udp-gateway` deployed into the `stunner` namespace:
+
+ ```console
+ stunnerctl -n stunner config udp-gateway
+ Gateway: stunner/udp-gateway (loglevel: "all:INFO")
+ Authentication type: static, username/password: user-1/pass-1
+ Listeners:
+ - Name: stunner/udp-gateway/udp-listener
+ Protocol: TURN-UDP
+ Public address:port: 34.118.88.91:9001
+ Routes: [stunner/iperf-server]
+ Endpoints: [10.76.1.3, 10.80.7.104]
+ ```
+
+- The same, but using the alternative Kubernetes config file `~/my-config.conf` to access the cluster. The rest of the usual `kubectl` flags (`--context`, `--token`, etc.) are also available to select the cluster to connect to.
+
+ ``` console
+ stunnerctl --kubeconfig ~/my-config.conf -n stunner config udp-gateway
+ ```
+
+- Dump the running config of all gateways in the `stunner` namespace in JSON format (YAML is also available using `-o yaml`):
+
+ ```console
+ stunnerctl -n stunner config -o json
+ {"version":"v1","admin":{"name":"stunner/tcp-gateway",...}}
+ {"version":"v1","admin":{"name":"stunner/udp-gateway",...}}}
+ ```
+
+- Watch STUNner configs as they are being refreshed by the operator and dump only the name of the gateway whose config changes:
+
+ ```console
+ stunnerctl config --all-namespaces -o jsonpath='{.admin.name}' -w
+ stunner/tcp-gateway
+ stunner/udp-gateway
+ ...
+ ```
+
+For those who don't have the Go toolchain available to run `go install`, STUNner provides a minimalistic `stunnerctl` replacement called `stunnerctl.sh`.
+This script requires nothing else than `bash`, `kubectl`, `curl` and `jq` to work.
+
+The below will dump the running config of `tcp-gateway` deployed into the `stunner` namespace:
```console
-cmd/stunnerctl/stunnerctl running-config stunner/stunnerd-config
-STUN/TURN authentication type: plaintext
-STUN/TURN username: user-1
-STUN/TURN password: pass-1
-Listener: udp-listener
-Protocol: UDP
-Public address: 34.118.36.108
-Public port: 3478
+cd stunner
+cmd/stunnerctl/stunnerctl.sh running-config stunner/tcp-gateway
+STUN/TURN authentication type: static
+STUN/TURN username: user-1
+STUN/TURN password: pass-1
+Listener 1
+ Name: stunner/tcp-gateway/tcp-listener
+ Listener: stunner/tcp-gateway/tcp-listener
+ Protocol: TURN-TCP
+ Public address: 35.187.97.94
+ Public port: 3478
+```
+
+You can also use `kubectl port-forward` to load or watch STUNner configs manually. Open a port-forwarded connection to the STUNner gateway operator:
+
+``` console
+export CDS_SERVER_NAME=$(kubectl get pods -l stunner.l7mp.io/config-discovery-service=enabled --all-namespaces -o jsonpath='{.items[0].metadata.name}')
+export CDS_SERVER_NAMESPACE=$(kubectl get pods -l stunner.l7mp.io/config-discovery-service=enabled --all-namespaces -o jsonpath='{.items[0].metadata.namespace}')
+kubectl -n $CDS_SERVER_NAMESPACE port-forward pod/${CDS_SERVER_NAME} 63478:13478 &
+```
+
+If all goes well, you can now connect to the STUNner CDS API served by the gateway operator through the port-forwarded tunnel opened by `kubectl` just using `curl`. The below will load the config of the `udp-gateway` in the `stunner` namespace:
+
+``` console
+curl -s http://127.0.0.1:63478/api/v1/configs/stunner/udp-gateway
+```
+
+If you happen to have a WebSocket client like the wonderful [`websocat`](https://github.com/vi/websocat) tool installed, you can also watch the configs as they are being rendered by the operator en live.
+
+``` console
+websocat ws://127.0.0.1:63478/api/v1/configs/stunner/udp-gateway?watch=true -
```
+### Status
+
+The `status` sub-command reports the status of the dataplane pods for a gateway, especially the runtime state of the `stunnerd` daemon.
+
+- Find all dataplane pods for the `udp-gateway` in the `stunner` namespace and dump a status summary:
+
+ ``` console
+ stunnerctl -n stunner status udp-gateway
+ stunner/udp-gateway-856c9f4dc9-524hc:
+ stunner/udp-gateway:{logLevel="all:INFO",health-check="http://:8086"}
+ static-auth:{realm="stunner.l7mp.io",username="",password=""}
+ listeners:1/clusters:1
+ allocs:3/status=READY
+ stunner/udp-gateway-856c9f4dc9-c7wcq:
+ stunner/udp-gateway:{logLevel="all:INFO",health-check="http://:8086"}
+ static-auth:{realm="stunner.l7mp.io",username="",password=""}
+ listeners:1/clusters:1
+ allocs:2/status=READY
+ ```
+
+- Same but report only the runtime status of the `stunnerd` pods in the `stunner` namespace:
+
+ ``` console
+ stunnerctl -n stunner status -o jsonpath='{.status}'
+ READY
+ TERMINATING
+ ```
+
+### Authentication
+
+The `auth` sub-command can be used to obtain a TURN credential or a full ICE server config for connecting to a specific gateway. The authentication service API is usually served by a separate [STUNner authentication server](https://github.com/l7mp/stunner-auth-service) deployed alongside the gateway operator. The main use of this command is to feed an ICE agent manually with the ICE server config to connect to a specific STUNner gateway.
+
+- Obtain a full ICE server config for `udp-gateway` deployed into the `stunner` namespace:
+
+ ``` console
+ stunnerctl -n stunner auth udp-gateway
+ {"iceServers":[{"credential":"pass-1","urls":["turn:10.104.19.179:3478?transport=udp"],"username":"user-1"}],"iceTransportPolicy":"all"}
+ ```
+
+- Request a plain [TURN credential](https://datatracker.ietf.org/doc/html/draft-uberti-behave-turn-rest-00) using the authentication service deployed into the `stunner-system-prod` namespace:
+
+ ``` console
+ stunnerctl -n stunner auth udp-gateway --auth-turn-credential --auth-service-namespace=stunner-system-prod
+ {"password":"pass-1","ttl":86400,"uris":["turn:10.104.19.179:3478?transport=udp"],"username":"user-1"}
+ ```
+
+### ICE test
+
+The `icetest` sub-command can be used to run a full-blown ICE test. This command is intended for
+users to check a STUNner installation and pinpoint installation errors.
+
+The tester will fire up a WHIP server in the cluster, configure a UDP and a TCP gateway to expose
+it, makes a PeerConnection to the WHIP server via the gateways, and performs a quick test by
+sending a set of packets via a data channel created over the PeerConnection and measures loss and
+latency using the packets echoed back by the WHIP server. If successful, the tester will output the
+measured statistics, otherwise it reports the error that stopped the ICE test and provides some
+diagnostics that to help troubleshooting.
+
+- Run a dataplane test over UDP and TCP:
+
+ ``` console
+ stunnerctl icetest
+ Initializing... completed
+ Checking installation... completed
+ Checking Gateway... completed
+ Obtaining ICE server configuration... completed
+ Running asymmetric ICE test over TURN-UDP... completed
+ Statistics: rate=48.65pps, loss=0/973pkts=0.00%, RTT:mean=20.67ms/median=20.54ms/P95=22.23ms/P99=23.34ms
+ LocalICECandidates:
+ * udp4 relay 10.244.0.24:43988 related 0.0.0.0:43716 (resolved: 10.244.0.24:43988)
+ RemoteICECandidates:
+ * udp4 host 10.244.0.163:35242 (resolved: 10.244.0.163:35242)
+ Running asymmetric ICE test over TURN-TCP... completed
+ Statistics: rate=48.55pps, loss=0/971pkts=0.00%, RTT:mean=21.00ms/median=20.89ms/P95=22.45ms/P99=23.47ms
+ LocalICECandidates:
+ * udp4 relay 10.244.0.162:45090 related 0.0.0.0:45654 (resolved: 10.244.0.162:45090)
+ RemoteICECandidates:
+ * udp4 host 10.244.0.163:51653 (resolved: 10.244.0.163:51653)
+ Running symmetric ICE test over TURN-UDP... completed
+ Statistics: rate=48.65pps, loss=0/973pkts=0.00%, RTT:mean=20.63ms/median=20.47ms/P95=21.85ms/P99=23.01ms
+ LocalICECandidates:
+ * udp4 relay 10.244.0.24:47282 related 0.0.0.0:55122 (resolved: 10.244.0.24:47282)
+ RemoteICECandidates:
+ * udp4 relay 10.244.0.24:47367 related 0.0.0.0:51777 (resolved: 10.244.0.24:47367)
+ Running symmetric ICE test over TURN-TCP... completed
+ Statistics: rate=48.60pps, loss=0/972pkts=0.00%, RTT:mean=24.61ms/median=20.56ms/P95=39.96ms/P99=133.40ms
+ LocalICECandidates:
+ * udp4 relay 10.244.0.162:56555 related 0.0.0.0:42600 (resolved: 10.244.0.162:56555)
+ RemoteICECandidates:
+ * udp4 relay 10.244.0.162:33397 related 10.244.0.163:53124 (resolved: 10.244.0.162:33397)
+ ```
+
+- Clean up the Kubernetes resources the tester might have left behind on a previous run and perform
+ the test only on TURN-UDP with at a rate of 100 packets per second using a 2 minute timeout:
+
+ ``` console
+ stunnerctl icetest --force-cleanup -packet-rate 100 --timeout 2m udp
+ ```
+
+Run `stunnerctl icetest --help` for further useful command line arguments.
+
## License
Copyright 2021-2023 by its authors. Some rights reserved. See [AUTHORS](../../AUTHORS).
diff --git a/cmd/stunnerctl/auth.go b/cmd/stunnerctl/auth.go
new file mode 100644
index 00000000..61efc6fa
--- /dev/null
+++ b/cmd/stunnerctl/auth.go
@@ -0,0 +1,67 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+
+ "github.com/spf13/cobra"
+
+ cdsclient "github.com/l7mp/stunner/pkg/config/client"
+)
+
+func runAuth(_ *cobra.Command, args []string) error {
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ log.Debug("Searching for authentication server")
+ pod, err := cdsclient.DiscoverK8sAuthServer(ctx, k8sConfigFlags, authConfigFlags,
+ loggerFactory.NewLogger("auth-fwd"))
+ if err != nil {
+ return fmt.Errorf("error searching for auth service: %w", err)
+ }
+
+ u := url.URL{
+ Scheme: "http",
+ Host: pod.Addr,
+ Path: "/ice",
+ }
+ q := u.Query()
+ q.Set("service", "turn")
+ u.RawQuery = q.Encode()
+
+ if authConfigFlags.TurnAuth {
+ // enforce TURN credential format
+ u.Path = ""
+ }
+
+ if k8sConfigFlags.Namespace != nil && *k8sConfigFlags.Namespace != "" {
+ q := u.Query()
+ q.Set("namespace", *k8sConfigFlags.Namespace)
+ if len(args) > 0 {
+ q.Set("gateway", args[0])
+ }
+ u.RawQuery = q.Encode()
+ }
+
+ log.Debugf("Querying to authentication server %s using URL %q", pod.String(), u.String())
+ res, err := http.Get(u.String())
+ if err != nil {
+ return fmt.Errorf("error querying auth service %s: %w", pod.String(), err)
+ }
+ if res.StatusCode != http.StatusOK {
+ return fmt.Errorf("HTTP error querying auth service %s: expected status %d, got %d",
+ pod.String(), http.StatusOK, res.StatusCode)
+ }
+
+ b, err := io.ReadAll(res.Body)
+ if err != nil {
+ return fmt.Errorf("cannot read HTTP response: %w", err)
+ }
+
+ fmt.Println(string(b))
+
+ return nil
+}
diff --git a/cmd/stunnerctl/config.go b/cmd/stunnerctl/config.go
new file mode 100644
index 00000000..a77da115
--- /dev/null
+++ b/cmd/stunnerctl/config.go
@@ -0,0 +1,123 @@
+package main
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "os"
+ "os/signal"
+ "syscall"
+
+ "github.com/spf13/cobra"
+ "sigs.k8s.io/yaml"
+
+ stnrv1 "github.com/l7mp/stunner/pkg/apis/v1"
+ cdsclient "github.com/l7mp/stunner/pkg/config/client"
+)
+
+func runConfig(_ *cobra.Command, args []string) error {
+ gwNs := "default"
+ if k8sConfigFlags.Namespace != nil && *k8sConfigFlags.Namespace != "" {
+ gwNs = *k8sConfigFlags.Namespace
+ }
+
+ jsonQuery, output, err := ParseJSONPathFlag(output)
+ if err != nil {
+ return err
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ log.Debug("Searching for CDS server")
+ pod, err := cdsclient.DiscoverK8sCDSServer(ctx, k8sConfigFlags, cdsConfigFlags,
+ loggerFactory.NewLogger("cds-fwd"))
+ if err != nil {
+ return fmt.Errorf("error searching for CDS server: %w", err)
+ }
+
+ log.Debugf("Connecting to CDS server: %s", pod.String())
+ var cds cdsclient.CdsApi
+ cdslog := loggerFactory.NewLogger("cds-client")
+ if all {
+ cds, err = cdsclient.NewAllConfigsAPI(pod.Addr, cdslog)
+ } else if len(args) == 0 {
+ cds, err = cdsclient.NewConfigsNamespaceAPI(pod.Addr, gwNs, cdslog)
+ } else {
+ gwName := args[0]
+ cds, err = cdsclient.NewConfigNamespaceNameAPI(pod.Addr, gwNs, gwName, cdslog)
+ }
+
+ if err != nil {
+ return fmt.Errorf("error creating CDS client: %w", err)
+ }
+
+ confChan := make(chan *stnrv1.StunnerConfig, 8)
+ if watch {
+ err := cds.Watch(ctx, confChan, false)
+ if err != nil {
+ close(confChan)
+ return err
+ }
+
+ go func() {
+ sigs := make(chan os.Signal, 1)
+ signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
+ <-sigs
+ close(confChan)
+ }()
+ } else {
+ resp, err := cds.Get(ctx)
+ if err != nil {
+ close(confChan)
+ return err
+ }
+ for _, c := range resp {
+ confChan <- c
+ }
+
+ close(confChan)
+ }
+
+ for c := range confChan {
+ if cdsclient.IsConfigDeleted(c) {
+ fmt.Printf("Gateway: %s \n", c.Admin.Name)
+ continue
+ }
+ switch output {
+ case "yaml":
+ if out, err := yaml.Marshal(c); err != nil {
+ return err
+ } else {
+ fmt.Println(string(out))
+ }
+ case "json":
+ if out, err := json.Marshal(c); err != nil {
+ return err
+ } else {
+ fmt.Println(string(out))
+ }
+ case "jsonpath":
+ values, err := jsonQuery.FindResults(c)
+ if err != nil {
+ return err
+ }
+
+ if len(values) == 0 || len(values[0]) == 0 {
+ fmt.Println("")
+ }
+
+ for arrIx := range values {
+ for valIx := range values[arrIx] {
+ fmt.Printf("%v\n", values[arrIx][valIx].Interface())
+ }
+ }
+ case "summary":
+ fmt.Print(c.Summary())
+ default:
+ fmt.Println(c.String())
+ }
+ }
+
+ return nil
+}
diff --git a/cmd/stunnerctl/icetest.go b/cmd/stunnerctl/icetest.go
new file mode 100644
index 00000000..a2d81efe
--- /dev/null
+++ b/cmd/stunnerctl/icetest.go
@@ -0,0 +1,187 @@
+package main
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "io"
+ "os"
+ "os/signal"
+ "sync"
+ "time"
+
+ "github.com/spf13/cobra"
+
+ "github.com/l7mp/stunner/internal/icetester"
+ v1 "github.com/l7mp/stunner/pkg/apis/v1"
+ "github.com/l7mp/stunner/pkg/logger"
+)
+
+const DefaultTestNamespace = "icetest"
+
+func runICETest(_ *cobra.Command, args []string) error {
+ ns := DefaultTestNamespace
+ if k8sConfigFlags.Namespace != nil && *k8sConfigFlags.Namespace != "" {
+ ns = *k8sConfigFlags.Namespace
+ }
+
+ turnTransports := []v1.ListenerProtocol{}
+ protos := args
+ if len(protos) == 0 {
+ // run all tests for all transports if no specific transport is provided
+ protos = []string{"udp", "tcp"}
+ }
+ for _, arg := range protos {
+ proto, err := v1.NewListenerProtocol(arg)
+ if err != nil {
+ return err
+ }
+ switch proto {
+ case v1.ListenerProtocolUDP:
+ turnTransports = append(turnTransports, v1.ListenerProtocolTURNUDP)
+ case v1.ListenerProtocolTCP:
+ turnTransports = append(turnTransports, v1.ListenerProtocolTURNTCP)
+ case v1.ListenerProtocolTURNUDP, v1.ListenerProtocolTURNTCP:
+ turnTransports = append(turnTransports, proto)
+ default:
+ return fmt.Errorf("ICE test is currently not available on TURN transport protocol %s", proto)
+ }
+ }
+
+ // Create a buffered logger for the tests
+ logBuffer := &bytes.Buffer{}
+ bufferedLoggerFactory := logger.NewLoggerFactory("all:TRACE") // hardcode highest loglevel
+ bufferedLoggerFactory.SetWriter(logBuffer)
+
+ eventCh := make(chan icetester.Event, 12)
+ defer close(eventCh)
+ tester, err := icetester.NewICETester(icetester.Config{
+ EventChannel: eventCh,
+
+ K8sConfigFlags: k8sConfigFlags,
+ CDSConfigFlags: cdsConfigFlags,
+ AuthConfigFlags: authConfigFlags,
+
+ Namespace: ns,
+ TURNTransports: turnTransports,
+ ICETesterImage: iceTesterImage,
+ ForceCleanup: forceCleanup,
+ PacketRate: iceTesterPacketRate,
+
+ Logger: bufferedLoggerFactory,
+ })
+ if err != nil {
+ return fmt.Errorf("Failed to create ICE tester: %w", err)
+ }
+
+ // run for at most 5 minutes
+ ctx, cancel := context.WithTimeout(context.Background(), iceTesterTimeout)
+
+ // stop on interrupt as well
+ ctx, stop := signal.NotifyContext(ctx, os.Interrupt)
+ defer stop()
+
+ // event handler
+ var wg sync.WaitGroup
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for {
+ select {
+ case e := <-eventCh:
+ printEvent(e, logBuffer)
+
+ case <-ctx.Done():
+ return
+ }
+ }
+ }()
+
+ err = tester.Start(ctx)
+ if err == nil {
+ err = ctx.Err()
+ }
+
+ if err != nil {
+ switch ctx.Err() {
+ case context.DeadlineExceeded:
+ fmt.Printf("\nICE tester timed out after %s\n", iceTesterTimeout)
+ printLogs(logBuffer)
+ case context.Canceled:
+ fmt.Printf("\nICE tester stopped due to user interrupt\n")
+ default:
+ fmt.Printf("\nICE tester error: %s\n", err.Error())
+ }
+ }
+
+ cancel()
+
+ // wait until the printer finishes
+ wg.Wait()
+
+ return nil
+}
+
+func printEvent(e icetester.Event, logbuf io.ReadWriter) {
+ // started
+ if e.InProgress {
+ proto := ""
+ if arg, ok := e.Args["ICETransport"]; ok {
+ if p, ok := arg.(string); ok {
+ proto = fmt.Sprintf(" over %s", p)
+ }
+ }
+
+ fmt.Printf("%s: %s%s... ", e.Timestamp.Format(time.RFC822), e.Type.String(), proto)
+ return
+ }
+
+ // completed successfully
+ if e.Error == nil {
+ fmt.Println("completed")
+ if arg, ok := e.Args["Stats"]; ok {
+ if s, ok := arg.(*icetester.Stats); ok {
+ fmt.Printf("\tStatistics: rate=%0.2fpps, loss=%d/%dpkts=%0.2f%%, "+
+ "RTT:mean=%0.2fms/median=%0.2fms/P95=%0.2fms/P99=%0.2fms\n",
+ s.SendRate, s.PacketsSent-s.PacketsReceived, s.PacketsSent,
+ s.LossRate, s.MeanLatency, s.MedianLatency, s.P95Latency,
+ s.P99Latency)
+ }
+ }
+
+ for _, ctype := range []string{"LocalICECandidates", "RemoteICECandidates"} {
+ if arg, ok := e.Args[ctype]; ok {
+ if cs, ok := arg.([]icetester.CandidateDesc); ok {
+ fmt.Printf("\t%s:\n", ctype)
+ for _, c := range cs {
+ sel := " "
+ if c.Selected {
+ sel = "* "
+ }
+ fmt.Printf("\t %s%s\n", sel, c.Candidate)
+ }
+ }
+ }
+ }
+
+ return
+ }
+
+ // completed with error
+ fmt.Printf("error\nError: %q\nTimeStamp: %s\n", e.Error.Error(),
+ e.Timestamp.Format(time.DateTime))
+ if e.Diagnostics != "" {
+ fmt.Printf("Diagnostics: %s\n", e.Diagnostics)
+ }
+ printLogs(logbuf)
+}
+
+func printLogs(logbuf io.ReadWriter) {
+ logs, err := io.ReadAll(logbuf)
+ if err != nil {
+ fmt.Printf("Logs not available due to error when reading log buffer: %s", err.Error())
+ } else if len(logs) != 0 {
+ fmt.Println("Detailed logs")
+ fmt.Print(string(logs))
+ }
+}
diff --git a/cmd/stunnerctl/main.go b/cmd/stunnerctl/main.go
new file mode 100644
index 00000000..15632b0e
--- /dev/null
+++ b/cmd/stunnerctl/main.go
@@ -0,0 +1,206 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "regexp"
+ "strings"
+ "time"
+
+ "github.com/pion/logging"
+ "github.com/spf13/cobra"
+ cliopt "k8s.io/cli-runtime/pkg/genericclioptions"
+ "k8s.io/client-go/util/jsonpath"
+
+ "github.com/l7mp/stunner/internal/icetester"
+ cdsclient "github.com/l7mp/stunner/pkg/config/client"
+ "github.com/l7mp/stunner/pkg/logger"
+)
+
+// list all configs: stunnerctl get config --all-namespaces
+// watch all configs in namesapce stunner: stunnerctl -n stunner get config --watch
+// get short-form config for stunner/udp-gateway: stunnerctl -n stunner get config udp-gateway
+// get config for stunner/udp-gateway in yaml format: stunnerctl -n stunner get config udp-gateway --output yaml
+
+var (
+ output, iceTesterImage, loglevel string
+ watch, all, verbose, forceCleanup bool
+ k8sConfigFlags *cliopt.ConfigFlags
+ cdsConfigFlags *cdsclient.CDSConfigFlags
+ authConfigFlags *cdsclient.AuthConfigFlags
+ podConfigFlags *cdsclient.PodConfigFlags
+ iceTesterTimeout time.Duration
+ iceTesterPacketRate int
+
+ loggerFactory logger.LoggerFactory
+ log logging.LeveledLogger
+
+ rootCmd = &cobra.Command{
+ Use: "stunnerctl",
+ Short: "A command line utility to inspect STUNner dataplane .",
+ Long: "The stunnerctl tool is a CLI for inspecting, watching and troublehssooting STUNner gateways",
+ DisableAutoGenTag: true,
+ PersistentPreRun: func(_ *cobra.Command, _ []string) {
+ if verbose {
+ loglevel = "all:TRACE"
+ }
+
+ loggerFactory = logger.NewLoggerFactory(loglevel)
+ log = loggerFactory.NewLogger("stunnerctl")
+ },
+ }
+)
+
+var (
+ configCmd = &cobra.Command{
+ Use: "config",
+ Aliases: []string{"stunner-config"},
+ Short: "Get or watch dataplane configs of a gateway",
+ Args: cobra.RangeArgs(0, 1),
+ DisableAutoGenTag: true,
+ Run: func(cmd *cobra.Command, args []string) {
+ if err := runConfig(cmd, args); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+ },
+ }
+ statusCmd = &cobra.Command{
+ Use: "status [gateway]",
+ Aliases: []string{"dataplane-status"},
+ Short: "Read status from dataplane pods for a gateway",
+ Args: cobra.RangeArgs(0, 1),
+ DisableAutoGenTag: true,
+ Run: func(cmd *cobra.Command, args []string) {
+ if err := runStatus(cmd, args); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+ },
+ }
+ authCmd = &cobra.Command{
+ Use: "auth",
+ Aliases: []string{"get-credential"},
+ Short: "Obtain authenticaction credentials for a gateway",
+ Args: cobra.RangeArgs(0, 1),
+ DisableAutoGenTag: true,
+ Run: func(cmd *cobra.Command, args []string) {
+ if err := runAuth(cmd, args); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+ },
+ }
+ iceTestCmd = &cobra.Command{
+ Use: "icetest [udp, tcp, ...]",
+ Short: "Test ICE connectivity with the specified transports",
+ DisableAutoGenTag: true,
+ Run: func(cmd *cobra.Command, args []string) {
+ if err := runICETest(cmd, args); err != nil {
+ fmt.Println(err)
+ os.Exit(1)
+ }
+ },
+ }
+)
+
+func init() {
+ rootCmd.PersistentFlags().BoolVarP(&all, "all-namespaces", "a", false, "Consider all namespaces")
+ rootCmd.PersistentFlags().StringVarP(&output, "output", "o", "summary", "Output format, either json, yaml, summary or jsonpath=template")
+ rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable verbose logging, identical to -l all:DEBUG (overrides -l)")
+ rootCmd.PersistentFlags().StringVarP(&loglevel, "loglevel", "l", "all:WARN", "Set loglevel (format: :, overrides: PION_LOG_*, default: all:WARN)")
+
+ // Kubernetes config flags: persistent, all commands
+ k8sConfigFlags = cliopt.NewConfigFlags(true)
+ k8sConfigFlags.AddFlags(rootCmd.PersistentFlags())
+
+ // CDS server discovery flags: only for "config" command
+ cdsConfigFlags = cdsclient.NewCDSConfigFlags()
+ cdsConfigFlags.AddFlags(configCmd.Flags())
+
+ // watch flag: only for config
+ configCmd.Flags().BoolVarP(&watch, "watch", "w", false, "Watch for config updates from server")
+
+ // Pod discovery flags: only for "status" command
+ podConfigFlags = cdsclient.NewPodConfigFlags()
+ podConfigFlags.AddFlags(statusCmd.Flags())
+
+ // Auth discovery flags: only for "auth" command
+ authConfigFlags = cdsclient.NewAuthConfigFlags()
+ authConfigFlags.AddFlags(authCmd.Flags())
+
+ // ICE test: uses CDS and auth args
+ cdsConfigFlags.AddFlags(iceTestCmd.Flags())
+ authConfigFlags.AddFlags(iceTestCmd.Flags())
+
+ // ICE test timeout
+ iceTestCmd.Flags().IntVarP(&iceTesterPacketRate, "packet-rate", "r", 50,
+ "Packet rate [pkts/sec], 0 means flood test (Default: 50)")
+ iceTestCmd.Flags().DurationVarP(&iceTesterTimeout, "timeout", "t", icetester.DefaultICETesterTimeout,
+ "Timeout")
+ iceTestCmd.Flags().StringVar(&iceTesterImage, "ice-tester-image", icetester.DefaultICETesterImage,
+ "Default icetester container image")
+ iceTestCmd.Flags().BoolVar(&forceCleanup, "force-cleanup", false, "Remove tester namespace if it exists")
+
+ // Add commands
+ rootCmd.AddCommand(configCmd)
+ rootCmd.AddCommand(statusCmd)
+ rootCmd.AddCommand(authCmd)
+ rootCmd.AddCommand(iceTestCmd)
+}
+
+func main() {
+ if err := rootCmd.Execute(); err != nil {
+ fmt.Fprintf(os.Stderr, "Whoops. There was an error while executing your CLI '%s'", err)
+ os.Exit(1)
+ }
+}
+
+// ////////////////////////
+var jsonRegexp = regexp.MustCompile(`^\{\.?([^{}]+)\}$|^\.?([^{}]+)$`)
+
+// k8s.io/kubectl/pkg/cmd/get
+func RelaxedJSONPathExpression(pathExpression string) (string, error) {
+ if len(pathExpression) == 0 {
+ return pathExpression, nil
+ }
+ submatches := jsonRegexp.FindStringSubmatch(pathExpression)
+ if submatches == nil {
+ return "", fmt.Errorf("unexpected path string, expected a 'name1.name2' or '.name1.name2' or '{name1.name2}' or '{.name1.name2}'")
+ }
+ if len(submatches) != 3 {
+ return "", fmt.Errorf("unexpected submatch list: %v", submatches)
+ }
+ var fieldSpec string
+ if len(submatches[1]) != 0 {
+ fieldSpec = submatches[1]
+ } else {
+ fieldSpec = submatches[2]
+ }
+ return fmt.Sprintf("{.%s}", fieldSpec), nil
+}
+
+func ParseJSONPathFlag(output string) (*jsonpath.JSONPath, string, error) {
+ if !strings.HasPrefix(output, "jsonpath") {
+ return nil, output, nil
+ }
+
+ as := strings.Split(output, "=")
+ if len(as) != 2 || as[0] != "jsonpath" {
+ return nil, output, fmt.Errorf("invalid jsonpath output definition %q", output)
+ }
+
+ jsonQuery := jsonpath.New("output")
+
+ // Parse and print jsonpath
+ fields, err := RelaxedJSONPathExpression(as[1])
+ if err != nil {
+ return nil, output, fmt.Errorf("invalid jsonpath query %w", err)
+ }
+
+ if err := jsonQuery.Parse(fields); err != nil {
+ return nil, output, fmt.Errorf("cannor parse jsonpath query %w", err)
+ }
+
+ return jsonQuery, "jsonpath", nil
+}
diff --git a/cmd/stunnerctl/status.go b/cmd/stunnerctl/status.go
new file mode 100644
index 00000000..ce4adc78
--- /dev/null
+++ b/cmd/stunnerctl/status.go
@@ -0,0 +1,118 @@
+package main
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "time"
+
+ "github.com/spf13/cobra"
+ "sigs.k8s.io/yaml"
+
+ v1 "github.com/l7mp/stunner/pkg/apis/v1"
+ cdsclient "github.com/l7mp/stunner/pkg/config/client"
+)
+
+func runStatus(_ *cobra.Command, args []string) error {
+ jsonQuery, output, err := ParseJSONPathFlag(output)
+ if err != nil {
+ return err
+ }
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ gwNs := "default"
+ extraLog := "in namespace default"
+ if k8sConfigFlags.Namespace != nil && *k8sConfigFlags.Namespace != "" {
+ gwNs = *k8sConfigFlags.Namespace
+ extraLog = fmt.Sprintf("in namespace %s", gwNs)
+ }
+ // --all-namespaces overrides -n
+ if all {
+ gwNs = ""
+ extraLog = "in all namespaces"
+ }
+
+ gw := ""
+ if len(args) > 0 {
+ gw = args[0]
+ }
+ if gwNs != "" && gw != "" {
+ extraLog += fmt.Sprintf("for gateway %s", gw)
+ }
+
+ log.Debug("Searching for dataplane pods " + extraLog)
+ pods, err := cdsclient.DiscoverK8sStunnerdPods(ctx, k8sConfigFlags, podConfigFlags,
+ gwNs, gw, loggerFactory.NewLogger("stunnerd-fwd"))
+ if err != nil {
+ return fmt.Errorf("error searching for stunnerd pods: %w", err)
+ }
+
+ for _, pod := range pods {
+ client := http.Client{
+ Timeout: 5 * time.Second,
+ }
+ url := fmt.Sprintf("http://%s/status", pod.Addr)
+ res, err := client.Get(url)
+ if err != nil {
+ log.Errorf("Error querying status for stunnerd pod at URL %q on %s: %s",
+ url, pod.String(), err.Error())
+ continue
+ }
+
+ if res.StatusCode != http.StatusOK {
+ log.Errorf("Status query failed on %s with HTTP error code %s",
+ pod.String(), res.Status)
+ continue
+ }
+
+ s := v1.StunnerStatus{}
+ err = json.NewDecoder(res.Body).Decode(&s)
+ if err != nil {
+ log.Errorf("Could not decode status response: %s", err.Error())
+ continue
+ }
+
+ switch output {
+ case "yaml":
+ if out, err := yaml.Marshal(s); err != nil {
+ return err
+ } else {
+ fmt.Println(string(out))
+ }
+ case "json":
+ if out, err := json.Marshal(s); err != nil {
+ return err
+ } else {
+ fmt.Println(string(out))
+ }
+ case "jsonpath":
+ values, err := jsonQuery.FindResults(s)
+ if err != nil {
+ return err
+ }
+
+ if len(values) == 0 || len(values[0]) == 0 {
+ fmt.Println("")
+ }
+
+ for arrIx := range values {
+ for valIx := range values[arrIx] {
+ fmt.Printf("%v\n", values[arrIx][valIx].Interface())
+ }
+ }
+ case "summary":
+ fallthrough
+ default:
+ if pod.Proxy {
+ fmt.Printf("%s/%s:\n\t%s\n", pod.Namespace, pod.Name, s.Summary())
+ } else {
+ fmt.Printf("%s:\n\t%s\n", pod.Addr, s.Summary())
+ }
+ }
+ }
+
+ return nil
+}
diff --git a/cmd/stunnerctl/stunnerctl b/cmd/stunnerctl/stunnerctl.sh
similarity index 73%
rename from cmd/stunnerctl/stunnerctl
rename to cmd/stunnerctl/stunnerctl.sh
index a9b1b88c..fa7272fb 100755
--- a/cmd/stunnerctl/stunnerctl
+++ b/cmd/stunnerctl/stunnerctl.sh
@@ -6,6 +6,9 @@ USAGE="stunnerctl running-config "
COMMAND="$1"
ARG="$2"
+# stop the port-forwarder
+trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
+
jq=$(which jq)
if [ -z "$jq" ] ; then
echo "Error: cannot find jq in PATH" && exit 0
@@ -19,12 +22,24 @@ running_config () {
name=${args[1]}
[ -z $namespace -o -z $name ] && echo "cannot parse argument" && exit 0
- [ $(kubectl get cm -n $namespace -o json| jq ".items | map(select(.metadata.name==\"${name}\"))|length") -eq 0 ] && \
- echo "STUNner configmap ${namespace}/${name} not found" && exit 1
+ # find the CDS server
+ CDS_SERVER_NAME=$(kubectl get pods -l stunner.l7mp.io/config-discovery-service=enabled --all-namespaces -o jsonpath='{.items[0].metadata.name}')
+ CDS_SERVER_NAMESPACE=$(kubectl get pods -l stunner.l7mp.io/config-discovery-service=enabled --all-namespaces -o jsonpath='{.items[0].metadata.namespace}')
+ [ -z $CDS_SERVER_NAME -o -z $CDS_SERVER_NAMESPACE ] && echo "Could not find CDS server" && exit 1
+
+ # start the port-forwarder
+ kubectl -n $CDS_SERVER_NAMESPACE port-forward pod/${CDS_SERVER_NAME} 63478:13478 >/dev/null 2>&1 &
+ # query the cds server
+ sleep 1
tmpfile=$(mktemp "./stunnerd-config.XXXXXX")
- kubectl get cm -n $namespace $name -o jsonpath="{.data.stunnerd\.conf}" > $tmpfile
+ curl -s http://127.0.0.1:63478/api/v1/configs/${namespace}/${name} > $tmpfile
+ if grep -q "onfig not found" $tmpfile >/dev/null 2>&1; then
+ cat $tmpfile
+ exit 1
+ fi
+
local AUTH_TYPE=$($jq ".auth.type" $tmpfile)
[ $AUTH_TYPE == "plaintext" ] && AUTH_TYPE="static"
[ $AUTH_TYPE == "longterm" ] && AUTH_TYPE="ephemeral"
diff --git a/cmd/stunnerd/README.md b/cmd/stunnerd/README.md
index 41eb67db..ba47d719 100644
--- a/cmd/stunnerd/README.md
+++ b/cmd/stunnerd/README.md
@@ -4,14 +4,14 @@ The `stunnerd` daemon implements the STUNner gateway dataplane.
The daemon supports two basic modes. For quick tests `stunnerd` can be configured as a TURN server
by specifying a TURN network URI on the command line. For more complex scenarios, and especially
-for use in a Kubernetes cluster, `stunnerd` can take configuration from a config file. In addition,
-`stunnerd` implements a watch-mode, so that it can actively monitor the config file for updates
-and, once the config file has changed, automatically reconcile the TURN server to the new
-configuration. This mode is intended for use with the [STUNner Kubernetes gateway
-operator](https://github.com/l7mp/stunner-gateway-operator): the operator watches the Kubernetes
-[Gateway API](https://gateway-api.sigs.k8s.io) resources and renders the active control plane
-configuration into a ConfigMap, which is then mapped into the `stunnerd` pod's filesystem so that
-the daemon can pick up the latest configuration using the watch mode.
+for use in a Kubernetes cluster, `stunnerd` can take configuration from a config origin, which can
+either be a config file or from a remote server reached over WebSocket. In addition, `stunnerd`
+implements a watch-mode, so that it can actively monitor the config origin for updates and
+automatically reconcile the TURN server to any new configuration. This mode is intended for use
+with the [STUNner Kubernetes gateway operator](https://github.com/l7mp/stunner-gateway-operator):
+the operator watches the Kubernetes [Gateway API](https://gateway-api.sigs.k8s.io) resources,
+renders the active control plane configuration per each `stunnerd` pod and dynamically updates the
+dataplane using STUNner's config discovery service.
## Features
@@ -23,14 +23,17 @@ the daemon can pick up the latest configuration using the watch mode.
Extensions for TCP Allocations
* TURN transport over UDP, TCP, TLS/TCP and DTLS/UDP.
* TURN/UDP listener CPU scaling.
-* Two authentication modes via the long-term STUN/TURN credential mechanism: `plaintext` using a
- static username/password pair, and `longterm` with dynamically generated time-scoped credentials.
+* Two authentication modes via the long-term STUN/TURN credential mechanism: `static` using a
+ static username/password pair, and `ephemeral` with dynamically generated time-scoped
+ credentials.
+* Peer port range filtering.
## Getting Started
### Installation
As easy as with any Go program.
+
```console
cd stunner
go build -o stunnerd cmd/stunnerd/main.go
@@ -38,95 +41,79 @@ go build -o stunnerd cmd/stunnerd/main.go
### Usage
-The below command will open a `stunnerd` UDP listener at `127.0.0.1:5000`, set `plaintext`
-authentication using the username/password pair `user1/passwrd1`, and raises the debug level to the
-maximum.
+The below command will open a `stunnerd` UDP listener at `127.0.0.1:5000`, set `static` authentication using the username/password pair `user1/passwrd1`, and raise the debug level to the maximum.
```console
./stunnerd --log=all:TRACE turn://user1:passwd1@127.0.0.1:5000
```
-Alternatively, run `stunnerd` in verbose mode with the config file taken from
-`cmd/stunnerd/stunnerd.conf`. Adding the flag `-w` will enable watch mode.
+Alternatively, run `stunnerd` in verbose mode with the config file taken from `cmd/stunnerd/stunnerd.conf`. Adding the flag `-w` will enable watch mode.
```console
-./stunnerd -v -w -c cmd/stunnerd/stunnerd.conf
+./stunnerd -v -w -c file://cmd/stunnerd/stunnerd.conf
```
-Type `./stunnerd` to see a short description of the command line arguments supported by `stunnerd`.
+Type `./stunnerd -h` to get a short description of the supported command line arguments.
-In practice, you'll rarely need to run `stunnerd` directly: just fire up the [prebuilt container
-image](https://hub.docker.com/repository/docker/l7mp/stunnerd) in Kubernetes and you should be good
-to go.
+In practice, you'll rarely need to run `stunnerd` directly: just fire up the [prebuilt container image](https://hub.docker.com/repository/docker/l7mp/stunnerd) in Kubernetes and you should be good to go. Or better yet, [install](/docs/INSTALL.md) the STUNner Kubernetes gateway operator that will readily manage the `stunnerd` pods for each Gateway you create.
## Configuration
-Using the below configuration, `stunnerd` will open 4 STUNner listeners: two for accepting
-unencrypted connections at UDP/3478 and TCP/3478, and two for encrypted connections at TLS/TCP/3479
-and DTLS/UDP/3479. For easier debugging, the port for the transport relay connections opened by
-`stunnerd` will be taken from [10000:19999] for the UDP listener, [20000:29999] for the TCP
-listener, etc. The daemon will use `longterm` authentication, with the shared secret read from the
-environment variable `$STUNNER_SHARED_SECRET` during initialization. The relay address is taken
-from the `$STUNNER_ADDR` environment variable.
+Using the below configuration, `stunnerd` will open 4 STUNner listeners: two for accepting unencrypted connections at UDP/3478 and TCP/3478, and two for encrypted connections at TLS/TCP/3479 and DTLS/UDP/3479. The daemon will use `ephemeral` authentication, with the shared secret taken from the environment variable `$STUNNER_SHARED_SECRET` during initialization. The relay address will be taken from the `$STUNNER_ADDR` environment variable.
``` yaml
-version: v1alpha1
+version: v1
admin:
name: my-stunnerd
logLevel: all:DEBUG
realm: "my-realm.example.com"
-static:
- auth:
- type: longterm
- credentials:
- secret: $STUNNER_SHARED_SECRET
- listeners:
- - name: stunnerd-udp
- address: "$STUNNER_ADDR"
- protocol: udp
- port: 3478
- minPort: 10000
- maxPort: 19999
- - name: stunnerd-tcp
- address: "$STUNNER_ADDR"
- protocol: tcp
- port: 3478
- minPort: 20000
- maxPort: 29999
- - name: stunnerd-tls
- protocol: tls
- port: 3479
- minPort: 30000
- maxPort: 39999
- cert: "my-cert.cert"
- key: "my-key.key"
- - name: stunnerd-dtls
- protocol: dtls
- port: 3479
- cert: "my-cert.cert"
- key: "my-key.key"
- minPort: 40000
- maxPort: 49999
+auth:
+ type: ephemeral
+ credentials:
+ secret: $STUNNER_SHARED_SECRET
+listeners:
+ - name: stunnerd-udp
+ address: "$STUNNER_ADDR"
+ protocol: turn-udp
+ port: 3478
+ routes:
+ - default/media-plane
+ - name: stunnerd-tcp
+ address: "$STUNNER_ADDR"
+ protocol: turn-tcp
+ port: 3478
+ routes:
+ - default/media-plane
+ - name: stunnerd-tls
+ address: "$STUNNER_ADDR"
+ protocol: turn-tls
+ port: 3479
+ cert: "my-cert.cert"
+ key: "my-key.key"
+ routes:
+ - default/media-plane
+ - name: stunnerd-dtls
+ address: "$STUNNER_ADDR"
+ protocol: turn-dtls
+ port: 3479
+ cert: "my-cert.cert"
+ key: "my-key.key"
+ routes:
+ - default/media-plane
+clusters:
+ - name: stunner/iperf-server
+ protocol: UDP
+ type: STATIC
+ endpoints:
+ - 127.0.0.1
```
-## Advanced features
-
-### TURN/UDP listener CPU scaling
+STUNner can run multiple parallel readloops for TURN/UDP listeners, which allows it to scale to practically any number of CPUs and brings massive performance improvements for UDP workloads. This can be achieved by creating a configurable number of UDP readloop threads over the same TURN listener. The kernel will load-balance allocations across the readloops per the IP 5-tuple and so the same allocation will always stay at the same CPU, which is important for correct TURN operations.
-STUNner can run multiple parallel readloops for TURN/UDP listeners, which allows it to scale to any
-practical number of CPUs and brings massive performance improvements on UDP workloads. This is
-achieved by creating a configurable number of UDP server sockets using the `SO_REUSEPORT` socket
-option and spawn a separate goroutine to run a parallel readloop per each listener. The kernel will
-load-balance allocations across the sockets/readloops per the IP 5-tuple, therefore the same
-allocation will always stay at the same CPU which is important for correct operations.
-
-The feature is exposed via the command line flag `--udp-thread-num=`. The below
-starts `stunnerd` watching the config file in `/etc/stunnerd/stunnerd.conf` using 32 parallel UDP
-readloops (the default is 16).
+The feature is exposed via the command line flag `--udp-thread-num=`. The below starts `stunnerd` watching the config file in `/etc/stunnerd/stunnerd.conf` using 32 parallel UDP readloops (the default is 16).
``` sh
./stunnerd -w -c /etc/stunnerd/stunnerd.conf --udp-thread-num=32
-
```
## License
@@ -137,5 +124,4 @@ MIT License - see [LICENSE](../../LICENSE) for full text.
## Acknowledgments
-Initial code adopted from [pion/stun](https://github.com/pion/stun) and
-[pion/turn](https://github.com/pion/turn).
+Initial code adopted from [pion/stun](https://github.com/pion/stun) and [pion/turn](https://github.com/pion/turn).
diff --git a/cmd/stunnerd/main.go b/cmd/stunnerd/main.go
index e5454c3a..462e79d5 100644
--- a/cmd/stunnerd/main.go
+++ b/cmd/stunnerd/main.go
@@ -1,48 +1,76 @@
package main
import (
- // "fmt"
"context"
+ "fmt"
"os"
"os/signal"
"syscall"
"time"
flag "github.com/spf13/pflag"
+ cliopt "k8s.io/cli-runtime/pkg/genericclioptions"
"github.com/l7mp/stunner"
- "github.com/l7mp/stunner/pkg/apis/v1alpha1"
+ stnrv1 "github.com/l7mp/stunner/pkg/apis/v1"
+ "github.com/l7mp/stunner/pkg/buildinfo"
+ cdsclient "github.com/l7mp/stunner/pkg/config/client"
)
-// usage: stunnerd -v turn://user1:passwd1@127.0.0.1:3478?transport=udp
-
-const (
- defaultLoglevel = "all:INFO"
- confUpdatePeriod = 1 * time.Second
+var (
+ version = "dev"
+ commitHash = "n/a"
+ buildDate = ""
)
func main() {
os.Args[0] = "stunnerd"
- var config = flag.StringP("config", "c", "", "Config file.")
- var level = flag.StringP("log", "l", "", "Log level (default: all:INFO).")
- var watch = flag.BoolP("watch", "w", false, "Watch config file for updates (default: false).")
+ var config = flag.StringP("config", "c", "", "Config origin, either a valid address in the format IP:port, or HTTP URL to the CDS server, or literal \"k8s\" to discover the CDS server from Kubernetes, or a proper file name URI in the format file:// (overrides: STUNNER_CONFIG_ORIGIN)")
+ var level = flag.StringP("log", "l", "", "Log level (format: :, overrides: PION_LOG_*, default: all:INFO)")
+ var id = flag.StringP("id", "i", "", "Id for identifying with the CDS server (format: /, overrides: STUNNER_NAMESPACE/STUNNER_NAME, default: )")
+ var watch = flag.BoolP("watch", "w", false, "Watch config file for updates (default: false)")
var udpThreadNum = flag.IntP("udp-thread-num", "u", 0,
- "Number of readloop threads (CPU cores) per UDP listener. Zero disables UDP multithreading (default: 0).")
- var dryRun = flag.BoolP("dry-run", "d", false, "Suppress side-effects, intended for testing (default: false).")
- var verbose = flag.BoolP("verbose", "v", false, "Verbose logging, identical to <-l all:DEBUG>.")
+ "Number of readloop threads (CPU cores) per UDP listener. Zero disables UDP multithreading (default: 0)")
+ var dryRun = flag.BoolP("dry-run", "d", false, "Suppress side-effects, intended for testing (default: false)")
+ var verbose = flag.BoolP("verbose", "v", false, "Verbose logging, identical to <-l all:DEBUG>")
+
+ // Kubernetes config flags
+ k8sConfigFlags := cliopt.NewConfigFlags(true)
+ k8sConfigFlags.AddFlags(flag.CommandLine)
+
+ // CDS server discovery flags
+ cdsConfigFlags := cdsclient.NewCDSConfigFlags()
+ cdsConfigFlags.AddFlags(flag.CommandLine)
+
flag.Parse()
- logLevel := defaultLoglevel
+ logLevel := stnrv1.DefaultLogLevel
if *verbose {
- // verbose mode on, override any loglevel
logLevel = "all:DEBUG"
}
+
if *level != "" {
- // loglevel set on the comman line, use that one instead
logLevel = *level
}
+ configOrigin := stnrv1.DefaultConfigDiscoveryAddress
+ if origin, ok := os.LookupEnv(stnrv1.DefaultEnvVarConfigOrigin); ok {
+ configOrigin = origin
+ }
+ if *config != "" {
+ configOrigin = *config
+ }
+
+ if *id == "" {
+ name, ok1 := os.LookupEnv(stnrv1.DefaultEnvVarName)
+ namespace, ok2 := os.LookupEnv(stnrv1.DefaultEnvVarNamespace)
+ if ok1 && ok2 {
+ *id = fmt.Sprintf("%s/%s", namespace, name)
+ }
+ }
+
st := stunner.NewStunner(stunner.Options{
+ Name: *id,
LogLevel: logLevel,
DryRun: *dryRun,
UDPListenerThreadNum: *udpThreadNum,
@@ -51,55 +79,72 @@ func main() {
log := st.GetLogger().NewLogger("stunnerd")
- conf := make(chan v1alpha1.StunnerConfig, 1)
+ buildInfo := buildinfo.BuildInfo{Version: version, CommitHash: commitHash, BuildDate: buildDate}
+ log.Infof("Starting stunnerd id %q, STUNner %s ", st.GetId(), buildInfo.String())
+
+ conf := make(chan *stnrv1.StunnerConfig, 1)
defer close(conf)
- var cancelWatcher context.CancelFunc
- if *config == "" && flag.NArg() == 1 {
- log.Infof("starting %s with default configuration at TURN URI: %s",
+ var cancelConfigLoader context.CancelFunc
+ if flag.NArg() == 1 {
+ log.Infof("Starting %s with default configuration at TURN URI: %s",
os.Args[0], flag.Arg(0))
c, err := stunner.NewDefaultConfig(flag.Arg(0))
if err != nil {
- log.Errorf("could not load default STUNner config: %s", err.Error())
+ log.Errorf("Could not load default STUNner config: %s", err.Error())
os.Exit(1)
}
- conf <- *c
+ conf <- c
- } else if *config != "" && !*watch {
- log.Infof("loading configuration from config file %q", *config)
+ } else if !*watch {
+ ctx, cancel := context.WithCancel(context.Background())
- c, err := stunner.LoadConfig(*config)
+ if configOrigin == "k8s" {
+ log.Info("Discovering configuration from Kubernetes")
+ cdsAddr, err := cdsclient.DiscoverK8sCDSServer(ctx, k8sConfigFlags, cdsConfigFlags,
+ st.GetLogger().NewLogger("cds-fwd"))
+ if err != nil {
+ log.Errorf("Error searching for CDS server: %s", err.Error())
+ os.Exit(1)
+ }
+ configOrigin = cdsAddr.Addr
+ }
+
+ log.Infof("Loading configuration from origin %q", configOrigin)
+ c, err := st.LoadConfig(configOrigin)
if err != nil {
log.Error(err.Error())
os.Exit(1)
}
+ cancel()
- conf <- *c
-
- } else if *config != "" && *watch {
- log.Infof("watching configuration file at %q", *config)
+ conf <- c
- // init stunnerd with an empty config: this bootstraps it with the default
- // resources (above all, starts the health-checker)
- initConf := stunner.NewZeroConfig()
- log.Debug("bootstrapping with zero reconciliation")
- if err := st.Reconcile(*initConf); err != nil {
- log.Errorf("could not reconcile initial configuratoin: %s", err.Error())
- os.Exit(1)
- }
+ } else if *watch {
+ log.Info("Bootstrapping stunnerd with minimal config")
+ z := cdsclient.ZeroConfig(st.GetId())
+ conf <- z
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
- cancelWatcher = cancel
-
- if err := stunner.WatchConfig(ctx, stunner.Watcher{
- ConfigFile: *config,
- ConfigChannel: conf,
- Logger: st.GetLogger(),
- }); err != nil {
- log.Errorf("could not create config file watcher: %s", err.Error())
+ cancelConfigLoader = cancel
+
+ if configOrigin == "k8s" {
+ log.Info("Discovering configuration from Kubernetes")
+ cdsAddr, err := cdsclient.DiscoverK8sCDSServer(ctx, k8sConfigFlags, cdsConfigFlags,
+ st.GetLogger().NewLogger("cds-fwd"))
+ if err != nil {
+ log.Errorf("Error searching for CDS server: %s", err.Error())
+ os.Exit(1)
+ }
+ configOrigin = cdsAddr.Addr
+ }
+
+ log.Infof("Watching configuration at origin %q (ignoring delete-config updates)", configOrigin)
+ if err := st.WatchConfig(ctx, configOrigin, conf, true); err != nil {
+ log.Errorf("Could not run config watcher: %s", err.Error())
os.Exit(1)
}
} else {
@@ -107,13 +152,9 @@ func main() {
os.Exit(1)
}
- sigint := make(chan os.Signal, 1)
- defer close(sigint)
- signal.Notify(sigint, syscall.SIGINT)
-
sigterm := make(chan os.Signal, 1)
defer close(sigterm)
- signal.Notify(sigterm, syscall.SIGTERM)
+ signal.Notify(sigterm, syscall.SIGTERM, syscall.SIGINT)
exit := make(chan bool, 1)
defer close(exit)
@@ -121,21 +162,18 @@ func main() {
for {
select {
case <-exit:
- log.Info("normal exit on graceful shutdown")
- os.Exit(0)
-
- case <-sigint:
- log.Info("normal exit")
+ log.Info("Normal exit on graceful shutdown")
os.Exit(0)
case <-sigterm:
- log.Info("caught SIGTERM: performing a graceful shutdown")
+ log.Infof("Commencing graceful shutdown with %d active connection(s)",
+ st.AllocationCount())
st.Shutdown()
- // cancel the config watcher
- if cancelWatcher != nil {
- log.Info("canceling config watcher")
- cancelWatcher()
+ if cancelConfigLoader != nil {
+ log.Info("Canceling config loader")
+ cancelConfigLoader()
+ cancelConfigLoader = nil
}
go func() {
@@ -150,25 +188,25 @@ func main() {
}()
case c := <-conf:
- log.Trace("new configuration file available")
+ log.Infof("New configuration available: %q", c.String())
// command line loglevel overrides config
if *verbose || *level != "" {
c.Admin.LogLevel = logLevel
}
- // we have working stunnerd: reconcile
- log.Debug("initiating reconciliation")
- err := st.Reconcile(c)
- log.Trace("reconciliation ready")
- if err != nil {
- if e, ok := err.(v1alpha1.ErrRestarted); ok {
- log.Debugf("reconciliation ready: %s", e.Error())
+ log.Debug("Initiating reconciliation")
+
+ if err := st.Reconcile(c); err != nil {
+ if e, ok := err.(stnrv1.ErrRestarted); ok {
+ log.Debugf("Reconciliation ready: %s", e.Error())
} else {
- log.Errorf("could not reconcile new configuration: %s, "+
- "rolling back to last running config", err.Error())
+ log.Errorf("Could not reconcile new configuration "+
+ "(running configuration unchanged): %s", err.Error())
}
}
+
+ log.Trace("Reconciliation ready")
}
}
}
diff --git a/cmd/stunnerd/stunnerd.conf b/cmd/stunnerd/stunnerd.conf
index 03403d72..bee46539 100644
--- a/cmd/stunnerd/stunnerd.conf
+++ b/cmd/stunnerd/stunnerd.conf
@@ -23,25 +23,25 @@ listeners:
- name: stunnerd-udp
public_address: "$STUNNER_ADDR"
address: "$STUNNER_ADDR"
- protocol: udp
+ protocol: TURN-UDP
port: $STUNNER_PORT
- min_port: $STUNNER_MIN_PORT
- max_port: $STUNNER_MAX_PORT
+ min_relay_port: $STUNNER_MIN_PORT
+ max_relay_port: $STUNNER_MAX_PORT
routes:
- open-cluster
# - media-server-cluster
- name: stunnerd-tcp
public_address: "$STUNNER_ADDR"
address: "$STUNNER_ADDR"
- protocol: tcp
+ protocol: TURN-TCP
port: $STUNNER_PORT
- min_port: $STUNNER_MIN_PORT
- max_port: $STUNNER_MAX_PORT
+ min_relay_port: $STUNNER_MIN_PORT
+ max_relay_port: $STUNNER_MAX_PORT
routes:
- open-cluster
# - media-server-cluster
# - name: stunnerd-tls
- # protocol: tls
+ # protocol: TURN-TLS
# port: 3479
# cert:
# key:
diff --git a/cmd/turncat/README.md b/cmd/turncat/README.md
index 9d4d5f2d..9589affe 100644
--- a/cmd/turncat/README.md
+++ b/cmd/turncat/README.md
@@ -1,50 +1,63 @@
# turncat: Swiss-army-knife testing tool for STUNner
-`turncat` is a STUN/TURN client to open a connection through a TURN server to an arbitrary remote
-address/port. The main use is to open a local tunnel endpoint to any service running inside a
-Kubernetes cluster via STUNner. This is very similar in functionality to `kubectl proxy`, but it
-uses STUN/TURN to enter the cluster.
+`turncat` is a STUN/TURN client to open a connection through a TURN server to an arbitrary remote address/port.
+The main use is to open a local tunnel endpoint to any service running inside a Kubernetes cluster via STUNner.
+This is very similar in functionality to `kubectl port-forward`, but it uses STUN/TURN to enter the cluster.
+This is much faster than the TCP connection used by `kubectl`.
-## Getting Started
+## Installation
-### Installation
+Install the `turncat` binary using the standard Go toolchain and add it to `$PATH`.
-As simple as it gets:
+```console
+go install github.com/l7mp/stunner/cmd/turncat@latest
+```
+
+You can also enforce a specific OS, CPU architecture, and STUNner version like below:
+
+```console
+GOOS=windows GOARCH=amd64 go install github.com/l7mp/stunner/cmd/turncat@v0.17.5
+```
+
+Building from source is as easy as it usually gets with Go:
```console
cd stunner
go build -o turncat cmd/turncat/main.go
```
-### Usage
+## Usage
+
+Listen to client connections on the UDP listener `127.0.0.1:5000` and tunnel the received packets through the TURN server located at `192.0.2.1:3478` to the UDP listener located at `192.0.2.2:53`.
+Use the [`static` STUN/TURN credential mechanism](/docs/AUTH.md) to authenticate with the TURN server and set the user/passwd to `test/test`:
+
+```console
+./turncat --log=all:INFO,turncat:DEBUG udp://127.0.0.1:5000 turn://test:test@192.0.2.1:3478 \
+ udp://192.0.2.2:53
+```
-Listen to client connections on the UDP listener `127.0.0.1:5000` and tunnel the received packets
-through the TURN server located at `192.0.2.1:3478` to the UDP server located at
-`192.0.2.2:53`. Use the longterm STUN/TURN credential mechanism to authenticate with the TURN
-server and set the user/passwd to `test/test`:
+TLS/DTLS should also work.
+Below `--insecure` allows `turncat` to accept self-signed TLS certificates and `--verbose` is equivalent to setting all loggers to DEBUG mode (`-l all:DEBUG`).
```console
-./turncat --log=all:INFO,turncat:DEBUG udp://127.0.0.1:5000 turn://test:test@192.0.2.1:3478 udp://192.0.2.2:53
+./turncat --verbose --insecure udp://127.0.0.1:5000 \
+ turn://test:test@192.0.2.1:3478?transport=tls udp://192.0.2.2:53
```
-TLS/DTLS should also work fine; note that `--insecure` allows `turncat` to accept self-signed TLS
-certificates and `--verbose` is equivalent to setting all `turncat` loggers to DEBUG mode (`-l
-all:DEBUG`).
+Alternatively, you can specify the special TURN server meta-URI `k8s://stunner/udp-gateway:udp-listener` to let `turncat` parse the running STUNner configuration from the active Kubernetes cluster.
+The URI directs `turncat` to read the config of the STUNner Gateway called `udp-gateway` in the `stunner` namespace and connect to the TURN listener named `udp-listener`.
+The CLI flag `-` instructs `turncat` to listen on the standard input: anything you type in the terminal will be sent via STUNner to the peer `udp://10.0.0.1:9001` (after you press Enter).
+The CLI flag `-v` will enable verbose logging.
```console
-./turncat --verbose --insecure udp://127.0.0.1:5000 turn://test:test@192.0.2.1:3478?transport=tls udp://192.0.2.2:53
+./turncat -v - k8s://stunner/udp-gateway:udp-listener udp://10.0.0.1:9001
```
-Alternatively, specify the special TURN server URI `k8s://stunner/stunnerd-config:udp-listener` to
-let `turncat` parse the running STUNner configuration from the active Kubernetes cluster. The URI
-directs `turncat` to read the STUNner config from the ConfigMap named `stunnerd-config` in the
-`stunner` namespace, and connect to the STUNner listener named `udp-listener`. The CLI flag `-`
-instructs `turncat` to listen on the standard input: anything you type in the terminal will be sent
-via STUNner to the peer `udp://10.0.0.1:9001` (after you press Enter). The CLI flag `-v` will
-enable verbose logging.
+Note that the standard `kubectl` command line flags are available.
+For instance, the below will use the context `prod-europe` from the kubeconfig file `kube-prod.conf`:
```console
-./turncat -v - k8s://stunner/stunnerd-config:udp-listener udp://10.0.0.1:9001
+./turncat --kubeconfig=kube-prod.conf --context prod-europe -v - k8s://... udp://...
```
## License
@@ -55,5 +68,4 @@ MIT License - see [LICENSE](../../LICENSE) for full text.
## Acknowledgments
-Initial code adopted from [pion/stun](https://github.com/pion/stun) and
-[pion/turn](https://github.com/pion/turn).
+Initial code adopted from [pion/stun](https://github.com/pion/stun) and [pion/turn](https://github.com/pion/turn).
diff --git a/cmd/turncat/main.go b/cmd/turncat/main.go
index 844c72d6..9f305daa 100644
--- a/cmd/turncat/main.go
+++ b/cmd/turncat/main.go
@@ -2,7 +2,6 @@ package main
import (
"context"
- "encoding/json"
"fmt"
"os"
"os/signal"
@@ -11,24 +10,36 @@ import (
"time"
"github.com/pion/logging"
- "github.com/pion/turn/v2"
+ "github.com/pion/turn/v4"
flag "github.com/spf13/pflag"
- corev1 "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/types"
- "sigs.k8s.io/controller-runtime/pkg/client"
- "sigs.k8s.io/controller-runtime/pkg/client/config"
+ cliopt "k8s.io/cli-runtime/pkg/genericclioptions"
"github.com/l7mp/stunner"
- stunnerv1alpha1 "github.com/l7mp/stunner/pkg/apis/v1alpha1"
+ stnrv1 "github.com/l7mp/stunner/pkg/apis/v1"
+ "github.com/l7mp/stunner/pkg/buildinfo"
+ cdsclient "github.com/l7mp/stunner/pkg/config/client"
"github.com/l7mp/stunner/pkg/logger"
)
-const usage = "turncat [-l|--log ] [-i|--insecure] client server peer\n\tclient: ://:\n\tserver: @: | /:listener\n\tpeer: udp://:\n\tauth: \n"
-const defaultStunnerdConfigfileName = "stunnerd.conf"
-
-var log logging.LeveledLogger
-var defaultDuration time.Duration
+const usage = `turncat [options]
+ client-addr: ://:
+ turn-server-addr: @: | /:
+ peer-addr: udp://:
+ auth:
+`
+
+var (
+ k8sConfigFlags *cliopt.ConfigFlags
+ cdsConfigFlags *cdsclient.CDSConfigFlags
+ log logging.LeveledLogger
+ defaultDuration time.Duration
+ loggerFactory logger.LoggerFactory
+
+ version = "dev"
+ commitHash = "n/a"
+ buildDate = ""
+)
func main() {
var Usage = func() {
@@ -38,13 +49,29 @@ func main() {
os.Args[0] = "turncat"
defaultDuration, _ = time.ParseDuration("1h")
- var level = flag.StringP("log", "l", "all:WARN", "Log level (default: all:WARN).")
- // var user = flag.StringP("user", "u", "", "Set username. Auth fields in the TURN URI override this.")
- // var passwd = flag.StringP("log", "l", "all:WARN", "Log level (default: all:WARN).")
- var insecure = flag.BoolP("insecure", "i", false, "Insecure TLS mode, accept self-signed certificates (default: false).")
- var verbose = flag.BoolP("verbose", "v", false, "Verbose logging, identical to -l all:DEBUG.")
+
+ // Kubernetes config flags
+ k8sConfigFlags = cliopt.NewConfigFlags(true)
+ k8sConfigFlags.AddFlags(flag.CommandLine)
+
+ // CDS server discovery flags
+ cdsConfigFlags = cdsclient.NewCDSConfigFlags()
+ cdsConfigFlags.AddFlags(flag.CommandLine)
+
+ var serverName string
+ flag.StringVar(&serverName, "sni", "", "Server name (SNI) for TURN/TLS client connections")
+ var insecure = flag.BoolP("insecure", "i", false, "Insecure TLS mode, accept self-signed TURN server certificates (default: false)")
+ var level = flag.StringP("log", "l", "all:WARN", "Log level")
+ var verbose = flag.BoolP("verbose", "v", false, "Enable verbose logging, identical to -l all:DEBUG")
+ var help = flag.BoolP("help", "h", false, "Display this help text and exit")
+
flag.Parse()
+ if *help {
+ Usage()
+ os.Exit(0)
+ }
+
if flag.NArg() != 3 {
Usage()
os.Exit(1)
@@ -54,8 +81,11 @@ func main() {
*level = "all:DEBUG"
}
- logger := logger.NewLoggerFactory(*level)
- log = logger.NewLogger("turncat-cli")
+ loggerFactory = logger.NewLoggerFactory(*level)
+ log = loggerFactory.NewLogger("turncat-cli")
+
+ buildInfo := buildinfo.BuildInfo{Version: version, CommitHash: commitHash, BuildDate: buildDate}
+ log.Debugf("Starting turncat %s", buildInfo.String())
uri := flag.Arg(1)
log.Debugf("Reading STUNner config from URI %q", uri)
@@ -86,8 +116,9 @@ func main() {
PeerAddr: flag.Arg(2),
Realm: config.Auth.Realm,
AuthGen: authGen,
+ ServerName: serverName,
InsecureMode: *insecure,
- LoggerFactory: logger,
+ LoggerFactory: loggerFactory,
}
t, err := stunner.NewTurncat(cfg)
if err != nil {
@@ -103,7 +134,7 @@ func main() {
t.Close()
}
-func getStunnerConf(uri string) (*stunnerv1alpha1.StunnerConfig, error) {
+func getStunnerConf(uri string) (*stnrv1.StunnerConfig, error) {
s := strings.Split(uri, "://")
if len(s) < 2 {
return nil, fmt.Errorf("cannot parse server URI")
@@ -115,14 +146,14 @@ func getStunnerConf(uri string) (*stunnerv1alpha1.StunnerConfig, error) {
case "k8s":
conf, err := getStunnerConfFromK8s(def)
if err != nil {
- return nil, fmt.Errorf("Could not read running STUNner configuration from "+
+ return nil, fmt.Errorf("could not read running STUNner configuration from "+
"Kubernetes: %w", err)
}
return conf, nil
case "turn":
conf, err := getStunnerConfFromCLI(def)
if err != nil {
- return nil, fmt.Errorf("Could not generate STUNner configuration from "+
+ return nil, fmt.Errorf("could not generate STUNner configuration from "+
"URI %q: %w", uri, err)
}
return conf, nil
@@ -131,46 +162,40 @@ func getStunnerConf(uri string) (*stunnerv1alpha1.StunnerConfig, error) {
}
}
-func getStunnerConfFromK8s(def string) (*stunnerv1alpha1.StunnerConfig, error) {
+func getStunnerConfFromK8s(def string) (*stnrv1.StunnerConfig, error) {
namespace, name, listener, err := parseK8sDef(def)
if err != nil {
return nil, err
}
- ctx := context.Background()
- cfg := config.GetConfigOrDie()
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
- cli, err := client.New(cfg, client.Options{})
+ log.Debug("Searching for CDS server")
+ cdsAddr, err := cdsclient.DiscoverK8sCDSServer(ctx, k8sConfigFlags, cdsConfigFlags,
+ loggerFactory.NewLogger("cds-fwd"))
if err != nil {
- return nil, err
- }
-
- // get the configmap
- lookupKey := types.NamespacedName{
- Namespace: namespace,
- Name: name,
+ return nil, fmt.Errorf("error searching for CDS server: %w", err)
}
- cm := &corev1.ConfigMap{}
- err = cli.Get(ctx, lookupKey, cm)
+ cds, err := cdsclient.NewConfigNamespaceNameAPI(cdsAddr.Addr, namespace, name,
+ loggerFactory.NewLogger("cds-client"))
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("error creating CDS client: %w", err)
}
- //parse out the stunnerconf
- jsonConf, found := cm.Data[defaultStunnerdConfigfileName]
- if !found {
- return nil, fmt.Errorf("error unpacking STUNner configmap: %s not found",
- defaultStunnerdConfigfileName)
+ confs, err := cds.Get(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("error obtaining config from CDS client: %w", err)
}
-
- conf := stunnerv1alpha1.StunnerConfig{}
- if err := json.Unmarshal([]byte(jsonConf), &conf); err != nil {
- return nil, err
+ if len(confs) != 1 {
+ return nil, fmt.Errorf("invalid number of configs returned from CDS client: %d",
+ len(confs))
}
+ conf := confs[0]
// remove all but the named listener
- ls := []stunnerv1alpha1.ListenerConfig{}
+ ls := []stnrv1.ListenerConfig{}
for _, l := range conf.Listeners {
// parse out the listener name (as per the Gateway API) from the TURN listener-name
// (this is in the form: //
@@ -196,13 +221,13 @@ func getStunnerConfFromK8s(def string) (*stunnerv1alpha1.StunnerConfig, error) {
"specified TURN server URI", listener)
}
- conf.Listeners = []stunnerv1alpha1.ListenerConfig{{}}
+ conf.Listeners = make([]stnrv1.ListenerConfig, 1)
copy(conf.Listeners, ls)
- return &conf, nil
+ return conf, nil
}
-func getStunnerConfFromCLI(def string) (*stunnerv1alpha1.StunnerConfig, error) {
+func getStunnerConfFromCLI(def string) (*stnrv1.StunnerConfig, error) {
uri := fmt.Sprintf("turn://%s", def)
conf, err := stunner.NewDefaultConfig(uri)
@@ -212,11 +237,11 @@ func getStunnerConfFromCLI(def string) (*stunnerv1alpha1.StunnerConfig, error) {
u, err := stunner.ParseUri(uri)
if err != nil {
- return nil, fmt.Errorf("Invalid STUNner URI %q: %s", uri, err)
+ return nil, fmt.Errorf("invalid STUNner URI %q: %s", uri, err)
}
if u.Username == "" || u.Password == "" {
- return nil, fmt.Errorf("Username/password must be set: '%s'", uri)
+ return nil, fmt.Errorf("username/password must be set: '%s'", uri)
}
conf.Listeners[0].PublicAddr = u.Address
@@ -225,15 +250,15 @@ func getStunnerConfFromCLI(def string) (*stunnerv1alpha1.StunnerConfig, error) {
return conf, nil
}
-func getAuth(config *stunnerv1alpha1.StunnerConfig) (stunner.AuthGen, error) {
+func getAuth(config *stnrv1.StunnerConfig) (stunner.AuthGen, error) {
auth := config.Auth
- atype, err := stunnerv1alpha1.NewAuthType(auth.Type)
+ atype, err := stnrv1.NewAuthType(auth.Type)
if err != nil {
return nil, err
}
switch atype {
- case stunnerv1alpha1.AuthTypeLongTerm:
+ case stnrv1.AuthTypeEphemeral:
s, found := auth.Credentials["secret"]
if !found {
return nil, fmt.Errorf("cannot find shared secret for %s authentication",
@@ -243,7 +268,7 @@ func getAuth(config *stunnerv1alpha1.StunnerConfig) (stunner.AuthGen, error) {
return turn.GenerateLongTermCredentials(s, defaultDuration)
}, nil
- case stunnerv1alpha1.AuthTypePlainText:
+ case stnrv1.AuthTypeStatic:
u, found := auth.Credentials["username"]
if !found {
return nil, fmt.Errorf("cannot find username for %s authentication",
@@ -264,7 +289,7 @@ func getAuth(config *stunnerv1alpha1.StunnerConfig) (stunner.AuthGen, error) {
}
}
-func getStunnerURI(config *stunnerv1alpha1.StunnerConfig) (string, error) {
+func getStunnerURI(config *stnrv1.StunnerConfig) (string, error) {
// we should have only a single listener at this point
if len(config.Listeners) != 1 {
return "", fmt.Errorf("cannot find listener in STUNner configuration: %s",
@@ -282,16 +307,21 @@ func getStunnerURI(config *stunnerv1alpha1.StunnerConfig) (string, error) {
return "", fmt.Errorf("no protocol for listener %q", l.Name)
}
- return fmt.Sprintf("%s://%s:%d", strings.ToLower(l.Protocol), l.PublicAddr,
- l.PublicPort), nil
+ return stunner.GetStandardURLFromListener(&l)
}
func parseK8sDef(def string) (string, string, string, error) {
- re := regexp.MustCompile(`([0-9A-Za-z_-]+)/([0-9A-Za-z_-]+):([0-9A-Za-z_-]+)`)
+ re := regexp.MustCompile(`^/([0-9A-Za-z_-]+):([0-9A-Za-z_-]+)$`)
xs := re.FindStringSubmatch(def)
- if len(xs) != 4 {
- return "", "", "", fmt.Errorf("cannot parse STUNner configmap def: %q", def)
+ if len(xs) == 3 && k8sConfigFlags.Namespace != nil {
+ return *k8sConfigFlags.Namespace, xs[1], xs[2], nil
+ }
+
+ re = regexp.MustCompile(`^([0-9A-Za-z_-]+)/([0-9A-Za-z_-]+):([0-9A-Za-z_-]+)$`)
+ xs = re.FindStringSubmatch(def)
+ if len(xs) == 4 {
+ return xs[1], xs[2], xs[3], nil
}
- return xs[1], xs[2], xs[3], nil
+ return "", "", "", fmt.Errorf("cannot parse STUNner K8s URI: %q", def)
}
diff --git a/config.go b/config.go
index a13d12d2..e39675be 100644
--- a/config.go
+++ b/config.go
@@ -3,28 +3,20 @@ package stunner
import (
"context"
"encoding/base64"
- "encoding/json"
- "errors"
"fmt"
- "os"
- "regexp"
- "strconv"
"strings"
- "time"
- "github.com/fsnotify/fsnotify"
- "github.com/pion/logging"
- "github.com/pion/transport/v2"
- "sigs.k8s.io/yaml"
+ "github.com/pion/transport/v3"
"github.com/l7mp/stunner/internal/resolver"
- "github.com/l7mp/stunner/pkg/apis/v1alpha1"
+ stnrv1 "github.com/l7mp/stunner/pkg/apis/v1"
+ "github.com/l7mp/stunner/pkg/config/client"
)
-const confUpdatePeriod = 1 * time.Second
-
// Options defines various options for the STUNner server.
type Options struct {
+ // Name is the identifier of this stunnerd daemon instance. Defaults to hostname.
+ Name string
// DryRun suppresses sideeffects: STUNner will not initialize listener sockets and bring up
// the TURN server, and it will not fire up the health-check and the metrics
// servers. Intended for testing, default is false.
@@ -52,30 +44,11 @@ type Options struct {
Net transport.Net
}
-// NewZeroConfig builds a zero configuration useful for bootstrapping STUNner. It starts with
-// plaintext authentication and opens no listeners and clusters.
-func NewZeroConfig() *v1alpha1.StunnerConfig {
- return &v1alpha1.StunnerConfig{
- ApiVersion: v1alpha1.ApiVersion,
- Admin: v1alpha1.AdminConfig{},
- Auth: v1alpha1.AuthConfig{
- Type: "plaintext",
- Realm: v1alpha1.DefaultRealm,
- Credentials: map[string]string{
- "username": "dummy-username",
- "password": "dummy-password",
- },
- },
- Listeners: []v1alpha1.ListenerConfig{},
- Clusters: []v1alpha1.ClusterConfig{},
- }
-}
-
// NewDefaultConfig builds a default configuration from a TURN server URI. Example: the URI
// `turn://user:pass@127.0.0.1:3478?transport=udp` will be parsed into a STUNner configuration with
// a server running on the localhost at UDP port 3478, with plain-text authentication using the
// username/password pair `user:pass`. Health-checks and metric scarping are disabled.
-func NewDefaultConfig(uri string) (*v1alpha1.StunnerConfig, error) {
+func NewDefaultConfig(uri string) (*stnrv1.StunnerConfig, error) {
u, err := ParseUri(uri)
if err != nil {
return nil, fmt.Errorf("Invalid URI '%s': %s", uri, err)
@@ -86,36 +59,37 @@ func NewDefaultConfig(uri string) (*v1alpha1.StunnerConfig, error) {
}
h := ""
- c := &v1alpha1.StunnerConfig{
- ApiVersion: v1alpha1.ApiVersion,
- Admin: v1alpha1.AdminConfig{
- LogLevel: v1alpha1.DefaultLogLevel,
+ c := &stnrv1.StunnerConfig{
+ ApiVersion: stnrv1.ApiVersion,
+ Admin: stnrv1.AdminConfig{
+ LogLevel: stnrv1.DefaultLogLevel,
// MetricsEndpoint: "http://:8088",
HealthCheckEndpoint: &h,
},
- Auth: v1alpha1.AuthConfig{
+ Auth: stnrv1.AuthConfig{
Type: "plaintext",
- Realm: v1alpha1.DefaultRealm,
+ Realm: stnrv1.DefaultRealm,
Credentials: map[string]string{
"username": u.Username,
"password": u.Password,
},
},
- Listeners: []v1alpha1.ListenerConfig{{
+ Listeners: []stnrv1.ListenerConfig{{
Name: "default-listener",
Protocol: u.Protocol,
Addr: u.Address,
Port: u.Port,
Routes: []string{"allow-any"},
}},
- Clusters: []v1alpha1.ClusterConfig{{
+ Clusters: []stnrv1.ClusterConfig{{
Name: "allow-any",
Type: "STATIC",
Endpoints: []string{"0.0.0.0/0"},
}},
}
- if strings.ToUpper(u.Protocol) == "TLS" || strings.ToUpper(u.Protocol) == "DTLS" {
+ p := strings.ToUpper(u.Protocol)
+ if p == "TLS" || p == "DTLS" || p == "TURN-TLS" || p == "TURN-DTLS" {
certPem, keyPem, err := GenerateSelfSignedKey()
if err != nil {
return nil, err
@@ -131,273 +105,60 @@ func NewDefaultConfig(uri string) (*v1alpha1.StunnerConfig, error) {
return c, nil
}
-// LoadConfig loads a configuration from a file, substituting environment variables for
-// placeholders in the configuration file. Returns the new configuration or error if load fails.
-func LoadConfig(config string) (*v1alpha1.StunnerConfig, error) {
- c, err := os.ReadFile(config)
- if err != nil {
- return nil, fmt.Errorf("could not read config: %s\n", err.Error())
- }
-
- // substitute environtment variables
- // default port: STUNNER_PUBLIC_PORT -> STUNNER_PORT
- re := regexp.MustCompile(`^[0-9]+$`)
- port, ok := os.LookupEnv("STUNNER_PORT")
- if !ok || (ok && port == "") || (ok && !re.Match([]byte(port))) {
- publicPort := v1alpha1.DefaultPort
- publicPortStr, ok := os.LookupEnv("STUNNER_PUBLIC_PORT")
- if ok {
- if p, err := strconv.Atoi(publicPortStr); err == nil {
- publicPort = p
- }
- }
- os.Setenv("STUNNER_PORT", fmt.Sprintf("%d", publicPort))
- }
-
- e := os.ExpandEnv(string(c))
-
- s := v1alpha1.StunnerConfig{}
- // try YAML first
- if err = yaml.Unmarshal([]byte(e), &s); err != nil {
- // if it fails, try to json
- if errJ := json.Unmarshal([]byte(e), &s); err != nil {
- return nil, fmt.Errorf("could not parse config file at '%s': "+
- "YAML parse error: %s, JSON parse error: %s\n",
- config, err.Error(), errJ.Error())
- }
- }
-
- return &s, nil
-}
-
// GetConfig returns the configuration of the running STUNner daemon.
-func (s *Stunner) GetConfig() *v1alpha1.StunnerConfig {
+func (s *Stunner) GetConfig() *stnrv1.StunnerConfig {
s.log.Tracef("GetConfig")
// singletons, but we want to avoid panics when GetConfig is called on an uninitialized
// STUNner object
- adminConf := v1alpha1.AdminConfig{}
+ adminConf := stnrv1.AdminConfig{}
if len(s.adminManager.Keys()) > 0 {
- adminConf = *s.GetAdmin().GetConfig().(*v1alpha1.AdminConfig)
+ adminConf = *s.GetAdmin().GetConfig().(*stnrv1.AdminConfig)
}
- authConf := v1alpha1.AuthConfig{}
+ authConf := stnrv1.AuthConfig{}
if len(s.authManager.Keys()) > 0 {
- authConf = *s.GetAuth().GetConfig().(*v1alpha1.AuthConfig)
+ authConf = *s.GetAuth().GetConfig().(*stnrv1.AuthConfig)
}
listeners := s.listenerManager.Keys()
clusters := s.clusterManager.Keys()
- c := v1alpha1.StunnerConfig{
+ c := stnrv1.StunnerConfig{
ApiVersion: s.version,
Admin: adminConf,
Auth: authConf,
- Listeners: make([]v1alpha1.ListenerConfig, len(listeners)),
- Clusters: make([]v1alpha1.ClusterConfig, len(clusters)),
+ Listeners: make([]stnrv1.ListenerConfig, len(listeners)),
+ Clusters: make([]stnrv1.ClusterConfig, len(clusters)),
}
for i, name := range listeners {
- c.Listeners[i] = *s.GetListener(name).GetConfig().(*v1alpha1.ListenerConfig)
+ c.Listeners[i] = *s.GetListener(name).GetConfig().(*stnrv1.ListenerConfig)
}
for i, name := range clusters {
- c.Clusters[i] = *s.GetCluster(name).GetConfig().(*v1alpha1.ClusterConfig)
+ c.Clusters[i] = *s.GetCluster(name).GetConfig().(*stnrv1.ClusterConfig)
}
return &c
}
-type Watcher struct {
- // ConfigFile specifies the config file name to watch.
- ConfigFile string
- // ConfigChannel is used to return the configs read.
- ConfigChannel chan<- v1alpha1.StunnerConfig
- // Logger is a logger factory as returned by, e.g., stunner.GetLogger().
- Logger logging.LoggerFactory
- // Log is a leveled logger used to report progress. Either Logger or Log must be specified.
- Log logging.LeveledLogger
-}
-
-// WatchConfig will watch a configuration file specified in the `Watcher.ConfigFile` parameter for
-// changes and emit a new `StunnerConfig` on `Watcher.ConfigChannel` each time the file changes. If
-// no file exists at the given path, then WatchConfig will periodically retry until the file
-// appears. The configuration sent through the channel is not validated, make sure to check for
-// syntax errors on the receiver side. Use the `context` to cancel the watcher.
-func WatchConfig(ctx context.Context, w Watcher) error {
- if w.ConfigChannel == nil {
- return errors.New("uninitialized config channel")
- }
-
- if w.ConfigFile == "" {
- return errors.New("uninitialized config file path")
- }
-
- if w.Log == nil {
- w.Log = w.Logger.NewLogger("watch-config")
- }
- w.Log.Tracef("WatchConfig")
-
- go func() {
- for {
- // try to watch
- if ok := configWatcher(ctx, w); !ok {
- return
- }
-
- if ok := tryWatchConfig(ctx, w); !ok {
- return
- }
- }
-
- }()
-
- return nil
-}
-
-// tryWatchConfig runs a timer to look for the config file at the given path and returns it
-// immediately once found. Returns true if further action is needed (configWatcher has to be
-// started) or false on normal exit.
-func tryWatchConfig(ctx context.Context, w Watcher) bool {
- w.Log.Tracef("tryWatchConfig")
- config := w.ConfigFile
-
- ticker := time.NewTicker(confUpdatePeriod)
- defer ticker.Stop()
-
- for {
- select {
- case <-ctx.Done():
- return false
-
- case <-ticker.C:
- w.Log.Debugf("trying to read config file %q from periodic timer",
- config)
-
- // check if config file exists and it is readable
- if _, err := os.Stat(config); errors.Is(err, os.ErrNotExist) {
- w.Log.Debugf("config file %q does not exist", config)
-
- // report status in every 10th second
- if time.Now().Second()%10 == 0 {
- w.Log.Warnf("waiting for config file %q", config)
- }
-
- continue
- }
-
- return true
- }
- }
-}
-
-// configWatcher actually watches the config and emits the configs found on the specified
-// channel. Returns true if further action is needed (tryWatachConfig is to be started) or false on
-// normal exit.
-func configWatcher(ctx context.Context, w Watcher) bool {
- w.Log.Tracef("configWatcher")
- prev := v1alpha1.StunnerConfig{}
-
- // create a new watcher
- watcher, err := fsnotify.NewWatcher()
+// LoadConfig loads a configuration from an origin. This is a shim wrapper around configclient.Load.
+func (s *Stunner) LoadConfig(origin string) (*stnrv1.StunnerConfig, error) {
+ client, err := client.New(origin, s.name, s.logger)
if err != nil {
- return true
+ return nil, err
}
- defer watcher.Close()
- config := w.ConfigFile
- ch := w.ConfigChannel
-
- if err := watcher.Add(config); err != nil {
- w.Log.Debugf("could not add config file %q watcher: %s", config, err.Error())
- return true
- }
+ return client.Load()
+}
- // emit an initial config
- c, err := LoadConfig(config)
+// WatchConfig watches a configuration from an origin. This is a shim wrapper around configclient.Watch.
+func (s *Stunner) WatchConfig(ctx context.Context, origin string, ch chan<- *stnrv1.StunnerConfig, suppressDelete bool) error {
+ client, err := client.New(origin, s.name, s.logger)
if err != nil {
- w.Log.Warnf("could not load config file %q: %s", config, err.Error())
- return true
+ return err
}
- w.Log.Debugf("config file successfully loaded from %q", config)
-
- // send a deepcopy over the channel
- copy := v1alpha1.StunnerConfig{}
- c.DeepCopyInto(©)
- ch <- copy
-
- // save deepcopy so that we can filter repeated events
- c.DeepCopyInto(&prev)
-
- for {
- select {
- case <-ctx.Done():
- return false
-
- case e, ok := <-watcher.Events:
- if !ok {
- w.Log.Debug("config watcher event handler received invalid event")
- return true
- }
-
- w.Log.Debugf("received watcher event: %s", e.String())
-
- if e.Has(fsnotify.Remove) {
- w.Log.Warnf("config file deleted %q, disabling watcher", e.Op.String())
-
- if err := watcher.Remove(config); err != nil {
- w.Log.Debugf("could not remove config file %q watcher: %s",
- config, err.Error())
- }
-
- return true
- }
-
- if !e.Has(fsnotify.Write) {
- w.Log.Debugf("unhandled notify op on config file %q (ignoring): %s",
- e.Name, e.Op.String())
- continue
- }
-
- w.Log.Debugf("loading configuration file: %s", config)
- c, err = LoadConfig(config)
- if err != nil {
- // assume it is a YAML/JSON syntax error (LoadConfig does not
- // validate): report and ignore
- w.Log.Warnf("could not load config file %q: %s", config, err.Error())
- continue
- }
-
- // suppress repeated events
- if c.DeepEqual(&prev) {
- w.Log.Debugf("ignoring recurrent notify event for the same config file")
- continue
- }
-
- w.Log.Debugf("config file successfully loaded from %q", config)
-
- copy := v1alpha1.StunnerConfig{}
- c.DeepCopyInto(©)
- ch <- copy
-
- // save deepcopy so that we can filter repeated events
- c.DeepCopyInto(&prev)
-
- case err, ok := <-watcher.Errors:
- if !ok {
- w.Log.Debugf("config watcher error handler received invalid error")
- return true
- }
-
- w.Log.Debugf("watcher error, deactivating watcher: %s", err.Error())
-
- if err := watcher.Remove(config); err != nil {
- w.Log.Debugf("could not remove config file %q watcher: %s",
- config, err.Error())
- }
-
- return true
- }
- }
+ return client.Watch(ctx, ch, suppressDelete)
}
diff --git a/config_test.go b/config_test.go
index b83faf34..0a9b247d 100644
--- a/config_test.go
+++ b/config_test.go
@@ -1,19 +1,22 @@
package stunner
import (
+ "context"
"fmt"
"net"
- // "reflect"
- "context"
+ "net/http"
"os"
+ "strings"
"testing"
"time"
- "github.com/pion/transport/test"
+ "github.com/gorilla/websocket"
+ "github.com/pion/transport/v3/test"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/yaml"
- "github.com/l7mp/stunner/pkg/apis/v1alpha1"
+ stnrv1 "github.com/l7mp/stunner/pkg/apis/v1"
+ cdsclient "github.com/l7mp/stunner/pkg/config/client"
"github.com/l7mp/stunner/pkg/logger"
)
@@ -35,8 +38,8 @@ func TestStunnerDefaultServerVNet(t *testing.T) {
for _, conf := range []string{
"turn://user1:passwd1@1.2.3.4:3478?transport=udp",
- "udp://user1:passwd1@1.2.3.4:3478?transport=udp",
- "udp://user1:passwd1@1.2.3.4:3478",
+ "turn://user1:passwd1@1.2.3.4?transport=udp",
+ "turn://user1:passwd1@1.2.3.4:3478",
} {
testName := fmt.Sprintf("TestStunner_NewDefaultConfig_URI:%s", conf)
t.Run(testName, func(t *testing.T) {
@@ -49,7 +52,7 @@ func TestStunnerDefaultServerVNet(t *testing.T) {
// patch in the loglevel
c.Admin.LogLevel = stunnerTestLoglevel
- checkDefaultConfig(t, c, "UDP")
+ checkDefaultConfig(t, c, "TURN-UDP")
// patch in the vnet
log.Debug("building virtual network")
@@ -64,7 +67,7 @@ func TestStunnerDefaultServerVNet(t *testing.T) {
})
log.Debug("starting stunnerd")
- assert.NoError(t, stunner.Reconcile(*c), "starting server")
+ assert.NoError(t, stunner.Reconcile(c), "starting server")
log.Debug("creating a client")
lconn, err := v.wan.ListenPacket("udp4", "0.0.0.0:0")
@@ -104,12 +107,12 @@ func TestStunnerConfigFileRoundTrip(t *testing.T) {
// patch in the loglevel
c.Admin.LogLevel = stunnerTestLoglevel
- checkDefaultConfig(t, c, "UDP")
+ checkDefaultConfig(t, c, "TURN-UDP")
file, err2 := yaml.Marshal(c)
assert.NoError(t, err2, "marschal config fike")
- newConf := &v1alpha1.StunnerConfig{}
+ newConf := &stnrv1.StunnerConfig{}
err = yaml.Unmarshal(file, newConf)
assert.NoError(t, err, "unmarschal config from file")
@@ -135,7 +138,7 @@ func TestStunnerConfigFileWatcher(t *testing.T) {
log.Debug("creating a temp file for config")
f, err := os.CreateTemp("", "stunner_conf_*.yaml")
assert.NoError(t, err, "creating temp config file")
- // we just need the filename for now so we remove the fle first
+ // we just need the filename for now so we remove the file first
file := f.Name()
assert.NoError(t, os.Remove(file), "removing temp config file")
@@ -143,17 +146,15 @@ func TestStunnerConfigFileWatcher(t *testing.T) {
stunner := NewStunner(Options{LogLevel: stunnerTestLoglevel})
log.Debug("starting watcher")
- conf := make(chan v1alpha1.StunnerConfig, 1)
+ conf := make(chan *stnrv1.StunnerConfig, 1)
defer close(conf)
log.Debug("init watcher with nonexistent config file")
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
- err = WatchConfig(ctx, Watcher{
- ConfigFile: file,
- ConfigChannel: conf,
- Logger: loggerFactory,
- })
+
+ url := "file://" + file
+ err = stunner.WatchConfig(ctx, url, conf, false)
assert.NoError(t, err, "creating config watcher")
// nothing should happen here: wait a bit so that the watcher has comfortable time to start
@@ -183,14 +184,15 @@ func TestStunnerConfigFileWatcher(t *testing.T) {
_, err = f.Write(y)
assert.NoError(t, err, "write config to temp file")
- // wait a bit so that the watcher has time to react
- time.Sleep(50 * time.Millisecond)
+ // // wait a bit so that the watcher has time to react
+ // time.Sleep(50 * time.Millisecond)
- // read back result
- c2 := <-conf
- checkDefaultConfig(t, &c2, "UDP")
+ c2, ok := <-conf
+ assert.True(t, ok, "config emitted")
+ checkDefaultConfig(t, c2, "TURN-UDP")
+
+ log.Debug("write a wrong config file: WatchConfig validates")
- log.Debug("write a wrong config file (WatchConfig does not validate)")
c2.Listeners[0].Protocol = "dummy"
y, err = yaml.Marshal(c2)
assert.NoError(t, err, "marshal config file")
@@ -204,15 +206,20 @@ func TestStunnerConfigFileWatcher(t *testing.T) {
// this makes sure that we do not share anything with ConfigWatch
c2.Listeners[0].PublicAddr = "AAAAAAAAAAAAAa"
- // wait a bit so that the watcher has time to react
+ // we should not read anything so that channel should not br redable
time.Sleep(50 * time.Millisecond)
-
- c3 := <-conf
- checkDefaultConfig(t, &c3, "dummy")
+ readable := false
+ select {
+ case _, ok := <-conf:
+ readable = ok
+ default:
+ readable = false
+ }
+ assert.False(t, readable, "wrong config file does not trigger a watch event")
log.Debug("update the config file and check")
- c3.Listeners[0].Protocol = "TCP"
- y, err = yaml.Marshal(c3)
+ c2.Listeners[0].Protocol = "TURN-TCP"
+ y, err = yaml.Marshal(c2)
assert.NoError(t, err, "marshal config file")
err = f.Truncate(0)
assert.NoError(t, err, "truncate temp file")
@@ -221,18 +228,327 @@ func TestStunnerConfigFileWatcher(t *testing.T) {
_, err = f.Write(y)
assert.NoError(t, err, "write config to temp file")
- // wait a bit so that the watcher has time to react
+ c3 := <-conf
+ checkDefaultConfig(t, c3, "TURN-TCP")
+
+ stunner.Close()
+}
+
+const (
+ testConfigV1 = `{"version":"v1","admin":{"name":"ns1/tester", "loglevel":"all:ERROR"},"auth":{"type":"static","credentials":{"password":"passwd1","username":"user1"}},"listeners":[{"name":"udp","protocol":"turn-udp","address":"1.2.3.4","port":3478,"routes":["echo-server-cluster"]}],"clusters":[{"name":"echo-server-cluster","type":"STATIC","endpoints":["1.2.3.5"]}]}`
+ testConfigV1A1 = `{"version":"v1alpha1","admin":{"name":"ns1/tester", "loglevel":"all:ERROR"},"auth":{"type":"ephemeral","credentials":{"secret":"test-secret"}},"listeners":[{"name":"udp","protocol":"turn-udp","address":"1.2.3.4","port":3478,"routes":["echo-server-cluster"]}],"clusters":[{"name":"echo-server-cluster","type":"STATIC","endpoints":["1.2.3.5"]}]}`
+)
+
+// test with v1alpha1 and v1
+func TestStunnerConfigFileWatcherMultiVersion(t *testing.T) {
+ lim := test.TimeOut(time.Second * 10)
+ defer lim.Stop()
+
+ loggerFactory := logger.NewLoggerFactory(stunnerTestLoglevel)
+ log := loggerFactory.NewLogger("test-watcher")
+
+ testName := "TestStunnerConfigFileWatcher"
+ log.Debugf("-------------- Running test: %s -------------", testName)
+
+ log.Debug("creating a temp file for config")
+ f, err := os.CreateTemp("", "stunner_conf_*.yaml")
+ assert.NoError(t, err, "creating temp config file")
+ // we just need the filename for now so we remove the file first
+ file := f.Name()
+ assert.NoError(t, os.Remove(file), "removing temp config file")
+
+ log.Debug("creating a stunnerd")
+ stunner := NewStunner(Options{LogLevel: stunnerTestLoglevel})
+
+ log.Debug("starting watcher")
+ conf := make(chan *stnrv1.StunnerConfig, 1)
+ defer close(conf)
+
+ log.Debug("init watcher with nonexistent config file")
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ url := "file://" + file
+ err = stunner.WatchConfig(ctx, url, conf, false)
+ assert.NoError(t, err, "creating config watcher")
+
+ // nothing should happen here: wait a bit so that the watcher has comfortable time to start
+ time.Sleep(50 * time.Millisecond)
+
+ log.Debug("write v1 config and check")
+
+ // recreate the temp file and write config
+ f, err = os.OpenFile(file, os.O_RDWR|os.O_CREATE, 0644)
+ assert.NoError(t, err, "recreate temp config file")
+ defer os.Remove(file)
+
+ err = f.Truncate(0)
+ assert.NoError(t, err, "truncate temp file")
+ _, err = f.Seek(0, 0)
+ assert.NoError(t, err, "seek temp file")
+ _, err = f.WriteString(testConfigV1)
+ assert.NoError(t, err, "write config to temp file")
+
+ c2, ok := <-conf
+ assert.True(t, ok, "config emitted")
+
+ assert.Equal(t, stnrv1.ApiVersion, c2.ApiVersion, "version")
+ assert.Equal(t, "all:ERROR", c2.Admin.LogLevel, "loglevel")
+ assert.True(t, c2.Auth.Type == "static" || c2.Auth.Type == "ephemeral", "loglevel")
+ assert.Len(t, c2.Listeners, 1, "listeners len")
+ assert.Equal(t, "udp", c2.Listeners[0].Name, "listener name")
+ assert.Equal(t, "TURN-UDP", c2.Listeners[0].Protocol, "listener proto")
+ assert.Equal(t, 3478, c2.Listeners[0].Port, "listener port")
+ assert.Len(t, c2.Listeners[0].Routes, 1, "routes len")
+ assert.Equal(t, "echo-server-cluster", c2.Listeners[0].Routes[0], "route name")
+ assert.Len(t, c2.Clusters, 1, "clusters len")
+ assert.Equal(t, "echo-server-cluster", c2.Clusters[0].Name, "cluster name")
+ assert.Equal(t, "STATIC", c2.Clusters[0].Type, "cluster proto")
+ assert.Len(t, c2.Clusters[0].Endpoints, 1, "endpoints len")
+ assert.Equal(t, "1.2.3.5", c2.Clusters[0].Endpoints[0], "cluster port")
+
+ err = f.Truncate(0)
+ assert.NoError(t, err, "truncate temp file")
+ _, err = f.Seek(0, 0)
+ assert.NoError(t, err, "seek temp file")
+ _, err = f.WriteString(testConfigV1A1)
+ assert.NoError(t, err, "write config to temp file")
+
+ c2, ok = <-conf
+ assert.True(t, ok, "config emitted")
+
+ assert.Equal(t, stnrv1.ApiVersion, c2.ApiVersion, "version")
+ assert.Equal(t, "all:ERROR", c2.Admin.LogLevel, "loglevel")
+ assert.True(t, c2.Auth.Type == "static" || c2.Auth.Type == "ephemeral", "loglevel")
+ assert.Len(t, c2.Listeners, 1, "listeners len")
+ assert.Equal(t, "udp", c2.Listeners[0].Name, "listener name")
+ assert.Equal(t, "TURN-UDP", c2.Listeners[0].Protocol, "listener proto")
+ assert.Equal(t, 3478, c2.Listeners[0].Port, "listener port")
+ assert.Len(t, c2.Listeners[0].Routes, 1, "routes len")
+ assert.Equal(t, "echo-server-cluster", c2.Listeners[0].Routes[0], "route name")
+ assert.Len(t, c2.Clusters, 1, "clusters len")
+ assert.Equal(t, "echo-server-cluster", c2.Clusters[0].Name, "cluster name")
+ assert.Equal(t, "STATIC", c2.Clusters[0].Type, "cluster proto")
+ assert.Len(t, c2.Clusters[0].Endpoints, 1, "endpoints len")
+ assert.Equal(t, "1.2.3.5", c2.Clusters[0].Endpoints[0], "cluster port")
+
+ stunner.Close()
+}
+
+func TestStunnerConfigPollerMultiVersion(t *testing.T) {
+ lim := test.TimeOut(time.Second * 10)
+ defer lim.Stop()
+
+ loggerFactory := logger.NewLoggerFactory(stunnerTestLoglevel)
+ log := loggerFactory.NewLogger("test-poller")
+
+ testName := "TestStunnerConfigPoller"
+ log.Debugf("-------------- Running test: %s -------------", testName)
+
+ log.Debug("creating a mock CDS server")
+ addr := "localhost:63479"
+ origin := "ws://" + addr
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ s := &http.Server{Addr: addr}
+ defer s.Close()
+
+ http.HandleFunc("/api/v1/configs/ns1/tester",
+ func(w http.ResponseWriter, req *http.Request) {
+ upgrader := websocket.Upgrader{
+ ReadBufferSize: 1024,
+ WriteBufferSize: 1024,
+ }
+
+ conn, err := upgrader.Upgrade(w, req, nil)
+ assert.NoError(t, err, "upgrade HTTP connection")
+ defer func() { _ = conn.Close() }()
+
+ // for the pong handler: conn.Close() will kill this
+ go func() {
+ for {
+ _, _, err := conn.ReadMessage()
+ if err != nil {
+ return
+ }
+ }
+ }()
+
+ conn.SetPingHandler(func(string) error {
+ return conn.WriteMessage(websocket.PongMessage, []byte("keepalive"))
+ })
+
+ // send v1config
+ assert.NoError(t, conn.WriteMessage(websocket.TextMessage, []byte(testConfigV1)), "write config v1")
+
+ // send v1config
+ assert.NoError(t, conn.WriteMessage(websocket.TextMessage, []byte(testConfigV1A1)), "write config v1alpha1")
+
+ select {
+ case <-ctx.Done():
+ case <-req.Context().Done():
+ }
+
+ conn.Close()
+ })
+
+ // serve
+ go func() {
+ _ = s.ListenAndServe()
+ }()
+
+ // wait a bit so that the server has time to setup
time.Sleep(50 * time.Millisecond)
- // read back result
- c4 := <-conf
- checkDefaultConfig(t, &c4, "TCP")
+ log.Debug("creating a stunnerd")
+ stunner := NewStunner(Options{LogLevel: stunnerTestLoglevel, Name: "ns1/tester"})
+
+ log.Debug("starting watcher")
+ conf := make(chan *stnrv1.StunnerConfig, 1)
+ defer close(conf)
+
+ log.Debug("init config poller")
+ assert.NoError(t, stunner.WatchConfig(ctx, origin, conf, true), "creating config poller")
+
+ c2, ok := <-conf
+ assert.True(t, ok, "config emitted")
+
+ assert.Equal(t, stnrv1.ApiVersion, c2.ApiVersion, "version")
+ assert.Equal(t, "all:ERROR", c2.Admin.LogLevel, "loglevel")
+ assert.True(t, c2.Auth.Type == "static" || c2.Auth.Type == "ephemeral", "loglevel")
+ assert.Len(t, c2.Listeners, 1, "listeners len")
+ assert.Equal(t, "udp", c2.Listeners[0].Name, "listener name")
+ assert.Equal(t, "TURN-UDP", c2.Listeners[0].Protocol, "listener proto")
+ assert.Equal(t, 3478, c2.Listeners[0].Port, "listener port")
+ assert.Len(t, c2.Listeners[0].Routes, 1, "routes len")
+ assert.Equal(t, "echo-server-cluster", c2.Listeners[0].Routes[0], "route name")
+ assert.Len(t, c2.Clusters, 1, "clusters len")
+ assert.Equal(t, "echo-server-cluster", c2.Clusters[0].Name, "cluster name")
+ assert.Equal(t, "STATIC", c2.Clusters[0].Type, "cluster proto")
+ assert.Len(t, c2.Clusters[0].Endpoints, 1, "endpoints len")
+ assert.Equal(t, "1.2.3.5", c2.Clusters[0].Endpoints[0], "cluster port")
+
+ // next read yields a v1alpha1 config
+ c2, ok = <-conf
+ assert.True(t, ok, "config emitted")
+
+ assert.Equal(t, stnrv1.ApiVersion, c2.ApiVersion, "version")
+ assert.Equal(t, "all:ERROR", c2.Admin.LogLevel, "loglevel")
+ assert.True(t, c2.Auth.Type == "static" || c2.Auth.Type == "ephemeral", "loglevel")
+ assert.Len(t, c2.Listeners, 1, "listeners len")
+ assert.Equal(t, "udp", c2.Listeners[0].Name, "listener name")
+ assert.Equal(t, "TURN-UDP", c2.Listeners[0].Protocol, "listener proto")
+ assert.Equal(t, 3478, c2.Listeners[0].Port, "listener port")
+ assert.Len(t, c2.Listeners[0].Routes, 1, "routes len")
+ assert.Equal(t, "echo-server-cluster", c2.Listeners[0].Routes[0], "route name")
+ assert.Len(t, c2.Clusters, 1, "clusters len")
+ assert.Equal(t, "echo-server-cluster", c2.Clusters[0].Name, "cluster name")
+ assert.Equal(t, "STATIC", c2.Clusters[0].Type, "cluster proto")
+ assert.Len(t, c2.Clusters[0].Endpoints, 1, "endpoints len")
+ assert.Equal(t, "1.2.3.5", c2.Clusters[0].Endpoints[0], "cluster port")
stunner.Close()
}
-func checkDefaultConfig(t *testing.T, c *v1alpha1.StunnerConfig, proto string) {
- assert.Equal(t, "plaintext", c.Auth.Type, "auth-type")
+func TestStunnerURIParser(t *testing.T) {
+ lim := test.TimeOut(time.Second * 30)
+ defer lim.Stop()
+
+ report := test.CheckRoutines(t)
+ defer report()
+
+ // loggerFactory := logger.NewLoggerFactory("all:TRACE")
+ loggerFactory := logger.NewLoggerFactory(stunnerTestLoglevel)
+ log := loggerFactory.NewLogger("test")
+
+ for _, conf := range []struct {
+ uri string
+ su StunnerUri
+ }{
+ // udp
+ {"turn://user1:passwd1@1.2.3.4:3478?transport=udp", StunnerUri{"turn-udp", "1.2.3.4", "user1", "passwd1", 3478, nil}},
+ {"turn://user1:passwd1@1.2.3.4?transport=udp", StunnerUri{"turn-udp", "1.2.3.4", "user1", "passwd1", 3478, nil}},
+ {"turn://user1:passwd1@1.2.3.4:3478", StunnerUri{"turn-udp", "1.2.3.4", "user1", "passwd1", 3478, nil}},
+ // tcp
+ {"turn://user1:passwd1@1.2.3.4:3478?transport=tcp", StunnerUri{"turn-tcp", "1.2.3.4", "user1", "passwd1", 3478, nil}},
+ {"turn://user1:passwd1@1.2.3.4?transport=tcp", StunnerUri{"turn-tcp", "1.2.3.4", "user1", "passwd1", 3478, nil}},
+ // tls - old style
+ {"turn://user1:passwd1@1.2.3.4:3478?transport=tls", StunnerUri{"turn-tls", "1.2.3.4", "user1", "passwd1", 3478, nil}},
+ {"turn://user1:passwd1@1.2.3.4?transport=tls", StunnerUri{"turn-tls", "1.2.3.4", "user1", "passwd1", 443, nil}},
+ // tls - RFC style
+ {"turns://user1:passwd1@1.2.3.4:3478?transport=tcp", StunnerUri{"turn-tls", "1.2.3.4", "user1", "passwd1", 3478, nil}},
+ {"turns://user1:passwd1@1.2.3.4?transport=tcp", StunnerUri{"turn-tls", "1.2.3.4", "user1", "passwd1", 443, nil}},
+ // dtls - old style
+ {"turn://user1:passwd1@1.2.3.4:3478?transport=dtls", StunnerUri{"turn-dtls", "1.2.3.4", "user1", "passwd1", 3478, nil}},
+ {"turn://user1:passwd1@1.2.3.4?transport=dtls", StunnerUri{"turn-dtls", "1.2.3.4", "user1", "passwd1", 443, nil}},
+ // dtls - RFC style
+ {"turns://user1:passwd1@1.2.3.4:3478?transport=udp", StunnerUri{"turn-dtls", "1.2.3.4", "user1", "passwd1", 3478, nil}},
+ {"turns://user1:passwd1@1.2.3.4?transport=udp", StunnerUri{"turn-dtls", "1.2.3.4", "user1", "passwd1", 443, nil}},
+ // no cred
+ {"turn://1.2.3.4:3478?transport=udp", StunnerUri{"turn-udp", "1.2.3.4", "", "", 3478, nil}},
+ {"turn://1.2.3.4?transport=udp", StunnerUri{"turn-udp", "1.2.3.4", "", "", 3478, nil}},
+ {"turn://1.2.3.4", StunnerUri{"turn-udp", "1.2.3.4", "", "", 3478, nil}},
+ } {
+ testName := fmt.Sprintf("TestStunnerURIParser:%s", conf.uri)
+ t.Run(testName, func(t *testing.T) {
+ log.Debugf("-------------- Running test: %s -------------", testName)
+ u, err := ParseUri(conf.uri)
+ assert.NoError(t, err, "URI parser")
+ assert.Equal(t, strings.ToLower(conf.su.Protocol), strings.ToLower(u.Protocol), "uri protocol")
+ assert.Equal(t, conf.su.Address, u.Address, "uri address")
+ assert.Equal(t, conf.su.Username, u.Username, "uri username")
+ assert.Equal(t, conf.su.Password, u.Password, "uri password")
+ assert.Equal(t, conf.su.Port, u.Port, "uri port")
+ })
+ }
+}
+
+// make sure credentials are excempt from env-substitution in ParseConfig
+func TestCredentialParser(t *testing.T) {
+ lim := test.TimeOut(time.Second * 30)
+ defer lim.Stop()
+
+ report := test.CheckRoutines(t)
+ defer report()
+
+ loggerFactory := logger.NewLoggerFactory(stunnerTestLoglevel)
+ log := loggerFactory.NewLogger("test")
+
+ for _, testConf := range []struct {
+ name string
+ config []byte
+ user, pass, secret string
+ }{
+ {"plain", []byte(`{"version":"v1","admin":{"name":"ns1/tester"},"auth":{"type":"static","credentials":{"password":"pass","username":"user"}}}`), "user", "pass", ""},
+ // user name with $
+ {"username_with_leading_$", []byte(`{"version":"v1","admin":{"name":"ns1/tester"},"auth":{"type":"static","credentials":{"password":"pass","username":"$user"}}}`), "$user", "pass", ""},
+ {"username_with_trailing_$", []byte(`{"version":"v1","admin":{"name":"ns1/tester"},"auth":{"type":"static","credentials":{"password":"pass","username":"user$"}}}`), "user$", "pass", ""},
+ {"username_with_$", []byte(`{"version":"v1","admin":{"name":"ns1/tester"},"auth":{"type":"static","credentials":{"password":"pass","username":"us$er"}}}`), "us$er", "pass", ""},
+ // passwd with $
+ {"passwd_with_leading_$", []byte(`{"version":"v1","admin":{"name":"ns1/tester"},"auth":{"type":"static","credentials":{"password":"$pass","username":"user"}}}`), "user", "$pass", ""},
+ {"passwd_with_trailing_$", []byte(`{"version":"v1","admin":{"name":"ns1/tester"},"auth":{"type":"static","credentials":{"password":"pass$","username":"user"}}}`), "user", "pass$", ""},
+ {"passwd_with_$", []byte(`{"version":"v1","admin":{"name":"ns1/tester"},"auth":{"type":"static","credentials":{"password":"pa$ss","username":"user"}}}`), "user", "pa$ss", ""},
+ // secret with $
+ {"secret_with_leading_$", []byte(`{"version":"v1","admin":{"name":"ns1/tester"},"auth":{"type":"static","credentials":{"secret":"$secret","username":"user"}}}`), "user", "", "$secret"},
+ {"secret_with_trailing_$", []byte(`{"version":"v1","admin":{"name":"ns1/tester"},"auth":{"type":"static","credentials":{"secret":"secret$","username":"user"}}}`), "user", "", "secret$"},
+ {"secret_with_$", []byte(`{"version":"v1","admin":{"name":"ns1/tester"},"auth":{"type":"static","credentials":{"secret":"sec$ret","username":"user"}}}`), "user", "", "sec$ret"},
+ } {
+ testName := fmt.Sprintf("TestCredentialParser:%s", testConf.name)
+ t.Run(testName, func(t *testing.T) {
+ log.Debugf("-------------- Running test: %s -------------", testName)
+ c, err := cdsclient.ParseConfig(testConf.config)
+ assert.NoError(t, err, "parser")
+ assert.Equal(t, testConf.user, c.Auth.Credentials["username"], "username")
+ assert.Equal(t, testConf.pass, c.Auth.Credentials["password"], "password")
+ assert.Equal(t, testConf.secret, c.Auth.Credentials["secret"], "secret")
+ })
+ }
+}
+
+func checkDefaultConfig(t *testing.T, c *stnrv1.StunnerConfig, proto string) {
+ assert.Equal(t, "static", c.Auth.Type, "auth-type")
assert.Equal(t, "user1", c.Auth.Credentials["username"], "username")
assert.Equal(t, "passwd1", c.Auth.Credentials["password"], "passwd")
assert.Len(t, c.Listeners, 1, "listeners len")
diff --git a/deploy/manifests/default-dataplane.yaml b/deploy/manifests/default-dataplane.yaml
new file mode 100644
index 00000000..a3fcfb52
--- /dev/null
+++ b/deploy/manifests/default-dataplane.yaml
@@ -0,0 +1,22 @@
+apiVersion: stunner.l7mp.io/v1
+kind: Dataplane
+metadata:
+ name: default
+spec:
+ replicas: 1
+ image: l7mp/stunnerd:dev
+ imagePullPolicy: Always
+ command:
+ - "stunnerd"
+ args:
+ - "-w"
+ - "--udp-thread-num=1"
+ hostNetwork: false
+ resources:
+ limits:
+ cpu: 250m
+ memory: 120Mi
+ requests:
+ cpu: 250m
+ memory: 120Mi
+ terminationGracePeriodSeconds: 3600
diff --git a/deploy/manifests/static/dataplane.yaml b/deploy/manifests/static/dataplane.yaml
new file mode 100644
index 00000000..4d59c0f9
--- /dev/null
+++ b/deploy/manifests/static/dataplane.yaml
@@ -0,0 +1,23 @@
+---
+apiVersion: stunner.l7mp.io/v1
+kind: Dataplane
+metadata:
+ name: default
+spec:
+ replicas: 1
+ image: l7mp/stunnerd:latest
+ imagePullPolicy: IfNotPresent
+ command:
+ - stunnerd
+ args:
+ - -w
+ - --udp-thread-num=16
+ resources:
+ limits:
+ cpu: 2
+ memory: 512Mi
+ requests:
+ cpu: 500m
+ memory: 128Mi
+ terminationGracePeriodSeconds: 3600
+ hostNetwork: false
diff --git a/deploy/manifests/static/gateway-api-crd.yaml b/deploy/manifests/static/gateway-api-crd.yaml
index 60e9d1c6..9a8c9e04 100644
--- a/deploy/manifests/static/gateway-api-crd.yaml
+++ b/deploy/manifests/static/gateway-api-crd.yaml
@@ -3,8 +3,8 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538
- gateway.networking.k8s.io/bundle-version: v0.6.2
+ api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2466
+ gateway.networking.k8s.io/bundle-version: v1.0.0
gateway.networking.k8s.io/channel: experimental
creationTimestamp: null
name: gatewayclasses.gateway.networking.k8s.io
@@ -35,10 +35,7 @@ spec:
name: Description
priority: 1
type: string
- # deprecated: true
- # deprecationWarning: The v1alpha2 version of GatewayClass has been deprecated and
- # will be removed in a future release of the API. Please upgrade to v1beta1.
- name: v1alpha2
+ name: v1
schema:
openAPIV3Schema:
description: "GatewayClass describes a class of Gateways available to the
@@ -50,7 +47,7 @@ spec:
to GatewayClass or associated parameters. If implementations choose to propagate
GatewayClass changes to existing Gateways, that MUST be clearly documented
by the implementation. \n Whenever one or more Gateways are using a GatewayClass,
- implementations MUST add the `gateway-exists-finalizer.gateway.networking.k8s.io`
+ 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. \n GatewayClass is
a Cluster level resource."
@@ -79,6 +76,9 @@ spec:
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
@@ -133,7 +133,9 @@ spec:
reason: Waiting
status: Unknown
type: Accepted
- description: Status defines the current state of GatewayClass.
+ description: "Status defines the current state of GatewayClass. \n Implementations
+ MUST populate status on all GatewayClass resources which specify their
+ controller name."
properties:
conditions:
default:
@@ -149,14 +151,12 @@ spec:
description: "Condition contains details for one aspect of the current
state of this API Resource. --- This struct is intended for direct
use as an array at the field path .status.conditions. For example,
- \n \ttype FooStatus struct{ \t // Represents the observations
- of a foo's current state. \t // Known .status.conditions.type
- are: \"Available\", \"Progressing\", and \"Degraded\" \t //
- +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map
- \t // +listMapKey=type \t Conditions []metav1.Condition
+ \n type FooStatus struct{ // Represents the observations of a
+ foo's current state. // Known .status.conditions.type are: \"Available\",
+ \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
+ // +listType=map // +listMapKey=type Conditions []metav1.Condition
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
- protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields
- \t}"
+ protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
@@ -218,6 +218,35 @@ spec:
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. '
+ items:
+ description: SupportedFeature is used to describe distinct features
+ that are covered by conformance tests.
+ enum:
+ - Gateway
+ - GatewayPort8080
+ - GatewayStaticAddresses
+ - HTTPRoute
+ - HTTPRouteDestinationPortMatching
+ - HTTPRouteHostRewrite
+ - HTTPRouteMethodMatching
+ - HTTPRoutePathRedirect
+ - HTTPRoutePathRewrite
+ - HTTPRoutePortRedirect
+ - HTTPRouteQueryParamMatching
+ - HTTPRouteRequestMirror
+ - HTTPRouteRequestMultipleMirrors
+ - HTTPRouteResponseHeaderModification
+ - HTTPRouteSchemeRedirect
+ - Mesh
+ - ReferenceGrant
+ - TLSRoute
+ type: string
+ maxItems: 64
+ type: array
+ x-kubernetes-list-type: set
type: object
required:
- spec
@@ -252,7 +281,7 @@ spec:
to GatewayClass or associated parameters. If implementations choose to propagate
GatewayClass changes to existing Gateways, that MUST be clearly documented
by the implementation. \n Whenever one or more Gateways are using a GatewayClass,
- implementations MUST add the `gateway-exists-finalizer.gateway.networking.k8s.io`
+ 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. \n GatewayClass is
a Cluster level resource."
@@ -281,6 +310,9 @@ spec:
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
@@ -335,7 +367,9 @@ spec:
reason: Waiting
status: Unknown
type: Accepted
- description: Status defines the current state of GatewayClass.
+ description: "Status defines the current state of GatewayClass. \n Implementations
+ MUST populate status on all GatewayClass resources which specify their
+ controller name."
properties:
conditions:
default:
@@ -351,14 +385,12 @@ spec:
description: "Condition contains details for one aspect of the current
state of this API Resource. --- This struct is intended for direct
use as an array at the field path .status.conditions. For example,
- \n \ttype FooStatus struct{ \t // Represents the observations
- of a foo's current state. \t // Known .status.conditions.type
- are: \"Available\", \"Progressing\", and \"Degraded\" \t //
- +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map
- \t // +listMapKey=type \t Conditions []metav1.Condition
+ \n type FooStatus struct{ // Represents the observations of a
+ foo's current state. // Known .status.conditions.type are: \"Available\",
+ \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
+ // +listType=map // +listMapKey=type Conditions []metav1.Condition
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
- protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields
- \t}"
+ protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
@@ -420,6 +452,35 @@ spec:
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. '
+ items:
+ description: SupportedFeature is used to describe distinct features
+ that are covered by conformance tests.
+ enum:
+ - Gateway
+ - GatewayPort8080
+ - GatewayStaticAddresses
+ - HTTPRoute
+ - HTTPRouteDestinationPortMatching
+ - HTTPRouteHostRewrite
+ - HTTPRouteMethodMatching
+ - HTTPRoutePathRedirect
+ - HTTPRoutePathRewrite
+ - HTTPRoutePortRedirect
+ - HTTPRouteQueryParamMatching
+ - HTTPRouteRequestMirror
+ - HTTPRouteRequestMultipleMirrors
+ - HTTPRouteResponseHeaderModification
+ - HTTPRouteSchemeRedirect
+ - Mesh
+ - ReferenceGrant
+ - TLSRoute
+ type: string
+ maxItems: 64
+ type: array
+ x-kubernetes-list-type: set
type: object
required:
- spec
@@ -432,15 +493,15 @@ status:
acceptedNames:
kind: ""
plural: ""
- conditions: []
- storedVersions: []
+ conditions: null
+ storedVersions: null
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538
- gateway.networking.k8s.io/bundle-version: v0.6.2
+ api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2466
+ gateway.networking.k8s.io/bundle-version: v1.0.0
gateway.networking.k8s.io/channel: experimental
creationTimestamp: null
name: gateways.gateway.networking.k8s.io
@@ -470,10 +531,7 @@ spec:
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
- # deprecated: true
- # deprecationWarning: The v1alpha2 version of Gateway has been deprecated and will
- # be removed in a future release of the API. Please upgrade to v1beta1.
- name: v1alpha2
+ name: v1
schema:
openAPIV3Schema:
description: Gateway represents an instance of a service-traffic handling
@@ -503,17 +561,29 @@ spec:
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. \n The .listener.hostname
- field is used to route traffic that has already arrived at the Gateway
- to the correct in-cluster destination. \n If no Addresses are specified,
- the implementation MAY schedule the Gateway in an implementation-specific
- manner, assigning an appropriate set of Addresses. \n The implementation
- MUST bind all Listeners to every GatewayAddress that it assigns
- to the Gateway and add a corresponding entry in GatewayStatus.Addresses.
- \n Support: Extended"
+ or some other address that traffic will be sent to. \n If no Addresses
+ are specified, the implementation MAY schedule the Gateway in an
+ implementation-specific manner, assigning an appropriate set of
+ Addresses. \n The implementation MUST bind all Listeners to every
+ GatewayAddress that it assigns to the Gateway and add a corresponding
+ entry in GatewayStatus.Addresses. \n Support: Extended \n "
items:
description: GatewayAddress 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
@@ -532,40 +602,154 @@ spec:
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-validations:
+ - message: IPAddress values must be unique
+ rule: 'self.all(a1, a1.type == ''IPAddress'' ? self.exists_one(a2,
+ a2.type == a1.type && a2.value == a1.value) : true )'
+ - message: Hostname values must be unique
+ rule: 'self.all(a1, a1.type == ''Hostname'' ? self.exists_one(a2,
+ a2.type == a1.type && a2.value == a1.value) : true )'
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. \n Support: Core \n "
+ 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. \n 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.
+ \n An implementation may chose to add additional implementation-specific
+ annotations as they see fit. \n Support: Extended"
+ maxProperties: 8
+ type: object
+ labels:
+ 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: "Labels that SHOULD be applied to any resources created
+ in response to this Gateway. \n 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.
+ \n An implementation may chose to add additional implementation-specific
+ labels as they see fit. \n Support: Extended"
+ maxProperties: 8
+ 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. \n Each listener in a Gateway
- must have a unique combination of Hostname, Port, and Protocol.
- \n An implementation MAY group Listeners by Port and then collapse
- each group of Listeners into a single Listener if the implementation
- determines that the Listeners in the group are \"compatible\". An
- implementation MAY also group together and collapse compatible Listeners
- belonging to different Gateways. \n For example, an implementation
- might consider Listeners to be compatible with each other if all
- of the following conditions are met: \n 1. Either each Listener
- within the group specifies the \"HTTP\" Protocol or each Listener
- within the group specifies either the \"HTTPS\" or \"TLS\" Protocol.
- \n 2. Each Listener within the group specifies a Hostname that is
- unique within the group. \n 3. As a special case, one Listener
- within a group may omit Hostname, in which case this Listener
- matches when no other Listener matches. \n If the implementation
- does collapse compatible Listeners, the hostname provided in the
- incoming client request MUST be matched to a Listener to find the
- correct set of Routes. The incoming hostname MUST be matched using
- the Hostname field for each Listener in order of most to least specific.
- That is, exact matches must be processed before wildcard matches.
- \n If this field specifies multiple Listeners that have the same
- Port value but are not compatible, the implementation must raise
- a \"Conflicted\" condition in the Listener status. \n Support: Core"
+ least one Listener MUST be specified. \n 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). \n Practically, this means that each
+ listener in a set MUST have a unique combination of Port, Protocol,
+ and, if supported by the protocol, Hostname. \n Some combinations
+ of port, protocol, and TLS settings are considered Core support
+ and MUST be supported by implementations based on their targeted
+ conformance profile: \n HTTP Profile \n 1. HTTPRoute, Port: 80,
+ Protocol: HTTP 2. HTTPRoute, Port: 443, Protocol: HTTPS, TLS Mode:
+ Terminate, TLS keypair provided \n TLS Profile \n 1. TLSRoute, Port:
+ 443, Protocol: TLS, TLS Mode: Passthrough \n \"Distinct\" Listeners
+ have the following property: \n 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. \n For example, the following
+ Listener scenarios are distinct: \n 1. Multiple Listeners with the
+ same Port that all use the \"HTTP\" Protocol that all have unique
+ Hostname values. 2. Multiple Listeners with the same Port that use
+ either the \"HTTPS\" or \"TLS\" Protocol that all have unique Hostname
+ values. 3. A mixture of \"TCP\" and \"UDP\" Protocol Listeners,
+ where no Listener with the same Protocol has the same Port value.
+ \n Some fields in the Listener struct have possible values that
+ affect whether the Listener is distinct. Hostname is particularly
+ relevant for HTTP or HTTPS protocols. \n When using the Hostname
+ value to select between same-Port, same-Protocol Listeners, the
+ Hostname value must be different on each Listener for the Listener
+ to be distinct. \n When the Listeners are distinct based 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. \n 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 `\"\"`. \n 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. \n 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\"`. \n 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\". \n Implementations MAY choose to accept
+ a Gateway with some Conflicted Listeners only if they only accept
+ the partial Listener set that contains no Conflicted Listeners.
+ To put this another way, implementations may accept a partial Listener
+ set only if they throw out *all* the conflicting Listeners. No picking
+ one of the conflicting listeners as the winner. This also means
+ that the Gateway must have at least one non-conflicting Listener
+ in this case, otherwise it violates the requirement that at least
+ one Listener must be present. \n 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. \n A Gateway's Listeners are considered \"compatible\"
+ if: \n 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. \n 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. \n 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.
+ \n Note that requests SHOULD match at most one Listener. For example,
+ if Listeners are defined for \"foo.example.com\" and \"*.example.com\",
+ a request to \"foo.example.com\" SHOULD only be routed using routes
+ attached to the \"foo.example.com\" Listener (and not the \"*.example.com\"
+ Listener). This concept is known as \"Listener Isolation\". Implementations
+ that do not support Listener Isolation MUST clearly document this.
+ \n Implementations MAY merge separate Gateways onto a single set
+ of Addresses if all Listeners across all Gateways are compatible.
+ \n Support: Core"
items:
description: Listener embodies the concept of a logical endpoint
where a Gateway accepts network connections.
@@ -582,19 +766,18 @@ spec:
determined in order of the following criteria: \n * 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. \n 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.
- \n Support: Core"
+ 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. \n
+ 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. \n Support: Core"
properties:
kinds:
description: "Kinds specifies the groups and kinds of Routes
@@ -639,12 +822,12 @@ spec:
from:
default: Same
description: "From indicates where Routes will be selected
- for this Gateway. Possible values are: * All: Routes
+ for this Gateway. Possible values are: \n * 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. \n
- Support: Core"
+ be used by this Gateway. * Same: Only Routes in the
+ same namespace may be used by this Gateway. \n Support:
+ Core"
enum:
- All
- Selector
@@ -700,6 +883,7 @@ spec:
requirements are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
type: object
type: object
hostname:
@@ -711,19 +895,18 @@ spec:
following protocols: \n * 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. \n 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. \n 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`.
- \n Support: Core"
+ 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. \n 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. \n 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`. \n 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])?)*$
@@ -804,7 +987,7 @@ spec:
kind:
default: Secret
description: Kind is kind of the referent. For example
- "HTTPRoute" or "Service".
+ "Secret".
maxLength: 63
minLength: 1
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
@@ -815,9 +998,10 @@ spec:
minLength: 1
type: string
namespace:
- description: "Namespace is the namespace of the backend.
- When unspecified, the local namespace is inferred.
- \n Note that when a namespace is specified, a ReferenceGrant
+ description: "Namespace is the namespace of the referenced
+ object. When unspecified, the local namespace is
+ inferred. \n 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.
@@ -836,13 +1020,13 @@ spec:
description: "Mode defines the TLS behavior for the TLS
session initiated by the client. There are two possible
modes: \n - Terminate: The TLS session between the downstream
- client and the Gateway is terminated at the Gateway.
- This mode requires certificateRefs to be set and contain
- at least one element. - 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. CertificateRefs
- field is ignored in this mode. \n Support: Core"
+ client and the Gateway is terminated at the Gateway. This
+ mode requires certificateRefs to be set and contain at
+ least one element. - 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. CertificateRefs field is
+ ignored in this mode. \n Support: Core"
enum:
- Terminate
- Passthrough
@@ -869,6 +1053,11 @@ spec:
maxProperties: 16
type: object
type: object
+ x-kubernetes-validations:
+ - message: certificateRefs must be specified when TLSModeType
+ is Terminate
+ rule: 'self.mode == ''Terminate'' ? size(self.certificateRefs)
+ > 0 : true'
required:
- name
- port
@@ -880,6 +1069,24 @@ spec:
x-kubernetes-list-map-keys:
- name
x-kubernetes-list-type: map
+ x-kubernetes-validations:
+ - message: tls must be specified for protocols ['HTTPS', 'TLS']
+ rule: 'self.all(l, l.protocol in [''HTTPS'', ''TLS''] ? has(l.tls)
+ : true)'
+ - 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: 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))))'
required:
- gatewayClassName
- listeners
@@ -889,19 +1096,40 @@ spec:
conditions:
- lastTransitionTime: "1970-01-01T00:00:00Z"
message: Waiting for controller
- reason: NotReconciled
+ 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 IP addresses that have actually been
- bound to the Gateway. These addresses may differ from the addresses
- in the Spec, e.g. if the Gateway automatically assigns an address
- from a reserved pool.
+ description: "Addresses lists the network addresses that have been
+ bound to the Gateway. \n This list may differ from the addresses
+ provided in the spec under some conditions: \n * 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) \n "
items:
- description: GatewayAddress describes an address that can be bound
- to a Gateway.
+ 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
@@ -920,6 +1148,11 @@ spec:
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
conditions:
@@ -939,19 +1172,17 @@ spec:
the `GatewayConditionType` and `GatewayConditionReason` constants
so that operators and tools can converge on a common vocabulary
to describe Gateway state. \n Known condition types are: \n * \"Accepted\"
- * \"Ready\""
+ * \"Programmed\" * \"Ready\""
items:
description: "Condition contains details for one aspect of the current
state of this API Resource. --- This struct is intended for direct
use as an array at the field path .status.conditions. For example,
- \n \ttype FooStatus struct{ \t // Represents the observations
- of a foo's current state. \t // Known .status.conditions.type
- are: \"Available\", \"Progressing\", and \"Degraded\" \t //
- +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map
- \t // +listMapKey=type \t Conditions []metav1.Condition
+ \n type FooStatus struct{ // Represents the observations of a
+ foo's current state. // Known .status.conditions.type are: \"Available\",
+ \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
+ // +listType=map // +listMapKey=type Conditions []metav1.Condition
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
- protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields
- \t}"
+ protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
@@ -1020,8 +1251,23 @@ spec:
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.
+ description: "AttachedRoutes represents the total number of
+ Routes that have been successfully attached to this Listener.
+ \n 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. \n Uses for this
+ field include troubleshooting Route attachment and measuring
+ blast radius/impact of changes to a Listener."
format: int32
type: integer
conditions:
@@ -1031,15 +1277,14 @@ spec:
description: "Condition contains details for one aspect of
the current state of this API Resource. --- This struct
is intended for direct use as an array at the field path
- .status.conditions. For example, \n \ttype FooStatus struct{
- \t // Represents the observations of a foo's current
- state. \t // Known .status.conditions.type are: \"Available\",
- \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type
- \t // +patchStrategy=merge \t // +listType=map \t
- \ // +listMapKey=type \t Conditions []metav1.Condition
+ .status.conditions. For example, \n type FooStatus struct{
+ // Represents the observations of a foo's current state.
+ // Known .status.conditions.type are: \"Available\", \"Progressing\",
+ and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
+ // +listType=map // +listMapKey=type Conditions []metav1.Condition
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
- protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other
- fields \t}"
+ protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields
+ }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
@@ -1204,17 +1449,29 @@ spec:
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. \n The .listener.hostname
- field is used to route traffic that has already arrived at the Gateway
- to the correct in-cluster destination. \n If no Addresses are specified,
- the implementation MAY schedule the Gateway in an implementation-specific
- manner, assigning an appropriate set of Addresses. \n The implementation
- MUST bind all Listeners to every GatewayAddress that it assigns
- to the Gateway and add a corresponding entry in GatewayStatus.Addresses.
- \n Support: Extended"
+ or some other address that traffic will be sent to. \n If no Addresses
+ are specified, the implementation MAY schedule the Gateway in an
+ implementation-specific manner, assigning an appropriate set of
+ Addresses. \n The implementation MUST bind all Listeners to every
+ GatewayAddress that it assigns to the Gateway and add a corresponding
+ entry in GatewayStatus.Addresses. \n Support: Extended \n "
items:
description: GatewayAddress 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
@@ -1233,40 +1490,154 @@ spec:
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-validations:
+ - message: IPAddress values must be unique
+ rule: 'self.all(a1, a1.type == ''IPAddress'' ? self.exists_one(a2,
+ a2.type == a1.type && a2.value == a1.value) : true )'
+ - message: Hostname values must be unique
+ rule: 'self.all(a1, a1.type == ''Hostname'' ? self.exists_one(a2,
+ a2.type == a1.type && a2.value == a1.value) : true )'
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. \n Support: Core \n "
+ 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. \n 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.
+ \n An implementation may chose to add additional implementation-specific
+ annotations as they see fit. \n Support: Extended"
+ maxProperties: 8
+ type: object
+ labels:
+ 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: "Labels that SHOULD be applied to any resources created
+ in response to this Gateway. \n 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.
+ \n An implementation may chose to add additional implementation-specific
+ labels as they see fit. \n Support: Extended"
+ maxProperties: 8
+ 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. \n Each listener in a Gateway
- must have a unique combination of Hostname, Port, and Protocol.
- \n An implementation MAY group Listeners by Port and then collapse
- each group of Listeners into a single Listener if the implementation
- determines that the Listeners in the group are \"compatible\". An
- implementation MAY also group together and collapse compatible Listeners
- belonging to different Gateways. \n For example, an implementation
- might consider Listeners to be compatible with each other if all
- of the following conditions are met: \n 1. Either each Listener
- within the group specifies the \"HTTP\" Protocol or each Listener
- within the group specifies either the \"HTTPS\" or \"TLS\" Protocol.
- \n 2. Each Listener within the group specifies a Hostname that is
- unique within the group. \n 3. As a special case, one Listener
- within a group may omit Hostname, in which case this Listener
- matches when no other Listener matches. \n If the implementation
- does collapse compatible Listeners, the hostname provided in the
- incoming client request MUST be matched to a Listener to find the
- correct set of Routes. The incoming hostname MUST be matched using
- the Hostname field for each Listener in order of most to least specific.
- That is, exact matches must be processed before wildcard matches.
- \n If this field specifies multiple Listeners that have the same
- Port value but are not compatible, the implementation must raise
- a \"Conflicted\" condition in the Listener status. \n Support: Core"
+ least one Listener MUST be specified. \n 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). \n Practically, this means that each
+ listener in a set MUST have a unique combination of Port, Protocol,
+ and, if supported by the protocol, Hostname. \n Some combinations
+ of port, protocol, and TLS settings are considered Core support
+ and MUST be supported by implementations based on their targeted
+ conformance profile: \n HTTP Profile \n 1. HTTPRoute, Port: 80,
+ Protocol: HTTP 2. HTTPRoute, Port: 443, Protocol: HTTPS, TLS Mode:
+ Terminate, TLS keypair provided \n TLS Profile \n 1. TLSRoute, Port:
+ 443, Protocol: TLS, TLS Mode: Passthrough \n \"Distinct\" Listeners
+ have the following property: \n 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. \n For example, the following
+ Listener scenarios are distinct: \n 1. Multiple Listeners with the
+ same Port that all use the \"HTTP\" Protocol that all have unique
+ Hostname values. 2. Multiple Listeners with the same Port that use
+ either the \"HTTPS\" or \"TLS\" Protocol that all have unique Hostname
+ values. 3. A mixture of \"TCP\" and \"UDP\" Protocol Listeners,
+ where no Listener with the same Protocol has the same Port value.
+ \n Some fields in the Listener struct have possible values that
+ affect whether the Listener is distinct. Hostname is particularly
+ relevant for HTTP or HTTPS protocols. \n When using the Hostname
+ value to select between same-Port, same-Protocol Listeners, the
+ Hostname value must be different on each Listener for the Listener
+ to be distinct. \n When the Listeners are distinct based 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. \n 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 `\"\"`. \n 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. \n 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\"`. \n 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\". \n Implementations MAY choose to accept
+ a Gateway with some Conflicted Listeners only if they only accept
+ the partial Listener set that contains no Conflicted Listeners.
+ To put this another way, implementations may accept a partial Listener
+ set only if they throw out *all* the conflicting Listeners. No picking
+ one of the conflicting listeners as the winner. This also means
+ that the Gateway must have at least one non-conflicting Listener
+ in this case, otherwise it violates the requirement that at least
+ one Listener must be present. \n 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. \n A Gateway's Listeners are considered \"compatible\"
+ if: \n 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. \n 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. \n 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.
+ \n Note that requests SHOULD match at most one Listener. For example,
+ if Listeners are defined for \"foo.example.com\" and \"*.example.com\",
+ a request to \"foo.example.com\" SHOULD only be routed using routes
+ attached to the \"foo.example.com\" Listener (and not the \"*.example.com\"
+ Listener). This concept is known as \"Listener Isolation\". Implementations
+ that do not support Listener Isolation MUST clearly document this.
+ \n Implementations MAY merge separate Gateways onto a single set
+ of Addresses if all Listeners across all Gateways are compatible.
+ \n Support: Core"
items:
description: Listener embodies the concept of a logical endpoint
where a Gateway accepts network connections.
@@ -1283,19 +1654,18 @@ spec:
determined in order of the following criteria: \n * 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. \n 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.
- \n Support: Core"
+ 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. \n
+ 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. \n Support: Core"
properties:
kinds:
description: "Kinds specifies the groups and kinds of Routes
@@ -1340,12 +1710,12 @@ spec:
from:
default: Same
description: "From indicates where Routes will be selected
- for this Gateway. Possible values are: * All: Routes
+ for this Gateway. Possible values are: \n * 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. \n
- Support: Core"
+ be used by this Gateway. * Same: Only Routes in the
+ same namespace may be used by this Gateway. \n Support:
+ Core"
enum:
- All
- Selector
@@ -1401,6 +1771,7 @@ spec:
requirements are ANDed.
type: object
type: object
+ x-kubernetes-map-type: atomic
type: object
type: object
hostname:
@@ -1412,19 +1783,18 @@ spec:
following protocols: \n * 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. \n 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. \n 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`.
- \n Support: Core"
+ 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. \n 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. \n 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`. \n 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])?)*$
@@ -1505,7 +1875,7 @@ spec:
kind:
default: Secret
description: Kind is kind of the referent. For example
- "HTTPRoute" or "Service".
+ "Secret".
maxLength: 63
minLength: 1
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
@@ -1516,9 +1886,10 @@ spec:
minLength: 1
type: string
namespace:
- description: "Namespace is the namespace of the backend.
- When unspecified, the local namespace is inferred.
- \n Note that when a namespace is specified, a ReferenceGrant
+ description: "Namespace is the namespace of the referenced
+ object. When unspecified, the local namespace is
+ inferred. \n 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.
@@ -1537,13 +1908,13 @@ spec:
description: "Mode defines the TLS behavior for the TLS
session initiated by the client. There are two possible
modes: \n - Terminate: The TLS session between the downstream
- client and the Gateway is terminated at the Gateway.
- This mode requires certificateRefs to be set and contain
- at least one element. - 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. CertificateRefs
- field is ignored in this mode. \n Support: Core"
+ client and the Gateway is terminated at the Gateway. This
+ mode requires certificateRefs to be set and contain at
+ least one element. - 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. CertificateRefs field is
+ ignored in this mode. \n Support: Core"
enum:
- Terminate
- Passthrough
@@ -1570,6 +1941,11 @@ spec:
maxProperties: 16
type: object
type: object
+ x-kubernetes-validations:
+ - message: certificateRefs must be specified when TLSModeType
+ is Terminate
+ rule: 'self.mode == ''Terminate'' ? size(self.certificateRefs)
+ > 0 : true'
required:
- name
- port
@@ -1581,6 +1957,24 @@ spec:
x-kubernetes-list-map-keys:
- name
x-kubernetes-list-type: map
+ x-kubernetes-validations:
+ - message: tls must be specified for protocols ['HTTPS', 'TLS']
+ rule: 'self.all(l, l.protocol in [''HTTPS'', ''TLS''] ? has(l.tls)
+ : true)'
+ - 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: 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))))'
required:
- gatewayClassName
- listeners
@@ -1590,19 +1984,40 @@ spec:
conditions:
- lastTransitionTime: "1970-01-01T00:00:00Z"
message: Waiting for controller
- reason: NotReconciled
+ 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 IP addresses that have actually been
- bound to the Gateway. These addresses may differ from the addresses
- in the Spec, e.g. if the Gateway automatically assigns an address
- from a reserved pool.
+ description: "Addresses lists the network addresses that have been
+ bound to the Gateway. \n This list may differ from the addresses
+ provided in the spec under some conditions: \n * 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) \n "
items:
- description: GatewayAddress describes an address that can be bound
- to a Gateway.
+ 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
@@ -1621,6 +2036,11 @@ spec:
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
conditions:
@@ -1640,19 +2060,17 @@ spec:
the `GatewayConditionType` and `GatewayConditionReason` constants
so that operators and tools can converge on a common vocabulary
to describe Gateway state. \n Known condition types are: \n * \"Accepted\"
- * \"Ready\""
+ * \"Programmed\" * \"Ready\""
items:
description: "Condition contains details for one aspect of the current
state of this API Resource. --- This struct is intended for direct
use as an array at the field path .status.conditions. For example,
- \n \ttype FooStatus struct{ \t // Represents the observations
- of a foo's current state. \t // Known .status.conditions.type
- are: \"Available\", \"Progressing\", and \"Degraded\" \t //
- +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map
- \t // +listMapKey=type \t Conditions []metav1.Condition
+ \n type FooStatus struct{ // Represents the observations of a
+ foo's current state. // Known .status.conditions.type are: \"Available\",
+ \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
+ // +listType=map // +listMapKey=type Conditions []metav1.Condition
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
- protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields
- \t}"
+ protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
@@ -1721,8 +2139,23 @@ spec:
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.
+ description: "AttachedRoutes represents the total number of
+ Routes that have been successfully attached to this Listener.
+ \n 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. \n Uses for this
+ field include troubleshooting Route attachment and measuring
+ blast radius/impact of changes to a Listener."
format: int32
type: integer
conditions:
@@ -1732,15 +2165,14 @@ spec:
description: "Condition contains details for one aspect of
the current state of this API Resource. --- This struct
is intended for direct use as an array at the field path
- .status.conditions. For example, \n \ttype FooStatus struct{
- \t // Represents the observations of a foo's current
- state. \t // Known .status.conditions.type are: \"Available\",
- \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type
- \t // +patchStrategy=merge \t // +listType=map \t
- \ // +listMapKey=type \t Conditions []metav1.Condition
+ .status.conditions. For example, \n type FooStatus struct{
+ // Represents the observations of a foo's current state.
+ // Known .status.conditions.type are: \"Available\", \"Progressing\",
+ and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
+ // +listType=map // +listMapKey=type Conditions []metav1.Condition
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
- protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other
- fields \t}"
+ protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields
+ }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
@@ -1866,39 +2298,60 @@ status:
acceptedNames:
kind: ""
plural: ""
- conditions: []
- storedVersions: []
+ conditions: null
+ storedVersions: null
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/1538
- gateway.networking.k8s.io/bundle-version: v0.6.2
+ api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2466
+ gateway.networking.k8s.io/bundle-version: v1.0.0
gateway.networking.k8s.io/channel: experimental
creationTimestamp: null
- name: udproutes.gateway.networking.k8s.io
+ name: grpcroutes.gateway.networking.k8s.io
spec:
group: gateway.networking.k8s.io
names:
categories:
- gateway-api
- kind: UDPRoute
- listKind: UDPRouteList
- plural: udproutes
- singular: udproute
+ 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: 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.
+ 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. \n 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.
+ \n 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. \n 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
@@ -1913,37 +2366,122 @@ spec:
metadata:
type: object
spec:
- description: Spec defines the desired state of UDPRoute.
+ 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: \n 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. \n 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:
+ \n * 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.
+ \n 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`. \n 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. \n 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. \n 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: \n * The oldest Route based on creation timestamp.
+ * The Route appearing first in alphabetical order by \"{namespace}/{name}\".
+ \n The rejected Route MUST raise an 'Accepted' condition with a
+ status of 'False' in the corresponding RouteParentStatus. \n 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: \n 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. \n 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`).
+ \n 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
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. \n The only kind of parent resource
- with \"Core\" support is Gateway. This API may be extended in the
- future to support additional kinds of parent resources such as one
- of the route kinds. \n It is invalid to reference an identical parent
- more than once. It is valid to reference multiple distinct sections
- within the same parent resource, such as 2 Listeners within a Gateway.
- \n 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. \n 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 any other
- kind of cross-namespace reference."
+ 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. \n There are two kinds of parent resources with \"Core\"
+ support: \n * Gateway (Gateway conformance profile) * Service (Mesh
+ conformance profile, experimental, ClusterIP Services only) This
+ API may be extended in the future to support additional kinds of
+ parent resources. \n ParentRefs must be _distinct_. This means either
+ that: \n * 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.
+ \n Some examples: \n * 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`. \n 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. \n 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. \n 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. \n 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. \n "
items:
description: "ParentReference identifies an API object (usually
a Gateway) that can be considered a parent of this resource (usually
- a route). The only kind of parent resource with \"Core\" support
- is Gateway. This API may be extended in the future to support
- additional kinds of parent resources, such as HTTPRoute. \n 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."
+ a route). There are two kinds of parent resources with \"Core\"
+ support: \n * Gateway (Gateway conformance profile) * Service
+ (Mesh conformance profile, experimental, ClusterIP Services only)
+ \n This API may be extended in the future to support additional
+ kinds of parent resources. \n 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
@@ -1957,8 +2495,11 @@ spec:
type: string
kind:
default: Gateway
- description: "Kind is kind of the referent. \n Support: Core
- (Gateway) \n Support: Implementation-specific (Other Resources)"
+ description: "Kind is kind of the referent. \n There are two
+ kinds of parent resources with \"Core\" support: \n * Gateway
+ (Gateway conformance profile) * Service (Mesh conformance
+ profile, experimental, ClusterIP Services only) \n Support
+ for other resources is Implementation-Specific."
maxLength: 63
minLength: 1
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
@@ -1978,7 +2519,15 @@ spec:
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.
- \n Support: Core"
+ \n 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.
+ \n 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. \n Support: Core"
maxLength: 63
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
@@ -1993,18 +2542,22 @@ spec:
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. \n Implementations MAY choose
- to support other parent resources. Implementations supporting
- other types of parent resources MUST clearly document how/if
- Port is interpreted. \n 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.
- \n Support: Extended \n "
+ must match both specified values. \n 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. \n Implementations MAY choose to support other parent
+ resources. Implementations supporting other types of parent
+ resources MUST clearly document how/if Port is interpreted.
+ \n 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. \n
+ Support: Extended \n "
format: int32
maximum: 65535
minimum: 1
@@ -2015,19 +2568,23 @@ spec:
interpreted as the following: \n * Gateway: Listener Name.
When both Port (experimental) and SectionName are specified,
the name and port of the selected listener must match both
- specified values. \n Implementations MAY choose to support
- attaching Routes to other resources. If that is the case,
- they MUST clearly document how SectionName is interpreted.
- \n 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.
- \n Support: Core"
+ 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. Note that attaching
+ Routes to Services as Parents is part of experimental Mesh
+ support and is not supported for any other purpose. \n Implementations
+ MAY choose to support attaching Routes to other resources.
+ If that is the case, they MUST clearly document how SectionName
+ is interpreted. \n 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. \n 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])?)*$
@@ -2037,29 +2594,525 @@ spec:
type: object
maxItems: 32
type: array
+ 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.
+ description: Rules are a list of GRPC matchers, filters and actions.
items:
- description: UDPRouteRule is the configuration for a given rule.
+ 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. If unspecified or invalid (refers
- to a non-existent resource or a Service with no endpoints),
- the underlying implementation MUST actively reject connection
- attempts to this backend. Packet drops must respect weight;
- if an invalid backend is requested to have 80% of the packets,
- then 80% of packets must be dropped instead. \n Support: Core
- for Kubernetes Service Support: Implementation-specific for
- any other resource \n Support for weight: Extended"
+ requests should be sent. \n Failure behavior here depends
+ on how many BackendRefs are specified and how many are invalid.
+ \n 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.
+ \n See the GRPCBackendRef definition for the rules about what
+ makes a single GRPCBackendRef invalid. \n 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. \n 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. \n Support: Core for Kubernetes
+ Service \n Support: Implementation-specific for any other
+ resource \n Support for weight: Core"
items:
- description: "BackendRef defines how a Route should forward
- a request to a Kubernetes resource. \n Note that when a
- namespace is specified, a ReferenceGrant object is required
- in the referent namespace to allow that namespace's owner
- to accept the reference. See the ReferenceGrant documentation
- for details."
+ description: "GRPCBackendRef defines how a GRPCRoute forwards
+ a gRPC request. \n 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. \n
+ \n When the BackendRef points to a Kubernetes Service, implementations
+ SHOULD honor the appProtocol field if it is set for the
+ target Service Port. \n Implementations supporting appProtocol
+ SHOULD recognize the Kubernetes Standard Application Protocols
+ defined in KEP-3726. \n 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. \n 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. \n
+ "
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. \n 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. \n Support: Implementation-specific \n
+ 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. \n
+ 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. \n Input: GET /foo HTTP/1.1
+ my-header: foo \n Config: add: - name: \"my-header\"
+ value: \"bar,baz\" \n 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).
+ \n 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).
+ \n Input: GET /foo HTTP/1.1 my-header1: foo
+ my-header2: bar my-header3: baz \n Config:
+ remove: [\"my-header1\", \"my-header3\"] \n
+ 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. \n Input: GET /foo HTTP/1.1 my-header:
+ foo \n Config: set: - name: \"my-header\"
+ value: \"bar\" \n 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).
+ \n 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. \n 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. \n Support: Extended"
+ properties:
+ backendRef:
+ description: "BackendRef references a resource
+ where mirrored requests are sent. \n Mirrored
+ requests must be sent only to a single destination
+ endpoint within this BackendRef, irrespective
+ of how many endpoints are present within this
+ BackendRef. \n 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.
+ \n 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. \n In either error
+ case, the Message of the `ResolvedRefs` Condition
+ should be used to provide more detail about
+ the problem. \n Support: Extended for Kubernetes
+ Service \n 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\".
+ \n Defaults to \"Service\" when not specified.
+ \n 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. \n Support: Core (Services with
+ a type other than ExternalName) \n 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. \n 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. \n 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'
+ required:
+ - backendRef
+ type: object
+ responseHeaderModifier:
+ description: "ResponseHeaderModifier defines a schema
+ for a filter that modifies response headers. \n
+ 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. \n Input: GET /foo HTTP/1.1
+ my-header: foo \n Config: add: - name: \"my-header\"
+ value: \"bar,baz\" \n 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).
+ \n 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).
+ \n Input: GET /foo HTTP/1.1 my-header1: foo
+ my-header2: bar my-header3: baz \n Config:
+ remove: [\"my-header1\", \"my-header3\"] \n
+ 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. \n Input: GET /foo HTTP/1.1 my-header:
+ foo \n Config: set: - name: \"my-header\"
+ value: \"bar\" \n 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).
+ \n 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: \n -
+ 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.
+ \n - Extended: Filter types and their corresponding
+ configuration defined by \"Support: Extended\"
+ in this package, e.g. \"RequestMirror\". Implementers
+ are encouraged to support extended filters. \n
+ - 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.
+ \n Implementers are encouraged to define custom
+ implementation types to extend the core API with
+ implementation-specific behavior. \n 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. \n "
+ 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-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,
@@ -2070,9 +3123,17 @@ spec:
type: string
kind:
default: Service
- description: Kind is kind of the referent. For example
- "HTTPRoute" or "Service". Defaults to "Service" when
- not specified.
+ description: "Kind is the Kubernetes resource kind of
+ the referent. For example \"Service\". \n Defaults to
+ \"Service\" when not specified. \n 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.
+ \n Support: Core (Services with a type other than ExternalName)
+ \n Support: Implementation-specific (Services with type
+ ExternalName)"
maxLength: 63
minLength: 1
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
@@ -2085,11 +3146,11 @@ spec:
namespace:
description: "Namespace is the namespace of the backend.
When unspecified, the local namespace is inferred. \n
- Note that when a namespace is specified, a ReferenceGrant
- object is required in the referent namespace to allow
- that namespace's owner to accept the reference. See
- the ReferenceGrant documentation for details. \n Support:
- Core"
+ 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. \n Support: Core"
maxLength: 63
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
@@ -2127,18 +3188,580 @@ spec:
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
+ filters:
+ description: "Filters define the filters that are applied to
+ requests that match this rule. \n The effects of ordering
+ of multiple behaviors are currently unspecified. This can
+ change in the future based on feedback during the alpha stage.
+ \n Conformance-levels at this level are defined based on the
+ type of filter: \n - 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.
+ \n Specifying the same filter multiple times is not supported
+ unless explicitly indicated in the filter. \n If an implementation
+ can not 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. \n 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. \n Support: Implementation-specific \n 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. \n 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. \n Input: GET /foo HTTP/1.1 my-header: foo
+ \n Config: add: - name: \"my-header\" value: \"bar,baz\"
+ \n 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).
+ \n 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).
+ \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2:
+ bar my-header3: baz \n Config: remove: [\"my-header1\",
+ \"my-header3\"] \n 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. \n
+ Input: GET /foo HTTP/1.1 my-header: foo \n Config:
+ set: - name: \"my-header\" value: \"bar\" \n 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).
+ \n 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. \n 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. \n
+ Support: Extended"
+ properties:
+ backendRef:
+ description: "BackendRef references a resource where
+ mirrored requests are sent. \n Mirrored requests
+ must be sent only to a single destination endpoint
+ within this BackendRef, irrespective of how many
+ endpoints are present within this BackendRef. \n
+ 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. \n 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. \n In either error case, the Message
+ of the `ResolvedRefs` Condition should be used to
+ provide more detail about the problem. \n Support:
+ Extended for Kubernetes Service \n 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\".
+ \n Defaults to \"Service\" when not specified.
+ \n 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. \n Support: Core (Services with a
+ type other than ExternalName) \n 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. \n 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. \n 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'
+ required:
+ - backendRef
+ type: object
+ responseHeaderModifier:
+ description: "ResponseHeaderModifier defines a schema
+ for a filter that modifies response headers. \n 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. \n Input: GET /foo HTTP/1.1 my-header: foo
+ \n Config: add: - name: \"my-header\" value: \"bar,baz\"
+ \n 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).
+ \n 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).
+ \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2:
+ bar my-header3: baz \n Config: remove: [\"my-header1\",
+ \"my-header3\"] \n 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. \n
+ Input: GET /foo HTTP/1.1 my-header: foo \n Config:
+ set: - name: \"my-header\" value: \"bar\" \n 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).
+ \n 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: \n - 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. \n - Extended: Filter types and their
+ corresponding configuration defined by \"Support: Extended\"
+ in this package, e.g. \"RequestMirror\". Implementers
+ are encouraged to support extended filters. \n - 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. \n Implementers are encouraged to
+ define custom implementation types to extend the core
+ API with implementation-specific behavior. \n 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. \n "
+ 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-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. \n For example, take the following matches configuration:
+ \n ``` matches: - method: service: foo.bar headers: values:
+ version: 2 - method: service: foo.bar.v2 ``` \n For a request
+ to match against this rule, it MUST satisfy EITHER of the
+ two conditions: \n - service of foo.bar AND contains the header
+ `version: 2` - service of foo.bar.v2 \n See the documentation
+ for GRPCRouteMatch on how to specify multiple match conditions
+ to be ANDed together. \n If no matches are specified, the
+ implementation MUST match every gRPC request. \n 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: \n * Characters in a matching non-wildcard hostname. *
+ Characters in a matching hostname. * Characters in a matching
+ service. * Characters in a matching method. * Header matches.
+ \n If ties still exist across multiple Routes, matching precedence
+ MUST be determined in order of the following criteria, continuing
+ on ties: \n * The oldest Route based on creation timestamp.
+ * The Route appearing first in alphabetical order by \"{namespace}/{name}\".
+ \n 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. \n For example, the match
+ below will match a gRPC request only if its service is `foo`
+ AND it contains the `version: v1` header: \n ``` matches:
+ - method: type: Exact service: \"foo\" headers: - name:
+ \"version\" value \"v1\" \n ```"
+ 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. \n 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.
+ \n 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.
+ \n 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) \n Support: Implementation-specific
+ (Exact with method specified but no service specified)
+ \n 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: 8
type: array
type: object
maxItems: 16
- minItems: 1
type: array
- required:
- - rules
type: object
status:
- description: Status defines the current state of UDPRoute.
+ description: Status defines the current state of GRPCRoute.
properties:
parents:
description: "Parents is a list of parent resources (usually Gateways)
@@ -2178,15 +3801,14 @@ spec:
description: "Condition contains details for one aspect of
the current state of this API Resource. --- This struct
is intended for direct use as an array at the field path
- .status.conditions. For example, \n \ttype FooStatus struct{
- \t // Represents the observations of a foo's current
- state. \t // Known .status.conditions.type are: \"Available\",
- \"Progressing\", and \"Degraded\" \t // +patchMergeKey=type
- \t // +patchStrategy=merge \t // +listType=map \t
- \ // +listMapKey=type \t Conditions []metav1.Condition
+ .status.conditions. For example, \n type FooStatus struct{
+ // Represents the observations of a foo's current state.
+ // Known .status.conditions.type are: \"Available\", \"Progressing\",
+ and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
+ // +listType=map // +listMapKey=type Conditions []metav1.Condition
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
- protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other
- fields \t}"
+ protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields
+ }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
@@ -2283,9 +3905,11 @@ spec:
type: string
kind:
default: Gateway
- description: "Kind is kind of the referent. \n Support:
- Core (Gateway) \n Support: Implementation-specific (Other
- Resources)"
+ description: "Kind is kind of the referent. \n There are
+ two kinds of parent resources with \"Core\" support: \n
+ * Gateway (Gateway conformance profile) * Service (Mesh
+ conformance profile, experimental, ClusterIP Services
+ only) \n Support for other resources is Implementation-Specific."
maxLength: 63
minLength: 1
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
@@ -2305,7 +3929,16 @@ spec:
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. \n Support: Core"
+ reference. \n 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. \n 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. \n Support:
+ Core"
maxLength: 63
minLength: 1
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
@@ -2321,8 +3954,12 @@ spec:
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. \n Implementations MAY choose to
- support other parent resources. Implementations supporting
+ both specified values. \n 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. \n Implementations MAY choose
+ to support other parent resources. Implementations supporting
other types of parent resources MUST clearly document
how/if Port is interpreted. \n For the purpose of status,
an attachment is considered successful as long as the
@@ -2333,7 +3970,7 @@ spec:
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.
- \n Support: Extended \n "
+ \n Support: Extended \n "
format: int32
maximum: 65535
minimum: 1
@@ -2344,8 +3981,7219 @@ spec:
is interpreted as the following: \n * Gateway: Listener
Name. When both Port (experimental) and SectionName are
specified, the name and port of the selected listener
- must match both specified values. \n Implementations MAY
- choose to support attaching Routes to other resources.
+ 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. Note that attaching Routes to Services
+ as Parents is part of experimental Mesh support and is
+ not supported for any other purpose. \n Implementations
+ MAY choose to support attaching Routes to other resources.
+ If that is the case, they MUST clearly document how SectionName
+ is interpreted. \n 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.
+ \n 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:
+ - controllerName
+ - parentRef
+ type: object
+ maxItems: 32
+ type: array
+ required:
+ - parents
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: null
+ storedVersions: null
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2466
+ gateway.networking.k8s.io/bundle-version: v1.0.0
+ gateway.networking.k8s.io/channel: experimental
+ creationTimestamp: null
+ 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. \n Valid values for Hostnames
+ are determined by RFC 1123 definition of a hostname with 2 notable
+ exceptions: \n 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. \n 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:
+ \n * 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. \n 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`. \n 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. \n 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. \n 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: \n * Characters in a matching
+ non-wildcard hostname. * Characters in a matching hostname. \n If
+ ties exist across multiple Routes, the matching precedence rules
+ for HTTPRouteMatches takes over. \n 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: \n 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. \n 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`).
+ \n 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
+ 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. \n There are two kinds of parent resources with \"Core\"
+ support: \n * Gateway (Gateway conformance profile) * Service (Mesh
+ conformance profile, experimental, ClusterIP Services only) This
+ API may be extended in the future to support additional kinds of
+ parent resources. \n ParentRefs must be _distinct_. This means either
+ that: \n * 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.
+ \n Some examples: \n * 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`. \n 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. \n 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. \n 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. \n 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. \n "
+ 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: \n * Gateway (Gateway conformance profile) * Service
+ (Mesh conformance profile, experimental, ClusterIP Services only)
+ \n This API may be extended in the future to support additional
+ kinds of parent resources. \n 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). \n 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. \n There are two
+ kinds of parent resources with \"Core\" support: \n * Gateway
+ (Gateway conformance profile) * Service (Mesh conformance
+ profile, experimental, ClusterIP Services only) \n 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. \n 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.
+ \n 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.
+ \n 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.
+ \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose to support other parent
+ resources. Implementations supporting other types of parent
+ resources MUST clearly document how/if Port is interpreted.
+ \n 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. \n
+ Support: Extended \n "
+ 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: \n * 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. Note that attaching
+ Routes to Services as Parents is part of experimental Mesh
+ support and is not supported for any other purpose. \n Implementations
+ MAY choose to support attaching Routes to other resources.
+ If that is the case, they MUST clearly document how SectionName
+ is interpreted. \n 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. \n 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-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. \n Failure behavior here depends
+ on how many BackendRefs are specified and how many are invalid.
+ \n 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. \n
+ See the HTTPBackendRef definition for the rules about what
+ makes a single HTTPBackendRef invalid. \n 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.
+ \n 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.
+ \n Support: Core for Kubernetes Service \n Support: Extended
+ for Kubernetes ServiceImport \n Support: Implementation-specific
+ for any other resource \n Support for weight: Core"
+ items:
+ description: "HTTPBackendRef defines how a HTTPRoute forwards
+ a HTTP request. \n 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. \n
+ \n When the BackendRef points to a Kubernetes Service, implementations
+ SHOULD honor the appProtocol field if it is set for the
+ target Service Port. \n Implementations supporting appProtocol
+ SHOULD recognize the Kubernetes Standard Application Protocols
+ defined in KEP-3726. \n 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. \n 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. \n
+ "
+ 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. \n 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:
+ 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. \n This filter can be used multiple times
+ within the same rule. \n 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. \n
+ 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. \n Input: GET /foo HTTP/1.1
+ my-header: foo \n Config: add: - name: \"my-header\"
+ value: \"bar,baz\" \n 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).
+ \n 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).
+ \n Input: GET /foo HTTP/1.1 my-header1: foo
+ my-header2: bar my-header3: baz \n Config:
+ remove: [\"my-header1\", \"my-header3\"] \n
+ 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. \n Input: GET /foo HTTP/1.1 my-header:
+ foo \n Config: set: - name: \"my-header\"
+ value: \"bar\" \n 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).
+ \n 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. \n 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. \n Support: Extended"
+ properties:
+ backendRef:
+ description: "BackendRef references a resource
+ where mirrored requests are sent. \n Mirrored
+ requests must be sent only to a single destination
+ endpoint within this BackendRef, irrespective
+ of how many endpoints are present within this
+ BackendRef. \n 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.
+ \n 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. \n In either error
+ case, the Message of the `ResolvedRefs` Condition
+ should be used to provide more detail about
+ the problem. \n Support: Extended for Kubernetes
+ Service \n 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\".
+ \n Defaults to \"Service\" when not specified.
+ \n 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. \n Support: Core (Services with
+ a type other than ExternalName) \n 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. \n 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. \n 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'
+ required:
+ - backendRef
+ type: object
+ requestRedirect:
+ description: "RequestRedirect defines a schema for
+ a filter that responds to the request with an
+ HTTP redirection. \n 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.
+ \n 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. \n 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\". \n 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.
+ \n 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`. \n Request Path
+ | Prefix Match | Replace Prefix | Modified
+ Path -------------|--------------|----------------|----------
+ /foo/bar | /foo | /xyz |
+ /xyz/bar /foo/bar | /foo |
+ /xyz/ | /xyz/bar /foo/bar |
+ /foo/ | /xyz | /xyz/bar
+ /foo/bar | /foo/ | /xyz/ |
+ /xyz/bar /foo | /foo |
+ /xyz | /xyz /foo/ | /foo
+ \ | /xyz | /xyz/ /foo/bar
+ \ | /foo | |
+ /bar /foo/ | /foo | | / /foo | /foo |
+ | / /foo/ | /foo
+ \ | / | / /foo |
+ /foo | / | /"
+ 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. \n Note
+ that values may be added to this enum,
+ implementations must ensure that unknown
+ values will not cause a crash. \n 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. \n If no port is specified, the
+ redirect port MUST be derived using the following
+ rules: \n * 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. \n Implementations SHOULD NOT
+ add the port number in the 'Location' header
+ in the following cases: \n * 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. \n 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. \n Scheme redirects can affect the
+ port of the redirect, for more information,
+ refer to the documentation for the port field
+ of this filter. \n Note that values may be
+ added to this enum, implementations must ensure
+ that unknown values will not cause a crash.
+ \n Unknown values here must result in the
+ implementation setting the Accepted Condition
+ for the Route to `status: False`, with a Reason
+ of `UnsupportedValue`. \n Support: Extended"
+ enum:
+ - http
+ - https
+ type: string
+ statusCode:
+ default: 302
+ description: "StatusCode is the HTTP status
+ code to be used in response. \n Note that
+ values may be added to this enum, implementations
+ must ensure that unknown values will not cause
+ a crash. \n Unknown values here must result
+ in the implementation setting the Accepted
+ Condition for the Route to `status: False`,
+ with a Reason of `UnsupportedValue`. \n Support:
+ Core"
+ enum:
+ - 301
+ - 302
+ type: integer
+ type: object
+ responseHeaderModifier:
+ description: "ResponseHeaderModifier defines a schema
+ for a filter that modifies response headers. \n
+ 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. \n Input: GET /foo HTTP/1.1
+ my-header: foo \n Config: add: - name: \"my-header\"
+ value: \"bar,baz\" \n 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).
+ \n 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).
+ \n Input: GET /foo HTTP/1.1 my-header1: foo
+ my-header2: bar my-header3: baz \n Config:
+ remove: [\"my-header1\", \"my-header3\"] \n
+ 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. \n Input: GET /foo HTTP/1.1 my-header:
+ foo \n Config: set: - name: \"my-header\"
+ value: \"bar\" \n 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).
+ \n 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: \n -
+ Core: Filter types and their corresponding configuration
+ defined by \"Support: Core\" in this package,
+ e.g. \"RequestHeaderModifier\". All implementations
+ must support core filters. \n - Extended: Filter
+ types and their corresponding configuration defined
+ by \"Support: Extended\" in this package, e.g.
+ \"RequestMirror\". Implementers are encouraged
+ to support extended filters. \n - 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. \n Implementers are encouraged
+ to define custom implementation types to extend
+ the core API with implementation-specific behavior.
+ \n 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. \n
+ Note that values may be added to this enum, implementations
+ must ensure that unknown values will not cause
+ a crash. \n 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
+ type: string
+ urlRewrite:
+ description: "URLRewrite defines a schema for a
+ filter that modifies a request during forwarding.
+ \n Support: Extended"
+ properties:
+ hostname:
+ description: "Hostname is the value to be used
+ to replace the Host header value during forwarding.
+ \n 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. \n
+ 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\". \n 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.
+ \n 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`. \n Request Path
+ | Prefix Match | Replace Prefix | Modified
+ Path -------------|--------------|----------------|----------
+ /foo/bar | /foo | /xyz |
+ /xyz/bar /foo/bar | /foo |
+ /xyz/ | /xyz/bar /foo/bar |
+ /foo/ | /xyz | /xyz/bar
+ /foo/bar | /foo/ | /xyz/ |
+ /xyz/bar /foo | /foo |
+ /xyz | /xyz /foo/ | /foo
+ \ | /xyz | /xyz/ /foo/bar
+ \ | /foo | |
+ /bar /foo/ | /foo | | / /foo | /foo |
+ | / /foo/ | /foo
+ \ | / | / /foo |
+ /foo | / | /"
+ 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. \n Note
+ that values may be added to this enum,
+ implementations must ensure that unknown
+ values will not cause a crash. \n 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.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-validations:
+ - message: May specify either httpRouteFilterRequestRedirect
+ or httpRouteFilterRequestRewrite, but not both
+ rule: '!(self.exists(f, f.type == ''RequestRedirect'')
+ && self.exists(f, f.type == ''URLRewrite''))'
+ - message: May specify either httpRouteFilterRequestRedirect
+ or httpRouteFilterRequestRewrite, but not both
+ rule: '!(self.exists(f, f.type == ''RequestRedirect'')
+ && 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\". \n Defaults to
+ \"Service\" when not specified. \n 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.
+ \n Support: Core (Services with a type other than ExternalName)
+ \n 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. \n
+ 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. \n 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. \n
+ 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. \n 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
+ filters:
+ description: "Filters define the filters that are applied to
+ requests that match this rule. \n The effects of ordering
+ of multiple behaviors are currently unspecified. This can
+ change in the future based on feedback during the alpha stage.
+ \n Conformance-levels at this level are defined based on the
+ type of filter: \n - 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. \n Specifying
+ the same filter multiple times is not supported unless explicitly
+ indicated in the filter. \n All filters are expected to be
+ compatible with each other except for the URLRewrite and RequestRedirect
+ filters, which may not be combined. If an implementation can
+ not support other combinations of filters, they must clearly
+ 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. \n 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:
+ 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. \n This filter can be used multiple times within
+ the same rule. \n 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. \n 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. \n Input: GET /foo HTTP/1.1 my-header: foo
+ \n Config: add: - name: \"my-header\" value: \"bar,baz\"
+ \n 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).
+ \n 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).
+ \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2:
+ bar my-header3: baz \n Config: remove: [\"my-header1\",
+ \"my-header3\"] \n 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. \n
+ Input: GET /foo HTTP/1.1 my-header: foo \n Config:
+ set: - name: \"my-header\" value: \"bar\" \n 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).
+ \n 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. \n 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. \n
+ Support: Extended"
+ properties:
+ backendRef:
+ description: "BackendRef references a resource where
+ mirrored requests are sent. \n Mirrored requests
+ must be sent only to a single destination endpoint
+ within this BackendRef, irrespective of how many
+ endpoints are present within this BackendRef. \n
+ 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. \n 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. \n In either error case, the Message
+ of the `ResolvedRefs` Condition should be used to
+ provide more detail about the problem. \n Support:
+ Extended for Kubernetes Service \n 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\".
+ \n Defaults to \"Service\" when not specified.
+ \n 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. \n Support: Core (Services with a
+ type other than ExternalName) \n 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. \n 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. \n 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'
+ required:
+ - backendRef
+ type: object
+ requestRedirect:
+ description: "RequestRedirect defines a schema for a filter
+ that responds to the request with an HTTP redirection.
+ \n 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. \n 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. \n 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\".
+ \n 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. \n 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`. \n Request Path | Prefix
+ Match | Replace Prefix | Modified Path -------------|--------------|----------------|----------
+ /foo/bar | /foo | /xyz |
+ /xyz/bar /foo/bar | /foo | /xyz/
+ \ | /xyz/bar /foo/bar | /foo/ |
+ /xyz | /xyz/bar /foo/bar | /foo/
+ \ | /xyz/ | /xyz/bar /foo |
+ /foo | /xyz | /xyz /foo/ |
+ /foo | /xyz | /xyz/ /foo/bar
+ \ | /foo | | /bar
+ /foo/ | /foo |
+ | / /foo | /foo |
+ | / /foo/ | /foo | / |
+ / /foo | /foo | / |
+ /"
+ 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. \n Note that values may be added
+ to this enum, implementations must ensure that
+ unknown values will not cause a crash. \n 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. \n If
+ no port is specified, the redirect port MUST be
+ derived using the following rules: \n * 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.
+ \n Implementations SHOULD NOT add the port number
+ in the 'Location' header in the following cases:
+ \n * 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. \n 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. \n
+ Scheme redirects can affect the port of the redirect,
+ for more information, refer to the documentation
+ for the port field of this filter. \n Note that
+ values may be added to this enum, implementations
+ must ensure that unknown values will not cause a
+ crash. \n Unknown values here must result in the
+ implementation setting the Accepted Condition for
+ the Route to `status: False`, with a Reason of `UnsupportedValue`.
+ \n Support: Extended"
+ enum:
+ - http
+ - https
+ type: string
+ statusCode:
+ default: 302
+ description: "StatusCode is the HTTP status code to
+ be used in response. \n Note that values may be
+ added to this enum, implementations must ensure
+ that unknown values will not cause a crash. \n Unknown
+ values here must result in the implementation setting
+ the Accepted Condition for the Route to `status:
+ False`, with a Reason of `UnsupportedValue`. \n
+ Support: Core"
+ enum:
+ - 301
+ - 302
+ type: integer
+ type: object
+ responseHeaderModifier:
+ description: "ResponseHeaderModifier defines a schema
+ for a filter that modifies response headers. \n 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. \n Input: GET /foo HTTP/1.1 my-header: foo
+ \n Config: add: - name: \"my-header\" value: \"bar,baz\"
+ \n 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).
+ \n 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).
+ \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2:
+ bar my-header3: baz \n Config: remove: [\"my-header1\",
+ \"my-header3\"] \n 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. \n
+ Input: GET /foo HTTP/1.1 my-header: foo \n Config:
+ set: - name: \"my-header\" value: \"bar\" \n 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).
+ \n 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: \n - Core: Filter types and
+ their corresponding configuration defined by \"Support:
+ Core\" in this package, e.g. \"RequestHeaderModifier\".
+ All implementations must support core filters. \n -
+ Extended: Filter types and their corresponding configuration
+ defined by \"Support: Extended\" in this package, e.g.
+ \"RequestMirror\". Implementers are encouraged to support
+ extended filters. \n - 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. \n Implementers are encouraged to
+ define custom implementation types to extend the core
+ API with implementation-specific behavior. \n 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. \n Note that values may be added to this enum,
+ implementations must ensure that unknown values will
+ not cause a crash. \n 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
+ type: string
+ urlRewrite:
+ description: "URLRewrite defines a schema for a filter
+ that modifies a request during forwarding. \n Support:
+ Extended"
+ properties:
+ hostname:
+ description: "Hostname is the value to be used to
+ replace the Host header value during forwarding.
+ \n 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. \n 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\".
+ \n 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. \n 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`. \n Request Path | Prefix
+ Match | Replace Prefix | Modified Path -------------|--------------|----------------|----------
+ /foo/bar | /foo | /xyz |
+ /xyz/bar /foo/bar | /foo | /xyz/
+ \ | /xyz/bar /foo/bar | /foo/ |
+ /xyz | /xyz/bar /foo/bar | /foo/
+ \ | /xyz/ | /xyz/bar /foo |
+ /foo | /xyz | /xyz /foo/ |
+ /foo | /xyz | /xyz/ /foo/bar
+ \ | /foo | | /bar
+ /foo/ | /foo |
+ | / /foo | /foo |
+ | / /foo/ | /foo | / |
+ / /foo | /foo | / |
+ /"
+ 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. \n Note that values may be added
+ to this enum, implementations must ensure that
+ unknown values will not cause a crash. \n 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.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-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. \n For example, take the following matches configuration:
+ \n ``` matches: - path: value: \"/foo\" headers: - name: \"version\"
+ value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request
+ to match against this rule, a request must satisfy EITHER
+ of the two conditions: \n - path prefixed with `/foo` AND
+ contains the header `version: v2` - path prefix of `/v2/foo`
+ \n See the documentation for HTTPRouteMatch on how to specify
+ multiple match conditions that should be ANDed together. \n
+ If no matches are specified, the default is a prefix path
+ match on \"/\", which has the effect of matching every HTTP
+ request. \n 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:
+ \n * \"Exact\" path match. * \"Prefix\" path match with largest
+ number of characters. * Method match. * Largest number of
+ header matches. * Largest number of query param matches. \n
+ Note: The precedence of RegularExpression path matches are
+ implementation-specific. \n If ties still exist across multiple
+ Routes, matching precedence MUST be determined in order of
+ the following criteria, continuing on ties: \n * The oldest
+ Route based on creation timestamp. * The Route appearing first
+ in alphabetical order by \"{namespace}/{name}\". \n 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. \n 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 action. Multiple match types are
+ ANDed together, i.e. the match will evaluate to true only
+ if all conditions are satisfied. \n For example, the match
+ below will match a HTTP request only if its path starts
+ with `/foo` AND it contains the `version: v1` header: \n
+ ``` match: \n path: value: \"/foo\" headers: - name: \"version\"
+ value \"v1\" \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).
+ \n 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.
+ \n 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. \n Support: Core (Exact)
+ \n Support: Implementation-specific (RegularExpression)
+ \n 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. \n 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. \n Support: Core (Exact, PathPrefix)
+ \n 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. \n 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).
+ \n 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. \n 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.
+ \n 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. \n Support:
+ Extended (Exact) \n Support: Implementation-specific
+ (RegularExpression) \n 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: 8
+ type: array
+ timeouts:
+ description: "Timeouts defines the timeouts that can be configured
+ for an HTTP request. \n Support: Extended \n "
+ 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. \n 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.
+ \n Because the Request timeout encompasses the BackendRequest
+ timeout, the value of BackendRequest must be <= the value
+ of Request timeout. \n 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. \n 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.
+ \n 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. \n When this
+ field is unspecified, request timeout behavior is implementation-specific.
+ \n 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
+ type: array
+ 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.
+ \n 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. \n 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. \n 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. \n A Route MUST be considered \"Accepted\"
+ if at least one of the Route's rules is implemented by the
+ Gateway. \n There are a number of cases where the \"Accepted\"
+ condition may not be set due to lack of controller visibility,
+ that includes when: \n * The Route refers to a non-existent
+ parent. * The Route is of a type that the controller does
+ not support. * The Route is in a namespace the controller
+ does not have access to."
+ items:
+ description: "Condition contains details for one aspect of
+ the current state of this API Resource. --- This struct
+ is intended for direct use as an array at the field path
+ .status.conditions. For example, \n type FooStatus struct{
+ // Represents the observations of a foo's current state.
+ // Known .status.conditions.type are: \"Available\", \"Progressing\",
+ and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
+ // +listType=map // +listMapKey=type Conditions []metav1.Condition
+ `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
+ protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields
+ }"
+ 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.
+ --- Many .condition.type values are consistent across
+ resources like Available, but because arbitrary conditions
+ can be useful (see .node.status.conditions), the ability
+ to deconflict is important. The regex it matches is
+ (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+ 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. \n Example:
+ \"example.net/gateway-controller\". \n 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).
+ \n 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). \n
+ 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. \n There are
+ two kinds of parent resources with \"Core\" support: \n
+ * Gateway (Gateway conformance profile) * Service (Mesh
+ conformance profile, experimental, ClusterIP Services
+ only) \n 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. \n 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. \n 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. \n 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. \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose
+ to support other parent resources. Implementations supporting
+ other types of parent resources MUST clearly document
+ how/if Port is interpreted. \n 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.
+ \n Support: Extended \n "
+ 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: \n * 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. Note that attaching Routes to Services
+ as Parents is part of experimental Mesh support and is
+ not supported for any other purpose. \n Implementations
+ MAY choose to support attaching Routes to other resources.
+ If that is the case, they MUST clearly document how SectionName
+ is interpreted. \n 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.
+ \n 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:
+ - controllerName
+ - parentRef
+ type: object
+ maxItems: 32
+ type: array
+ required:
+ - parents
+ type: object
+ required:
+ - spec
+ type: object
+ served: true
+ storage: false
+ 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. \n Valid values for Hostnames
+ are determined by RFC 1123 definition of a hostname with 2 notable
+ exceptions: \n 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. \n 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:
+ \n * 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. \n 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`. \n 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. \n 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. \n 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: \n * Characters in a matching
+ non-wildcard hostname. * Characters in a matching hostname. \n If
+ ties exist across multiple Routes, the matching precedence rules
+ for HTTPRouteMatches takes over. \n 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: \n 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. \n 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`).
+ \n 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
+ 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. \n There are two kinds of parent resources with \"Core\"
+ support: \n * Gateway (Gateway conformance profile) * Service (Mesh
+ conformance profile, experimental, ClusterIP Services only) This
+ API may be extended in the future to support additional kinds of
+ parent resources. \n ParentRefs must be _distinct_. This means either
+ that: \n * 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.
+ \n Some examples: \n * 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`. \n 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. \n 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. \n 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. \n 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. \n "
+ 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: \n * Gateway (Gateway conformance profile) * Service
+ (Mesh conformance profile, experimental, ClusterIP Services only)
+ \n This API may be extended in the future to support additional
+ kinds of parent resources. \n 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). \n 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. \n There are two
+ kinds of parent resources with \"Core\" support: \n * Gateway
+ (Gateway conformance profile) * Service (Mesh conformance
+ profile, experimental, ClusterIP Services only) \n 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. \n 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.
+ \n 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.
+ \n 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.
+ \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose to support other parent
+ resources. Implementations supporting other types of parent
+ resources MUST clearly document how/if Port is interpreted.
+ \n 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. \n
+ Support: Extended \n "
+ 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: \n * 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. Note that attaching
+ Routes to Services as Parents is part of experimental Mesh
+ support and is not supported for any other purpose. \n Implementations
+ MAY choose to support attaching Routes to other resources.
+ If that is the case, they MUST clearly document how SectionName
+ is interpreted. \n 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. \n 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-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. \n Failure behavior here depends
+ on how many BackendRefs are specified and how many are invalid.
+ \n 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. \n
+ See the HTTPBackendRef definition for the rules about what
+ makes a single HTTPBackendRef invalid. \n 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.
+ \n 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.
+ \n Support: Core for Kubernetes Service \n Support: Extended
+ for Kubernetes ServiceImport \n Support: Implementation-specific
+ for any other resource \n Support for weight: Core"
+ items:
+ description: "HTTPBackendRef defines how a HTTPRoute forwards
+ a HTTP request. \n 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. \n
+ \n When the BackendRef points to a Kubernetes Service, implementations
+ SHOULD honor the appProtocol field if it is set for the
+ target Service Port. \n Implementations supporting appProtocol
+ SHOULD recognize the Kubernetes Standard Application Protocols
+ defined in KEP-3726. \n 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. \n 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. \n
+ "
+ 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. \n 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:
+ 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. \n This filter can be used multiple times
+ within the same rule. \n 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. \n
+ 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. \n Input: GET /foo HTTP/1.1
+ my-header: foo \n Config: add: - name: \"my-header\"
+ value: \"bar,baz\" \n 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).
+ \n 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).
+ \n Input: GET /foo HTTP/1.1 my-header1: foo
+ my-header2: bar my-header3: baz \n Config:
+ remove: [\"my-header1\", \"my-header3\"] \n
+ 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. \n Input: GET /foo HTTP/1.1 my-header:
+ foo \n Config: set: - name: \"my-header\"
+ value: \"bar\" \n 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).
+ \n 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. \n 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. \n Support: Extended"
+ properties:
+ backendRef:
+ description: "BackendRef references a resource
+ where mirrored requests are sent. \n Mirrored
+ requests must be sent only to a single destination
+ endpoint within this BackendRef, irrespective
+ of how many endpoints are present within this
+ BackendRef. \n 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.
+ \n 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. \n In either error
+ case, the Message of the `ResolvedRefs` Condition
+ should be used to provide more detail about
+ the problem. \n Support: Extended for Kubernetes
+ Service \n 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\".
+ \n Defaults to \"Service\" when not specified.
+ \n 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. \n Support: Core (Services with
+ a type other than ExternalName) \n 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. \n 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. \n 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'
+ required:
+ - backendRef
+ type: object
+ requestRedirect:
+ description: "RequestRedirect defines a schema for
+ a filter that responds to the request with an
+ HTTP redirection. \n 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.
+ \n 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. \n 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\". \n 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.
+ \n 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`. \n Request Path
+ | Prefix Match | Replace Prefix | Modified
+ Path -------------|--------------|----------------|----------
+ /foo/bar | /foo | /xyz |
+ /xyz/bar /foo/bar | /foo |
+ /xyz/ | /xyz/bar /foo/bar |
+ /foo/ | /xyz | /xyz/bar
+ /foo/bar | /foo/ | /xyz/ |
+ /xyz/bar /foo | /foo |
+ /xyz | /xyz /foo/ | /foo
+ \ | /xyz | /xyz/ /foo/bar
+ \ | /foo | |
+ /bar /foo/ | /foo | | / /foo | /foo |
+ | / /foo/ | /foo
+ \ | / | / /foo |
+ /foo | / | /"
+ 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. \n Note
+ that values may be added to this enum,
+ implementations must ensure that unknown
+ values will not cause a crash. \n 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. \n If no port is specified, the
+ redirect port MUST be derived using the following
+ rules: \n * 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. \n Implementations SHOULD NOT
+ add the port number in the 'Location' header
+ in the following cases: \n * 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. \n 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. \n Scheme redirects can affect the
+ port of the redirect, for more information,
+ refer to the documentation for the port field
+ of this filter. \n Note that values may be
+ added to this enum, implementations must ensure
+ that unknown values will not cause a crash.
+ \n Unknown values here must result in the
+ implementation setting the Accepted Condition
+ for the Route to `status: False`, with a Reason
+ of `UnsupportedValue`. \n Support: Extended"
+ enum:
+ - http
+ - https
+ type: string
+ statusCode:
+ default: 302
+ description: "StatusCode is the HTTP status
+ code to be used in response. \n Note that
+ values may be added to this enum, implementations
+ must ensure that unknown values will not cause
+ a crash. \n Unknown values here must result
+ in the implementation setting the Accepted
+ Condition for the Route to `status: False`,
+ with a Reason of `UnsupportedValue`. \n Support:
+ Core"
+ enum:
+ - 301
+ - 302
+ type: integer
+ type: object
+ responseHeaderModifier:
+ description: "ResponseHeaderModifier defines a schema
+ for a filter that modifies response headers. \n
+ 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. \n Input: GET /foo HTTP/1.1
+ my-header: foo \n Config: add: - name: \"my-header\"
+ value: \"bar,baz\" \n 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).
+ \n 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).
+ \n Input: GET /foo HTTP/1.1 my-header1: foo
+ my-header2: bar my-header3: baz \n Config:
+ remove: [\"my-header1\", \"my-header3\"] \n
+ 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. \n Input: GET /foo HTTP/1.1 my-header:
+ foo \n Config: set: - name: \"my-header\"
+ value: \"bar\" \n 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).
+ \n 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: \n -
+ Core: Filter types and their corresponding configuration
+ defined by \"Support: Core\" in this package,
+ e.g. \"RequestHeaderModifier\". All implementations
+ must support core filters. \n - Extended: Filter
+ types and their corresponding configuration defined
+ by \"Support: Extended\" in this package, e.g.
+ \"RequestMirror\". Implementers are encouraged
+ to support extended filters. \n - 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. \n Implementers are encouraged
+ to define custom implementation types to extend
+ the core API with implementation-specific behavior.
+ \n 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. \n
+ Note that values may be added to this enum, implementations
+ must ensure that unknown values will not cause
+ a crash. \n 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
+ type: string
+ urlRewrite:
+ description: "URLRewrite defines a schema for a
+ filter that modifies a request during forwarding.
+ \n Support: Extended"
+ properties:
+ hostname:
+ description: "Hostname is the value to be used
+ to replace the Host header value during forwarding.
+ \n 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. \n
+ 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\". \n 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.
+ \n 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`. \n Request Path
+ | Prefix Match | Replace Prefix | Modified
+ Path -------------|--------------|----------------|----------
+ /foo/bar | /foo | /xyz |
+ /xyz/bar /foo/bar | /foo |
+ /xyz/ | /xyz/bar /foo/bar |
+ /foo/ | /xyz | /xyz/bar
+ /foo/bar | /foo/ | /xyz/ |
+ /xyz/bar /foo | /foo |
+ /xyz | /xyz /foo/ | /foo
+ \ | /xyz | /xyz/ /foo/bar
+ \ | /foo | |
+ /bar /foo/ | /foo | | / /foo | /foo |
+ | / /foo/ | /foo
+ \ | / | / /foo |
+ /foo | / | /"
+ 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. \n Note
+ that values may be added to this enum,
+ implementations must ensure that unknown
+ values will not cause a crash. \n 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.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-validations:
+ - message: May specify either httpRouteFilterRequestRedirect
+ or httpRouteFilterRequestRewrite, but not both
+ rule: '!(self.exists(f, f.type == ''RequestRedirect'')
+ && self.exists(f, f.type == ''URLRewrite''))'
+ - message: May specify either httpRouteFilterRequestRedirect
+ or httpRouteFilterRequestRewrite, but not both
+ rule: '!(self.exists(f, f.type == ''RequestRedirect'')
+ && 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\". \n Defaults to
+ \"Service\" when not specified. \n 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.
+ \n Support: Core (Services with a type other than ExternalName)
+ \n 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. \n
+ 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. \n 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. \n
+ 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. \n 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
+ filters:
+ description: "Filters define the filters that are applied to
+ requests that match this rule. \n The effects of ordering
+ of multiple behaviors are currently unspecified. This can
+ change in the future based on feedback during the alpha stage.
+ \n Conformance-levels at this level are defined based on the
+ type of filter: \n - 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. \n Specifying
+ the same filter multiple times is not supported unless explicitly
+ indicated in the filter. \n All filters are expected to be
+ compatible with each other except for the URLRewrite and RequestRedirect
+ filters, which may not be combined. If an implementation can
+ not support other combinations of filters, they must clearly
+ 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. \n 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:
+ 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. \n This filter can be used multiple times within
+ the same rule. \n 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. \n 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. \n Input: GET /foo HTTP/1.1 my-header: foo
+ \n Config: add: - name: \"my-header\" value: \"bar,baz\"
+ \n 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).
+ \n 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).
+ \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2:
+ bar my-header3: baz \n Config: remove: [\"my-header1\",
+ \"my-header3\"] \n 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. \n
+ Input: GET /foo HTTP/1.1 my-header: foo \n Config:
+ set: - name: \"my-header\" value: \"bar\" \n 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).
+ \n 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. \n 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. \n
+ Support: Extended"
+ properties:
+ backendRef:
+ description: "BackendRef references a resource where
+ mirrored requests are sent. \n Mirrored requests
+ must be sent only to a single destination endpoint
+ within this BackendRef, irrespective of how many
+ endpoints are present within this BackendRef. \n
+ 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. \n 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. \n In either error case, the Message
+ of the `ResolvedRefs` Condition should be used to
+ provide more detail about the problem. \n Support:
+ Extended for Kubernetes Service \n 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\".
+ \n Defaults to \"Service\" when not specified.
+ \n 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. \n Support: Core (Services with a
+ type other than ExternalName) \n 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. \n 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. \n 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'
+ required:
+ - backendRef
+ type: object
+ requestRedirect:
+ description: "RequestRedirect defines a schema for a filter
+ that responds to the request with an HTTP redirection.
+ \n 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. \n 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. \n 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\".
+ \n 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. \n 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`. \n Request Path | Prefix
+ Match | Replace Prefix | Modified Path -------------|--------------|----------------|----------
+ /foo/bar | /foo | /xyz |
+ /xyz/bar /foo/bar | /foo | /xyz/
+ \ | /xyz/bar /foo/bar | /foo/ |
+ /xyz | /xyz/bar /foo/bar | /foo/
+ \ | /xyz/ | /xyz/bar /foo |
+ /foo | /xyz | /xyz /foo/ |
+ /foo | /xyz | /xyz/ /foo/bar
+ \ | /foo | | /bar
+ /foo/ | /foo |
+ | / /foo | /foo |
+ | / /foo/ | /foo | / |
+ / /foo | /foo | / |
+ /"
+ 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. \n Note that values may be added
+ to this enum, implementations must ensure that
+ unknown values will not cause a crash. \n 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. \n If
+ no port is specified, the redirect port MUST be
+ derived using the following rules: \n * 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.
+ \n Implementations SHOULD NOT add the port number
+ in the 'Location' header in the following cases:
+ \n * 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. \n 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. \n
+ Scheme redirects can affect the port of the redirect,
+ for more information, refer to the documentation
+ for the port field of this filter. \n Note that
+ values may be added to this enum, implementations
+ must ensure that unknown values will not cause a
+ crash. \n Unknown values here must result in the
+ implementation setting the Accepted Condition for
+ the Route to `status: False`, with a Reason of `UnsupportedValue`.
+ \n Support: Extended"
+ enum:
+ - http
+ - https
+ type: string
+ statusCode:
+ default: 302
+ description: "StatusCode is the HTTP status code to
+ be used in response. \n Note that values may be
+ added to this enum, implementations must ensure
+ that unknown values will not cause a crash. \n Unknown
+ values here must result in the implementation setting
+ the Accepted Condition for the Route to `status:
+ False`, with a Reason of `UnsupportedValue`. \n
+ Support: Core"
+ enum:
+ - 301
+ - 302
+ type: integer
+ type: object
+ responseHeaderModifier:
+ description: "ResponseHeaderModifier defines a schema
+ for a filter that modifies response headers. \n 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. \n Input: GET /foo HTTP/1.1 my-header: foo
+ \n Config: add: - name: \"my-header\" value: \"bar,baz\"
+ \n 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).
+ \n 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).
+ \n Input: GET /foo HTTP/1.1 my-header1: foo my-header2:
+ bar my-header3: baz \n Config: remove: [\"my-header1\",
+ \"my-header3\"] \n 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. \n
+ Input: GET /foo HTTP/1.1 my-header: foo \n Config:
+ set: - name: \"my-header\" value: \"bar\" \n 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).
+ \n 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: \n - Core: Filter types and
+ their corresponding configuration defined by \"Support:
+ Core\" in this package, e.g. \"RequestHeaderModifier\".
+ All implementations must support core filters. \n -
+ Extended: Filter types and their corresponding configuration
+ defined by \"Support: Extended\" in this package, e.g.
+ \"RequestMirror\". Implementers are encouraged to support
+ extended filters. \n - 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. \n Implementers are encouraged to
+ define custom implementation types to extend the core
+ API with implementation-specific behavior. \n 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. \n Note that values may be added to this enum,
+ implementations must ensure that unknown values will
+ not cause a crash. \n 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
+ type: string
+ urlRewrite:
+ description: "URLRewrite defines a schema for a filter
+ that modifies a request during forwarding. \n Support:
+ Extended"
+ properties:
+ hostname:
+ description: "Hostname is the value to be used to
+ replace the Host header value during forwarding.
+ \n 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. \n 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\".
+ \n 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. \n 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`. \n Request Path | Prefix
+ Match | Replace Prefix | Modified Path -------------|--------------|----------------|----------
+ /foo/bar | /foo | /xyz |
+ /xyz/bar /foo/bar | /foo | /xyz/
+ \ | /xyz/bar /foo/bar | /foo/ |
+ /xyz | /xyz/bar /foo/bar | /foo/
+ \ | /xyz/ | /xyz/bar /foo |
+ /foo | /xyz | /xyz /foo/ |
+ /foo | /xyz | /xyz/ /foo/bar
+ \ | /foo | | /bar
+ /foo/ | /foo |
+ | / /foo | /foo |
+ | / /foo/ | /foo | / |
+ / /foo | /foo | / |
+ /"
+ 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. \n Note that values may be added
+ to this enum, implementations must ensure that
+ unknown values will not cause a crash. \n 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.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-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. \n For example, take the following matches configuration:
+ \n ``` matches: - path: value: \"/foo\" headers: - name: \"version\"
+ value: \"v2\" - path: value: \"/v2/foo\" ``` \n For a request
+ to match against this rule, a request must satisfy EITHER
+ of the two conditions: \n - path prefixed with `/foo` AND
+ contains the header `version: v2` - path prefix of `/v2/foo`
+ \n See the documentation for HTTPRouteMatch on how to specify
+ multiple match conditions that should be ANDed together. \n
+ If no matches are specified, the default is a prefix path
+ match on \"/\", which has the effect of matching every HTTP
+ request. \n 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:
+ \n * \"Exact\" path match. * \"Prefix\" path match with largest
+ number of characters. * Method match. * Largest number of
+ header matches. * Largest number of query param matches. \n
+ Note: The precedence of RegularExpression path matches are
+ implementation-specific. \n If ties still exist across multiple
+ Routes, matching precedence MUST be determined in order of
+ the following criteria, continuing on ties: \n * The oldest
+ Route based on creation timestamp. * The Route appearing first
+ in alphabetical order by \"{namespace}/{name}\". \n 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. \n 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 action. Multiple match types are
+ ANDed together, i.e. the match will evaluate to true only
+ if all conditions are satisfied. \n For example, the match
+ below will match a HTTP request only if its path starts
+ with `/foo` AND it contains the `version: v1` header: \n
+ ``` match: \n path: value: \"/foo\" headers: - name: \"version\"
+ value \"v1\" \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).
+ \n 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.
+ \n 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. \n Support: Core (Exact)
+ \n Support: Implementation-specific (RegularExpression)
+ \n 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. \n 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. \n Support: Core (Exact, PathPrefix)
+ \n 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. \n 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).
+ \n 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. \n 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.
+ \n 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. \n Support:
+ Extended (Exact) \n Support: Implementation-specific
+ (RegularExpression) \n 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: 8
+ type: array
+ timeouts:
+ description: "Timeouts defines the timeouts that can be configured
+ for an HTTP request. \n Support: Extended \n "
+ 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. \n 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.
+ \n Because the Request timeout encompasses the BackendRequest
+ timeout, the value of BackendRequest must be <= the value
+ of Request timeout. \n 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. \n 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.
+ \n 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. \n When this
+ field is unspecified, request timeout behavior is implementation-specific.
+ \n 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
+ type: array
+ 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.
+ \n 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. \n 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. \n 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. \n A Route MUST be considered \"Accepted\"
+ if at least one of the Route's rules is implemented by the
+ Gateway. \n There are a number of cases where the \"Accepted\"
+ condition may not be set due to lack of controller visibility,
+ that includes when: \n * The Route refers to a non-existent
+ parent. * The Route is of a type that the controller does
+ not support. * The Route is in a namespace the controller
+ does not have access to."
+ items:
+ description: "Condition contains details for one aspect of
+ the current state of this API Resource. --- This struct
+ is intended for direct use as an array at the field path
+ .status.conditions. For example, \n type FooStatus struct{
+ // Represents the observations of a foo's current state.
+ // Known .status.conditions.type are: \"Available\", \"Progressing\",
+ and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
+ // +listType=map // +listMapKey=type Conditions []metav1.Condition
+ `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
+ protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields
+ }"
+ 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.
+ --- Many .condition.type values are consistent across
+ resources like Available, but because arbitrary conditions
+ can be useful (see .node.status.conditions), the ability
+ to deconflict is important. The regex it matches is
+ (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+ 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. \n Example:
+ \"example.net/gateway-controller\". \n 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).
+ \n 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). \n
+ 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. \n There are
+ two kinds of parent resources with \"Core\" support: \n
+ * Gateway (Gateway conformance profile) * Service (Mesh
+ conformance profile, experimental, ClusterIP Services
+ only) \n 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. \n 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. \n 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. \n 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. \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose
+ to support other parent resources. Implementations supporting
+ other types of parent resources MUST clearly document
+ how/if Port is interpreted. \n 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.
+ \n Support: Extended \n "
+ 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: \n * 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. Note that attaching Routes to Services
+ as Parents is part of experimental Mesh support and is
+ not supported for any other purpose. \n Implementations
+ MAY choose to support attaching Routes to other resources.
+ If that is the case, they MUST clearly document how SectionName
+ is interpreted. \n 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.
+ \n 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:
+ - controllerName
+ - parentRef
+ type: object
+ maxItems: 32
+ type: array
+ required:
+ - parents
+ type: object
+ required:
+ - spec
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: null
+ storedVersions: null
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2466
+ gateway.networking.k8s.io/bundle-version: v1.0.0
+ gateway.networking.k8s.io/channel: experimental
+ creationTimestamp: null
+ 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
+ deprecated: true
+ deprecationWarning: The v1alpha2 version of ReferenceGrant has been deprecated
+ and will be removed in a future release of the API. Please upgrade to v1beta1.
+ name: v1alpha2
+ 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. \n 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. \n A ReferenceGrant is required for all cross-namespace
+ references in Gateway API (with the exception of cross-namespace Route-Gateway
+ attachment, which is governed by the AllowedRoutes configuration on the
+ Gateway, and cross-namespace Service ParentRefs on a \"consumer\" mesh Route,
+ which defines routing rules applicable only to workloads in the Route namespace).
+ ReferenceGrants allowing a reference from a Route to a Service are only
+ applicable to BackendRefs. \n 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. \n 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. \n 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. \n When
+ used to permit a SecretObjectReference: \n * Gateway \n When
+ used to permit a BackendObjectReference: \n * 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. \n
+ 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
+ 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.
+ \n 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. \n 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: \n * 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
+ 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. \n 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. \n All cross-namespace references in Gateway API
+ (with the exception of cross-namespace Gateway-route attachment) require
+ a ReferenceGrant. \n 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. \n 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. \n 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. \n When
+ used to permit a SecretObjectReference: \n * Gateway \n When
+ used to permit a BackendObjectReference: \n * 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. \n
+ 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
+ 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.
+ \n 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. \n 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: \n * 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
+ required:
+ - from
+ - to
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources: {}
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: null
+ storedVersions: null
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2466
+ gateway.networking.k8s.io/bundle-version: v1.0.0
+ gateway.networking.k8s.io/channel: experimental
+ creationTimestamp: null
+ 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. \n There are two kinds of parent resources with \"Core\"
+ support: \n * Gateway (Gateway conformance profile) * Service (Mesh
+ conformance profile, experimental, ClusterIP Services only) This
+ API may be extended in the future to support additional kinds of
+ parent resources. \n ParentRefs must be _distinct_. This means either
+ that: \n * 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.
+ \n Some examples: \n * 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`. \n 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. \n 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. \n 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. \n 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. \n "
+ 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: \n * Gateway (Gateway conformance profile) * Service
+ (Mesh conformance profile, experimental, ClusterIP Services only)
+ \n This API may be extended in the future to support additional
+ kinds of parent resources. \n 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). \n 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. \n There are two
+ kinds of parent resources with \"Core\" support: \n * Gateway
+ (Gateway conformance profile) * Service (Mesh conformance
+ profile, experimental, ClusterIP Services only) \n 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. \n 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.
+ \n 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.
+ \n 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.
+ \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose to support other parent
+ resources. Implementations supporting other types of parent
+ resources MUST clearly document how/if Port is interpreted.
+ \n 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. \n
+ Support: Extended \n "
+ 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: \n * 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. Note that attaching
+ Routes to Services as Parents is part of experimental Mesh
+ support and is not supported for any other purpose. \n Implementations
+ MAY choose to support attaching Routes to other resources.
+ If that is the case, they MUST clearly document how SectionName
+ is interpreted. \n 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. \n 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-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 non-existent 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.
+ \n Support: Core for Kubernetes Service \n Support: Extended
+ for Kubernetes ServiceImport \n Support: Implementation-specific
+ for any other resource \n Support for weight: Extended"
+ items:
+ description: "BackendRef defines how a Route should forward
+ a request to a Kubernetes resource. \n 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. \n
+ \n When the BackendRef points to a Kubernetes Service, implementations
+ SHOULD honor the appProtocol field if it is set for the
+ target Service Port. \n Implementations supporting appProtocol
+ SHOULD recognize the Kubernetes Standard Application Protocols
+ defined in KEP-3726. \n 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. \n 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. \n
+ \n 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\". \n Defaults to
+ \"Service\" when not specified. \n 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.
+ \n Support: Core (Services with a type other than ExternalName)
+ \n 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. \n
+ 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. \n 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. \n
+ 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. \n 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
+ type: object
+ maxItems: 16
+ minItems: 1
+ type: array
+ 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.
+ \n 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. \n 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. \n 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. \n A Route MUST be considered \"Accepted\"
+ if at least one of the Route's rules is implemented by the
+ Gateway. \n There are a number of cases where the \"Accepted\"
+ condition may not be set due to lack of controller visibility,
+ that includes when: \n * The Route refers to a non-existent
+ parent. * The Route is of a type that the controller does
+ not support. * The Route is in a namespace the controller
+ does not have access to."
+ items:
+ description: "Condition contains details for one aspect of
+ the current state of this API Resource. --- This struct
+ is intended for direct use as an array at the field path
+ .status.conditions. For example, \n type FooStatus struct{
+ // Represents the observations of a foo's current state.
+ // Known .status.conditions.type are: \"Available\", \"Progressing\",
+ and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
+ // +listType=map // +listMapKey=type Conditions []metav1.Condition
+ `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
+ protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields
+ }"
+ 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.
+ --- Many .condition.type values are consistent across
+ resources like Available, but because arbitrary conditions
+ can be useful (see .node.status.conditions), the ability
+ to deconflict is important. The regex it matches is
+ (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+ 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. \n Example:
+ \"example.net/gateway-controller\". \n 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).
+ \n 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). \n
+ 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. \n There are
+ two kinds of parent resources with \"Core\" support: \n
+ * Gateway (Gateway conformance profile) * Service (Mesh
+ conformance profile, experimental, ClusterIP Services
+ only) \n 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. \n 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. \n 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. \n 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. \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose
+ to support other parent resources. Implementations supporting
+ other types of parent resources MUST clearly document
+ how/if Port is interpreted. \n 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.
+ \n Support: Extended \n "
+ 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: \n * 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. Note that attaching Routes to Services
+ as Parents is part of experimental Mesh support and is
+ not supported for any other purpose. \n Implementations
+ MAY choose to support attaching Routes to other resources.
+ If that is the case, they MUST clearly document how SectionName
+ is interpreted. \n 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.
+ \n 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:
+ - controllerName
+ - parentRef
+ type: object
+ maxItems: 32
+ type: array
+ required:
+ - parents
+ type: object
+ required:
+ - spec
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: null
+ storedVersions: null
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2466
+ gateway.networking.k8s.io/bundle-version: v1.0.0
+ gateway.networking.k8s.io/channel: experimental
+ creationTimestamp: null
+ 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: 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. \n 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 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: \n 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. \n 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: \n * 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. \n 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. \n 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. \n 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: \n 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. \n 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`).
+ \n 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
+ 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. \n There are two kinds of parent resources with \"Core\"
+ support: \n * Gateway (Gateway conformance profile) * Service (Mesh
+ conformance profile, experimental, ClusterIP Services only) This
+ API may be extended in the future to support additional kinds of
+ parent resources. \n ParentRefs must be _distinct_. This means either
+ that: \n * 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.
+ \n Some examples: \n * 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`. \n 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. \n 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. \n 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. \n 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. \n "
+ 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: \n * Gateway (Gateway conformance profile) * Service
+ (Mesh conformance profile, experimental, ClusterIP Services only)
+ \n This API may be extended in the future to support additional
+ kinds of parent resources. \n 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). \n 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. \n There are two
+ kinds of parent resources with \"Core\" support: \n * Gateway
+ (Gateway conformance profile) * Service (Mesh conformance
+ profile, experimental, ClusterIP Services only) \n 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. \n 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.
+ \n 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.
+ \n 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.
+ \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose to support other parent
+ resources. Implementations supporting other types of parent
+ resources MUST clearly document how/if Port is interpreted.
+ \n 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. \n
+ Support: Extended \n "
+ 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: \n * 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. Note that attaching
+ Routes to Services as Parents is part of experimental Mesh
+ support and is not supported for any other purpose. \n Implementations
+ MAY choose to support attaching Routes to other resources.
+ If that is the case, they MUST clearly document how SectionName
+ is interpreted. \n 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. \n 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-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 non-existent 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. \n Support: Core for Kubernetes
+ Service \n Support: Extended for Kubernetes ServiceImport
+ \n Support: Implementation-specific for any other resource
+ \n Support for weight: Extended"
+ items:
+ description: "BackendRef defines how a Route should forward
+ a request to a Kubernetes resource. \n 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. \n
+ \n When the BackendRef points to a Kubernetes Service, implementations
+ SHOULD honor the appProtocol field if it is set for the
+ target Service Port. \n Implementations supporting appProtocol
+ SHOULD recognize the Kubernetes Standard Application Protocols
+ defined in KEP-3726. \n 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. \n 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. \n
+ \n 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\". \n Defaults to
+ \"Service\" when not specified. \n 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.
+ \n Support: Core (Services with a type other than ExternalName)
+ \n 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. \n
+ 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. \n 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. \n
+ 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. \n 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
+ type: object
+ maxItems: 16
+ minItems: 1
+ type: array
+ 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.
+ \n 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. \n 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. \n 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. \n A Route MUST be considered \"Accepted\"
+ if at least one of the Route's rules is implemented by the
+ Gateway. \n There are a number of cases where the \"Accepted\"
+ condition may not be set due to lack of controller visibility,
+ that includes when: \n * The Route refers to a non-existent
+ parent. * The Route is of a type that the controller does
+ not support. * The Route is in a namespace the controller
+ does not have access to."
+ items:
+ description: "Condition contains details for one aspect of
+ the current state of this API Resource. --- This struct
+ is intended for direct use as an array at the field path
+ .status.conditions. For example, \n type FooStatus struct{
+ // Represents the observations of a foo's current state.
+ // Known .status.conditions.type are: \"Available\", \"Progressing\",
+ and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
+ // +listType=map // +listMapKey=type Conditions []metav1.Condition
+ `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
+ protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields
+ }"
+ 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.
+ --- Many .condition.type values are consistent across
+ resources like Available, but because arbitrary conditions
+ can be useful (see .node.status.conditions), the ability
+ to deconflict is important. The regex it matches is
+ (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+ 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. \n Example:
+ \"example.net/gateway-controller\". \n 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).
+ \n 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). \n
+ 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. \n There are
+ two kinds of parent resources with \"Core\" support: \n
+ * Gateway (Gateway conformance profile) * Service (Mesh
+ conformance profile, experimental, ClusterIP Services
+ only) \n 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. \n 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. \n 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. \n 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. \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose
+ to support other parent resources. Implementations supporting
+ other types of parent resources MUST clearly document
+ how/if Port is interpreted. \n 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.
+ \n Support: Extended \n "
+ 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: \n * 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. Note that attaching Routes to Services
+ as Parents is part of experimental Mesh support and is
+ not supported for any other purpose. \n Implementations
+ MAY choose to support attaching Routes to other resources.
+ If that is the case, they MUST clearly document how SectionName
+ is interpreted. \n 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.
+ \n 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:
+ - controllerName
+ - parentRef
+ type: object
+ maxItems: 32
+ type: array
+ required:
+ - parents
+ type: object
+ required:
+ - spec
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: null
+ storedVersions: null
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ api-approved.kubernetes.io: https://github.com/kubernetes-sigs/gateway-api/pull/2466
+ gateway.networking.k8s.io/bundle-version: v1.0.0
+ gateway.networking.k8s.io/channel: experimental
+ creationTimestamp: null
+ name: udproutes.gateway.networking.k8s.io
+spec:
+ group: gateway.networking.k8s.io
+ names:
+ categories:
+ - gateway-api
+ kind: UDPRoute
+ listKind: UDPRouteList
+ plural: udproutes
+ singular: udproute
+ scope: Namespaced
+ versions:
+ - additionalPrinterColumns:
+ - jsonPath: .metadata.creationTimestamp
+ name: Age
+ type: date
+ name: v1alpha2
+ 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. \n There are two kinds of parent resources with \"Core\"
+ support: \n * Gateway (Gateway conformance profile) * Service (Mesh
+ conformance profile, experimental, ClusterIP Services only) This
+ API may be extended in the future to support additional kinds of
+ parent resources. \n ParentRefs must be _distinct_. This means either
+ that: \n * 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.
+ \n Some examples: \n * 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`. \n 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. \n 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. \n 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. \n 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. \n "
+ 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: \n * Gateway (Gateway conformance profile) * Service
+ (Mesh conformance profile, experimental, ClusterIP Services only)
+ \n This API may be extended in the future to support additional
+ kinds of parent resources. \n 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). \n 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. \n There are two
+ kinds of parent resources with \"Core\" support: \n * Gateway
+ (Gateway conformance profile) * Service (Mesh conformance
+ profile, experimental, ClusterIP Services only) \n 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. \n 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.
+ \n 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.
+ \n 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.
+ \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose to support other parent
+ resources. Implementations supporting other types of parent
+ resources MUST clearly document how/if Port is interpreted.
+ \n 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. \n
+ Support: Extended \n "
+ 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: \n * 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. Note that attaching
+ Routes to Services as Parents is part of experimental Mesh
+ support and is not supported for any other purpose. \n Implementations
+ MAY choose to support attaching Routes to other resources.
+ If that is the case, they MUST clearly document how SectionName
+ is interpreted. \n 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. \n 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-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 non-existent resource or a Service with no endpoints),
+ the underlying implementation MUST actively reject connection
+ attempts to this backend. Packet drops must respect weight;
+ if an invalid backend is requested to have 80% of the packets,
+ then 80% of packets must be dropped instead. \n Support: Core
+ for Kubernetes Service \n Support: Extended for Kubernetes
+ ServiceImport \n Support: Implementation-specific for any
+ other resource \n Support for weight: Extended"
+ items:
+ description: "BackendRef defines how a Route should forward
+ a request to a Kubernetes resource. \n 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. \n
+ \n When the BackendRef points to a Kubernetes Service, implementations
+ SHOULD honor the appProtocol field if it is set for the
+ target Service Port. \n Implementations supporting appProtocol
+ SHOULD recognize the Kubernetes Standard Application Protocols
+ defined in KEP-3726. \n 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. \n 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. \n
+ \n 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\". \n Defaults to
+ \"Service\" when not specified. \n 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.
+ \n Support: Core (Services with a type other than ExternalName)
+ \n 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. \n
+ 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. \n 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. \n
+ 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. \n 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
+ type: object
+ maxItems: 16
+ minItems: 1
+ type: array
+ 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.
+ \n 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. \n 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. \n 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. \n A Route MUST be considered \"Accepted\"
+ if at least one of the Route's rules is implemented by the
+ Gateway. \n There are a number of cases where the \"Accepted\"
+ condition may not be set due to lack of controller visibility,
+ that includes when: \n * The Route refers to a non-existent
+ parent. * The Route is of a type that the controller does
+ not support. * The Route is in a namespace the controller
+ does not have access to."
+ items:
+ description: "Condition contains details for one aspect of
+ the current state of this API Resource. --- This struct
+ is intended for direct use as an array at the field path
+ .status.conditions. For example, \n type FooStatus struct{
+ // Represents the observations of a foo's current state.
+ // Known .status.conditions.type are: \"Available\", \"Progressing\",
+ and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
+ // +listType=map // +listMapKey=type Conditions []metav1.Condition
+ `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
+ protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields
+ }"
+ 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.
+ --- Many .condition.type values are consistent across
+ resources like Available, but because arbitrary conditions
+ can be useful (see .node.status.conditions), the ability
+ to deconflict is important. The regex it matches is
+ (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+ 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. \n Example:
+ \"example.net/gateway-controller\". \n 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).
+ \n 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). \n
+ 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. \n There are
+ two kinds of parent resources with \"Core\" support: \n
+ * Gateway (Gateway conformance profile) * Service (Mesh
+ conformance profile, experimental, ClusterIP Services
+ only) \n 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. \n 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. \n 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. \n 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. \n 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. \n 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. \n 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. \n 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. \n Implementations MAY choose
+ to support other parent resources. Implementations supporting
+ other types of parent resources MUST clearly document
+ how/if Port is interpreted. \n 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.
+ \n Support: Extended \n "
+ 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: \n * 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. Note that attaching Routes to Services
+ as Parents is part of experimental Mesh support and is
+ not supported for any other purpose. \n Implementations
+ MAY choose to support attaching Routes to other resources.
If that is the case, they MUST clearly document how SectionName
is interpreted. \n When unspecified (empty string), this
will reference the entire resource. For the purpose of
@@ -2385,5 +11233,5 @@ status:
acceptedNames:
kind: ""
plural: ""
- conditions: []
- storedVersions: []
\ No newline at end of file
+ conditions: null
+ storedVersions: null
diff --git a/deploy/manifests/static/stunner-crd.yaml b/deploy/manifests/static/stunner-crd.yaml
index 04457aed..db856cb4 100644
--- a/deploy/manifests/static/stunner-crd.yaml
+++ b/deploy/manifests/static/stunner-crd.yaml
@@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.8.0
- creationTimestamp: null
+ controller-gen.kubebuilder.io/version: v0.14.0
name: gatewayconfigs.stunner.l7mp.io
spec:
group: stunner.l7mp.io
@@ -15,10 +14,160 @@ spec:
listKind: GatewayConfigList
plural: gatewayconfigs
shortNames:
- - gtwconf
+ - gwconf
singular: gatewayconfig
scope: Namespaced
versions:
+ - additionalPrinterColumns:
+ - jsonPath: .spec.realm
+ name: Realm
+ type: string
+ - jsonPath: .spec.dataplane
+ name: Dataplane
+ type: string
+ - jsonPath: .metadata.creationTimestamp
+ name: Age
+ type: date
+ name: v1
+ schema:
+ openAPIV3Schema:
+ description: GatewayConfig is the Schema for the gatewayconfigs API
+ 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: GatewayConfigSpec defines the desired state of GatewayConfig
+ properties:
+ authLifetime:
+ description: AuthLifetime defines the lifetime of "longterm" authentication
+ credentials in seconds.
+ format: int32
+ type: integer
+ authRef:
+ description: |-
+ Note that externally set credentials override any inline auth credentials (AuthType,
+ AuthUsername, etc.): if AuthRef is nonempty then it is expected that the referenced
+ Secret exists and *all* authentication credentials are correctly set in the referenced
+ Secret (username/password or shared secret). Mixing of credential sources
+ (inline/external) is not supported.
+ 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
+ authType:
+ default: plaintext
+ description: AuthType is the type of the STUN/TURN authentication
+ mechanism.
+ pattern: ^plaintext|static|longterm|ephemeral|timewindowed$
+ type: string
+ dataplane:
+ default: default
+ description: |-
+ Dataplane defines the dataplane (stunnerd image, version, etc) for STUNner gateways
+ using this GatewayConfig.
+ type: string
+ loadBalancerServiceAnnotations:
+ additionalProperties:
+ type: string
+ description: |-
+ LoadBalancerServiceAnnotations is a list of annotations that will go into the
+ LoadBalancer services created automatically by the operator to wrap Gateways.
+
+
+ NOTE: removing annotations from a GatewayConfig will not result in the removal of the
+ corresponding annotations from the LoadBalancer service, in order to prevent the
+ accidental removal of an annotation installed there by Kubernetes or the cloud
+ provider. If you really want to remove an annotation, do this manually or simply remove
+ all Gateways (which will remove the corresponding LoadBalancer services), update the
+ GatewayConfig and then recreate the Gateways, so that the newly created LoadBalancer
+ services will contain the required annotations.
+ type: object
+ logLevel:
+ description: LogLevel specifies the default loglevel for the STUNner
+ daemon.
+ type: string
+ password:
+ description: Password defines the `password` credential for "plaintext"
+ authentication.
+ pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$
+ type: string
+ realm:
+ default: stunner.l7mp.io
+ description: |-
+ Realm defines the STUN/TURN authentication realm to be used for clients toauthenticate
+ with STUNner.
+
+
+ The realm must consist of lower case alphanumeric characters or '-', and must start and
+ end with an alphanumeric character. No other punctuation is allowed.
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ sharedSecret:
+ description: SharedSecret defines the shared secret to be used for
+ "longterm" authentication.
+ type: string
+ userName:
+ description: Username defines the `username` credential for "plaintext"
+ authentication.
+ pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$
+ type: string
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources: {}
- additionalPrinterColumns:
- jsonPath: .spec.realm
name: Realm
@@ -35,14 +184,19 @@ spec:
description: GatewayConfig is the Schema for the gatewayconfigs API
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'
+ 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'
+ 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
@@ -55,25 +209,24 @@ spec:
format: int32
type: integer
authRef:
- description: 'Note that externally set credentials override any inline
- auth credentials (AuthType, AuthUsername, etc.): if AuthRef is nonempty
- then it is expected that the referenced Secret exists and *all*
- authentication credentials are correctly set in the referenced Secret
- (username/password or shared secret). Mixing of credential sources
- (inline/external) is not supported.'
+ description: |-
+ Note that externally set credentials override any inline auth credentials (AuthType,
+ AuthUsername, etc.): if AuthRef is nonempty then it is expected that the referenced
+ Secret exists and *all* authentication credentials are correctly set in the referenced
+ Secret (username/password or shared secret). Mixing of credential sources
+ (inline/external) is not supported.
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.
+ 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 "HTTPRoute"
- or "Service".
+ 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])?$
@@ -84,12 +237,18 @@ spec:
minLength: 1
type: string
namespace:
- description: "Namespace is the namespace of the backend. When
- unspecified, the local namespace is inferred. \n Note that when
- a namespace is specified, a ReferenceGrant object is required
- in the referent namespace to allow that namespace's owner to
- accept the reference. See the ReferenceGrant documentation for
- details. \n Support: Core"
+ 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])?$
@@ -103,27 +262,35 @@ spec:
mechanism.
pattern: ^plaintext|static|longterm|ephemeral|timewindowed$
type: string
+ dataplane:
+ default: default
+ description: |-
+ Dataplane defines the TURN server to set up for the STUNner Gateways using this
+ GatewayConfig. Can be used to select the stunnerd image repo and version or deploy into
+ the host-network namespace.
+ type: string
healthCheckEndpoint:
- description: HealthCheckEndpoint is the URI of the form `http://address:port`
- exposed for external HTTP health-checking. A liveness probe responder
- will be exposed on path `/live` and readiness probe on path `/ready`.
- The scheme (`http://`) is mandatory, default is to enable health-checking
- at "http://0.0.0.0:8086".
+ description: |-
+ HealthCheckEndpoint is the URI of the form `http://address:port` exposed for external
+ HTTP health-checking. A liveness probe responder will be exposed on path `/live` and
+ readiness probe on path `/ready`. The scheme (`http://`) is mandatory, default is to
+ enable health-checking at "http://0.0.0.0:8086".
type: string
loadBalancerServiceAnnotations:
additionalProperties:
type: string
- description: "LoadBalancerServiceAnnotations is a list of annotations
- that will go into the LoadBalancer services created automatically
- by the operator to wrap Gateways. \n NOTE: removing annotations
- from a GatewayConfig will not result in the removal of the corresponding
- annotations from the LoadBalancer service, in order to prevent the
- accidental removal of an annotation installed there by Kubernetes
- or the cloud provider. If you really want to remove an annotation,
- do this manually or simply remove all Gateways (which will remove
- the corresponding LoadBalancer services), update the GatewayConfig
- and then recreate the Gateways, so that the newly created LoadBalancer
- services will contain the required annotations."
+ description: |-
+ LoadBalancerServiceAnnotations is a list of annotations that will go into the
+ LoadBalancer services created automatically by the operator to wrap Gateways.
+
+
+ NOTE: removing annotations from a GatewayConfig will not result in the removal of the
+ corresponding annotations from the LoadBalancer service, in order to prevent the
+ accidental removal of an annotation installed there by Kubernetes or the cloud
+ provider. If you really want to remove an annotation, do this manually or simply remove
+ all Gateways (which will remove the corresponding LoadBalancer services), update the
+ GatewayConfig and then recreate the Gateways, so that the newly created LoadBalancer
+ services will contain the required annotations.
type: object
logLevel:
description: LogLevel specifies the default loglevel for the STUNner
@@ -135,9 +302,10 @@ spec:
format: int32
type: integer
metricsEndpoint:
- description: MetricsEndpoint is the URI in the form `http://address:port/path`
- exposed for metric scraping (Prometheus). The scheme (`http://`)
- is mandatory. Default is to expose no metric endpoint.
+ description: |-
+ MetricsEndpoint is the URI in the form `http://address:port/path` exposed for metric
+ scraping (Prometheus). The scheme (`http://`) is mandatory. Default is to expose no
+ metric endpoint.
type: string
minPort:
description: MinRelayPort is the smallest relay port assigned for
@@ -151,11 +319,13 @@ spec:
type: string
realm:
default: stunner.l7mp.io
- description: "Realm defines the STUN/TURN authentication realm to
- be used for clients toauthenticate with STUNner. \n The realm must
- consist of lower case alphanumeric characters or '-', and must start
- and end with an alphanumeric character. No other punctuation is
- allowed."
+ description: |-
+ Realm defines the STUN/TURN authentication realm to be used for clients toauthenticate
+ with STUNner.
+
+
+ The realm must consist of lower case alphanumeric characters or '-', and must start and
+ end with an alphanumeric character. No other punctuation is allowed.
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
sharedSecret:
@@ -164,8 +334,9 @@ spec:
type: string
stunnerConfig:
default: stunnerd-config
- description: StunnerConfig specifies the name of the ConfigMap into
- which the operator renders the stunnerd configfile.
+ description: |-
+ StunnerConfig specifies the name of the ConfigMap into which the operator renders the
+ stunnerd configfile.
maxLength: 64
pattern: ^[a-zA-Z]([-a-zA-Z0-9]*[a-zA-Z0-9])?$
type: string
@@ -177,11 +348,4024 @@ spec:
type: object
type: object
served: true
- storage: true
+ storage: false
subresources: {}
-status:
- acceptedNames:
- kind: ""
- plural: ""
- conditions: []
- storedVersions: []
\ No newline at end of file
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.14.0
+ name: staticservices.stunner.l7mp.io
+spec:
+ group: stunner.l7mp.io
+ names:
+ categories:
+ - stunner
+ kind: StaticService
+ listKind: StaticServiceList
+ plural: staticservices
+ shortNames:
+ - ssvc
+ singular: staticservice
+ scope: Namespaced
+ versions:
+ - name: v1
+ schema:
+ openAPIV3Schema:
+ description: |-
+ StaticService is a set of static IP address prefixes STUNner allows access to via a UDPRoute (or
+ TCPRoute in the future). In contrast to Kubernetes Services, StaticServices expose all ports on
+ the given IPs. See also https://github.com/kubernetes/enhancements/pull/2611.
+ 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 behavior of a service.
+ properties:
+ prefixes:
+ description: Prefixes is a list of IP address prefixes reachable via
+ this route.
+ items:
+ type: string
+ type: array
+ required:
+ - prefixes
+ type: object
+ type: object
+ served: true
+ storage: true
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: |-
+ StaticService is a set of static IP address prefixes STUNner allows access to via a Route. The
+ purpose is to allow a Service-like CRD containing a set of static IP address prefixes to be set
+ as the backend of a UDPRoute (or 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 behavior of a service.
+ properties:
+ ports:
+ description: The list of ports reachable via this service (currently
+ omitted).
+ items:
+ description: ServicePort contains information on service's port.
+ properties:
+ appProtocol:
+ description: |-
+ The application protocol for this port.
+ This is used as a hint for implementations to offer richer behavior for protocols that they understand.
+ This field follows standard Kubernetes label syntax.
+ Valid values are either:
+
+
+ * Un-prefixed protocol names - reserved for IANA standard service names (as per
+ RFC-6335 and https://www.iana.org/assignments/service-names).
+
+
+ * Kubernetes-defined prefixed names:
+ * 'kubernetes.io/h2c' - HTTP/2 prior knowledge over cleartext as described in https://www.rfc-editor.org/rfc/rfc9113.html#name-starting-http-2-with-prior-
+ * 'kubernetes.io/ws' - WebSocket over cleartext as described in https://www.rfc-editor.org/rfc/rfc6455
+ * 'kubernetes.io/wss' - WebSocket over TLS as described in https://www.rfc-editor.org/rfc/rfc6455
+
+
+ * Other protocols should use implementation-defined prefixed names such as
+ mycompany.com/my-custom-protocol.
+ type: string
+ name:
+ description: |-
+ The name of this port within the service. This must be a DNS_LABEL.
+ All ports within a ServiceSpec must have unique names. When considering
+ the endpoints for a Service, this must match the 'name' field in the
+ EndpointPort.
+ Optional if only one ServicePort is defined on this service.
+ type: string
+ nodePort:
+ description: |-
+ The port on each node on which this service is exposed when type is
+ NodePort or LoadBalancer. Usually assigned by the system. If a value is
+ specified, in-range, and not in use it will be used, otherwise the
+ operation will fail. If not specified, a port will be allocated if this
+ Service requires one. If this field is specified when creating a
+ Service which does not need it, creation will fail. This field will be
+ wiped when updating a Service to no longer need it (e.g. changing type
+ from NodePort to ClusterIP).
+ More info: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport
+ format: int32
+ type: integer
+ port:
+ description: The port that will be exposed by this service.
+ format: int32
+ type: integer
+ protocol:
+ default: TCP
+ description: |-
+ The IP protocol for this port. Supports "TCP", "UDP", and "SCTP".
+ Default is TCP.
+ type: string
+ targetPort:
+ anyOf:
+ - type: integer
+ - type: string
+ description: |-
+ Number or name of the port to access on the pods targeted by the service.
+ Number must be in the range 1 to 65535. Name must be an IANA_SVC_NAME.
+ If this is a string, it will be looked up as a named port in the
+ target Pod's container ports. If this is not specified, the value
+ of the 'port' field is used (an identity map).
+ This field is ignored for services with clusterIP=None, and should be
+ omitted or set equal to the 'port' field.
+ More info: https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service
+ x-kubernetes-int-or-string: true
+ required:
+ - port
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - port
+ - protocol
+ x-kubernetes-list-type: map
+ prefixes:
+ description: Prefixes is a list of IP address prefixes reachable via
+ this route.
+ items:
+ type: string
+ type: array
+ required:
+ - prefixes
+ type: object
+ type: object
+ served: true
+ storage: false
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.14.0
+ name: dataplanes.stunner.l7mp.io
+spec:
+ group: stunner.l7mp.io
+ names:
+ categories:
+ - stunner
+ kind: Dataplane
+ listKind: DataplaneList
+ plural: dataplanes
+ shortNames:
+ - dps
+ singular: dataplane
+ scope: Cluster
+ versions:
+ - name: v1
+ schema:
+ openAPIV3Schema:
+ description: |-
+ Dataplane is a collection of configuration parameters that can be used for spawning a `stunnerd`
+ instance for a Gateway. Labels and annotations on the Dataplane object will be copied verbatim
+ into the target Deployment.
+ 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 behavior of a Dataplane resource.
+ properties:
+ affinity:
+ description: Scheduling constraints.
+ properties:
+ nodeAffinity:
+ description: Describes node affinity scheduling rules for the
+ pod.
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ description: |-
+ The scheduler will prefer to schedule pods to nodes that satisfy
+ the affinity expressions specified by this field, but it may choose
+ a node that violates one or more of the expressions. The node that is
+ most preferred is the one with the greatest sum of weights, i.e.
+ for each node that meets all of the scheduling requirements (resource
+ request, requiredDuringScheduling affinity expressions, etc.),
+ compute a sum by iterating through the elements of this field and adding
+ "weight" to the sum if the node matches the corresponding matchExpressions; the
+ node(s) with the highest sum are the most preferred.
+ items:
+ description: |-
+ An empty preferred scheduling term matches all objects with implicit weight 0
+ (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).
+ properties:
+ preference:
+ description: A node selector term, associated with the
+ corresponding weight.
+ properties:
+ matchExpressions:
+ description: A list of node selector requirements
+ by node's labels.
+ items:
+ description: |-
+ A node selector requirement is a selector that contains values, a key, and an operator
+ that relates the key and values.
+ properties:
+ key:
+ description: The label key that the selector
+ applies to.
+ type: string
+ operator:
+ description: |-
+ Represents a key's relationship to a set of values.
+ Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+ type: string
+ values:
+ description: |-
+ 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. If the operator is Gt or Lt, the values
+ array must have a single element, which will be interpreted as an integer.
+ This array is replaced during a strategic merge patch.
+ items:
+ type: string
+ type: array
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ matchFields:
+ description: A list of node selector requirements
+ by node's fields.
+ items:
+ description: |-
+ A node selector requirement is a selector that contains values, a key, and an operator
+ that relates the key and values.
+ properties:
+ key:
+ description: The label key that the selector
+ applies to.
+ type: string
+ operator:
+ description: |-
+ Represents a key's relationship to a set of values.
+ Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+ type: string
+ values:
+ description: |-
+ 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. If the operator is Gt or Lt, the values
+ array must have a single element, which will be interpreted as an integer.
+ This array is replaced during a strategic merge patch.
+ items:
+ type: string
+ type: array
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ type: object
+ x-kubernetes-map-type: atomic
+ weight:
+ description: Weight associated with matching the corresponding
+ nodeSelectorTerm, in the range 1-100.
+ format: int32
+ type: integer
+ required:
+ - preference
+ - weight
+ type: object
+ type: array
+ requiredDuringSchedulingIgnoredDuringExecution:
+ description: |-
+ If the affinity requirements specified by this field are not met at
+ scheduling time, the pod will not be scheduled onto the node.
+ If the affinity requirements specified by this field cease to be met
+ at some point during pod execution (e.g. due to an update), the system
+ may or may not try to eventually evict the pod from its node.
+ properties:
+ nodeSelectorTerms:
+ description: Required. A list of node selector terms.
+ The terms are ORed.
+ items:
+ description: |-
+ A null or empty node selector term matches no objects. The requirements of
+ them are ANDed.
+ The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.
+ properties:
+ matchExpressions:
+ description: A list of node selector requirements
+ by node's labels.
+ items:
+ description: |-
+ A node selector requirement is a selector that contains values, a key, and an operator
+ that relates the key and values.
+ properties:
+ key:
+ description: The label key that the selector
+ applies to.
+ type: string
+ operator:
+ description: |-
+ Represents a key's relationship to a set of values.
+ Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+ type: string
+ values:
+ description: |-
+ 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. If the operator is Gt or Lt, the values
+ array must have a single element, which will be interpreted as an integer.
+ This array is replaced during a strategic merge patch.
+ items:
+ type: string
+ type: array
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ matchFields:
+ description: A list of node selector requirements
+ by node's fields.
+ items:
+ description: |-
+ A node selector requirement is a selector that contains values, a key, and an operator
+ that relates the key and values.
+ properties:
+ key:
+ description: The label key that the selector
+ applies to.
+ type: string
+ operator:
+ description: |-
+ Represents a key's relationship to a set of values.
+ Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+ type: string
+ values:
+ description: |-
+ 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. If the operator is Gt or Lt, the values
+ array must have a single element, which will be interpreted as an integer.
+ This array is replaced during a strategic merge patch.
+ items:
+ type: string
+ type: array
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ type: object
+ x-kubernetes-map-type: atomic
+ type: array
+ required:
+ - nodeSelectorTerms
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ podAffinity:
+ description: Describes pod affinity scheduling rules (e.g. co-locate
+ this pod in the same node, zone, etc. as some other pod(s)).
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ description: |-
+ The scheduler will prefer to schedule pods to nodes that satisfy
+ the affinity expressions specified by this field, but it may choose
+ a node that violates one or more of the expressions. The node that is
+ most preferred is the one with the greatest sum of weights, i.e.
+ for each node that meets all of the scheduling requirements (resource
+ request, requiredDuringScheduling affinity expressions, etc.),
+ compute a sum by iterating through the elements of this field and adding
+ "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the
+ node(s) with the highest sum are the most preferred.
+ items:
+ description: The weights of all of the matched WeightedPodAffinityTerm
+ fields are added per-node to find the most preferred node(s)
+ properties:
+ podAffinityTerm:
+ description: Required. A pod affinity term, associated
+ with the corresponding weight.
+ properties:
+ labelSelector:
+ description: |-
+ A label query over a set of resources, in this case pods.
+ If it's null, this PodAffinityTerm matches with no Pods.
+ 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
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ 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
+ matchLabelKeys:
+ description: |-
+ MatchLabelKeys is a set of pod label keys to select which pods will
+ be taken into consideration. The keys are used to lookup values from the
+ incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)`
+ to select the group of existing pods which pods will be taken into consideration
+ for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming
+ pod labels will be ignored. The default value is empty.
+ The same key is forbidden to exist in both MatchLabelKeys and LabelSelector.
+ Also, MatchLabelKeys cannot be set when LabelSelector isn't set.
+ This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ description: |-
+ MismatchLabelKeys is a set of pod label keys to select which pods will
+ be taken into consideration. The keys are used to lookup values from the
+ incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)`
+ to select the group of existing pods which pods will be taken into consideration
+ for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming
+ pod labels will be ignored. The default value is empty.
+ The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector.
+ Also, MismatchLabelKeys cannot be set when LabelSelector isn't set.
+ This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ description: |-
+ A label query over the set of namespaces that the term applies to.
+ The term is applied to the union of the namespaces selected by this field
+ and the ones listed in the namespaces field.
+ null selector and null or empty namespaces list means "this pod's namespace".
+ An empty selector ({}) matches all namespaces.
+ 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
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ 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
+ namespaces:
+ description: |-
+ namespaces specifies a static list of namespace names that the term applies to.
+ The term is applied to the union of the namespaces listed in this field
+ and the ones selected by namespaceSelector.
+ null or empty namespaces list and null namespaceSelector means "this pod's namespace".
+ items:
+ type: string
+ type: array
+ topologyKey:
+ description: |-
+ This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching
+ the labelSelector in the specified namespaces, where co-located is defined as running on a node
+ whose value of the label with key topologyKey matches that of any node on which any of the
+ selected pods is running.
+ Empty topologyKey is not allowed.
+ type: string
+ required:
+ - topologyKey
+ type: object
+ weight:
+ description: |-
+ weight associated with matching the corresponding podAffinityTerm,
+ in the range 1-100.
+ format: int32
+ type: integer
+ required:
+ - podAffinityTerm
+ - weight
+ type: object
+ type: array
+ requiredDuringSchedulingIgnoredDuringExecution:
+ description: |-
+ If the affinity requirements specified by this field are not met at
+ scheduling time, the pod will not be scheduled onto the node.
+ If the affinity requirements specified by this field cease to be met
+ at some point during pod execution (e.g. due to a pod label update), the
+ system may or may not try to eventually evict the pod from its node.
+ When there are multiple elements, the lists of nodes corresponding to each
+ podAffinityTerm are intersected, i.e. all terms must be satisfied.
+ items:
+ description: |-
+ Defines a set of pods (namely those matching the labelSelector
+ relative to the given namespace(s)) that this pod should be
+ co-located (affinity) or not co-located (anti-affinity) with,
+ where co-located is defined as running on a node whose value of
+ the label with key matches that of any node on which
+ a pod of the set of pods is running
+ properties:
+ labelSelector:
+ description: |-
+ A label query over a set of resources, in this case pods.
+ If it's null, this PodAffinityTerm matches with no Pods.
+ 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
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ 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
+ matchLabelKeys:
+ description: |-
+ MatchLabelKeys is a set of pod label keys to select which pods will
+ be taken into consideration. The keys are used to lookup values from the
+ incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)`
+ to select the group of existing pods which pods will be taken into consideration
+ for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming
+ pod labels will be ignored. The default value is empty.
+ The same key is forbidden to exist in both MatchLabelKeys and LabelSelector.
+ Also, MatchLabelKeys cannot be set when LabelSelector isn't set.
+ This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ description: |-
+ MismatchLabelKeys is a set of pod label keys to select which pods will
+ be taken into consideration. The keys are used to lookup values from the
+ incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)`
+ to select the group of existing pods which pods will be taken into consideration
+ for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming
+ pod labels will be ignored. The default value is empty.
+ The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector.
+ Also, MismatchLabelKeys cannot be set when LabelSelector isn't set.
+ This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ description: |-
+ A label query over the set of namespaces that the term applies to.
+ The term is applied to the union of the namespaces selected by this field
+ and the ones listed in the namespaces field.
+ null selector and null or empty namespaces list means "this pod's namespace".
+ An empty selector ({}) matches all namespaces.
+ 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
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ 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
+ namespaces:
+ description: |-
+ namespaces specifies a static list of namespace names that the term applies to.
+ The term is applied to the union of the namespaces listed in this field
+ and the ones selected by namespaceSelector.
+ null or empty namespaces list and null namespaceSelector means "this pod's namespace".
+ items:
+ type: string
+ type: array
+ topologyKey:
+ description: |-
+ This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching
+ the labelSelector in the specified namespaces, where co-located is defined as running on a node
+ whose value of the label with key topologyKey matches that of any node on which any of the
+ selected pods is running.
+ Empty topologyKey is not allowed.
+ type: string
+ required:
+ - topologyKey
+ type: object
+ type: array
+ type: object
+ podAntiAffinity:
+ description: Describes pod anti-affinity scheduling rules (e.g.
+ avoid putting this pod in the same node, zone, etc. as some
+ other pod(s)).
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ description: |-
+ The scheduler will prefer to schedule pods to nodes that satisfy
+ the anti-affinity expressions specified by this field, but it may choose
+ a node that violates one or more of the expressions. The node that is
+ most preferred is the one with the greatest sum of weights, i.e.
+ for each node that meets all of the scheduling requirements (resource
+ request, requiredDuringScheduling anti-affinity expressions, etc.),
+ compute a sum by iterating through the elements of this field and adding
+ "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the
+ node(s) with the highest sum are the most preferred.
+ items:
+ description: The weights of all of the matched WeightedPodAffinityTerm
+ fields are added per-node to find the most preferred node(s)
+ properties:
+ podAffinityTerm:
+ description: Required. A pod affinity term, associated
+ with the corresponding weight.
+ properties:
+ labelSelector:
+ description: |-
+ A label query over a set of resources, in this case pods.
+ If it's null, this PodAffinityTerm matches with no Pods.
+ 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
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ 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
+ matchLabelKeys:
+ description: |-
+ MatchLabelKeys is a set of pod label keys to select which pods will
+ be taken into consideration. The keys are used to lookup values from the
+ incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)`
+ to select the group of existing pods which pods will be taken into consideration
+ for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming
+ pod labels will be ignored. The default value is empty.
+ The same key is forbidden to exist in both MatchLabelKeys and LabelSelector.
+ Also, MatchLabelKeys cannot be set when LabelSelector isn't set.
+ This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ description: |-
+ MismatchLabelKeys is a set of pod label keys to select which pods will
+ be taken into consideration. The keys are used to lookup values from the
+ incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)`
+ to select the group of existing pods which pods will be taken into consideration
+ for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming
+ pod labels will be ignored. The default value is empty.
+ The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector.
+ Also, MismatchLabelKeys cannot be set when LabelSelector isn't set.
+ This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ description: |-
+ A label query over the set of namespaces that the term applies to.
+ The term is applied to the union of the namespaces selected by this field
+ and the ones listed in the namespaces field.
+ null selector and null or empty namespaces list means "this pod's namespace".
+ An empty selector ({}) matches all namespaces.
+ 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
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ 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
+ namespaces:
+ description: |-
+ namespaces specifies a static list of namespace names that the term applies to.
+ The term is applied to the union of the namespaces listed in this field
+ and the ones selected by namespaceSelector.
+ null or empty namespaces list and null namespaceSelector means "this pod's namespace".
+ items:
+ type: string
+ type: array
+ topologyKey:
+ description: |-
+ This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching
+ the labelSelector in the specified namespaces, where co-located is defined as running on a node
+ whose value of the label with key topologyKey matches that of any node on which any of the
+ selected pods is running.
+ Empty topologyKey is not allowed.
+ type: string
+ required:
+ - topologyKey
+ type: object
+ weight:
+ description: |-
+ weight associated with matching the corresponding podAffinityTerm,
+ in the range 1-100.
+ format: int32
+ type: integer
+ required:
+ - podAffinityTerm
+ - weight
+ type: object
+ type: array
+ requiredDuringSchedulingIgnoredDuringExecution:
+ description: |-
+ If the anti-affinity requirements specified by this field are not met at
+ scheduling time, the pod will not be scheduled onto the node.
+ If the anti-affinity requirements specified by this field cease to be met
+ at some point during pod execution (e.g. due to a pod label update), the
+ system may or may not try to eventually evict the pod from its node.
+ When there are multiple elements, the lists of nodes corresponding to each
+ podAffinityTerm are intersected, i.e. all terms must be satisfied.
+ items:
+ description: |-
+ Defines a set of pods (namely those matching the labelSelector
+ relative to the given namespace(s)) that this pod should be
+ co-located (affinity) or not co-located (anti-affinity) with,
+ where co-located is defined as running on a node whose value of
+ the label with key matches that of any node on which
+ a pod of the set of pods is running
+ properties:
+ labelSelector:
+ description: |-
+ A label query over a set of resources, in this case pods.
+ If it's null, this PodAffinityTerm matches with no Pods.
+ 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
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ 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
+ matchLabelKeys:
+ description: |-
+ MatchLabelKeys is a set of pod label keys to select which pods will
+ be taken into consideration. The keys are used to lookup values from the
+ incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)`
+ to select the group of existing pods which pods will be taken into consideration
+ for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming
+ pod labels will be ignored. The default value is empty.
+ The same key is forbidden to exist in both MatchLabelKeys and LabelSelector.
+ Also, MatchLabelKeys cannot be set when LabelSelector isn't set.
+ This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ description: |-
+ MismatchLabelKeys is a set of pod label keys to select which pods will
+ be taken into consideration. The keys are used to lookup values from the
+ incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)`
+ to select the group of existing pods which pods will be taken into consideration
+ for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming
+ pod labels will be ignored. The default value is empty.
+ The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector.
+ Also, MismatchLabelKeys cannot be set when LabelSelector isn't set.
+ This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ description: |-
+ A label query over the set of namespaces that the term applies to.
+ The term is applied to the union of the namespaces selected by this field
+ and the ones listed in the namespaces field.
+ null selector and null or empty namespaces list means "this pod's namespace".
+ An empty selector ({}) matches all namespaces.
+ 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
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ 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
+ namespaces:
+ description: |-
+ namespaces specifies a static list of namespace names that the term applies to.
+ The term is applied to the union of the namespaces listed in this field
+ and the ones selected by namespaceSelector.
+ null or empty namespaces list and null namespaceSelector means "this pod's namespace".
+ items:
+ type: string
+ type: array
+ topologyKey:
+ description: |-
+ This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching
+ the labelSelector in the specified namespaces, where co-located is defined as running on a node
+ whose value of the label with key topologyKey matches that of any node on which any of the
+ selected pods is running.
+ Empty topologyKey is not allowed.
+ type: string
+ required:
+ - topologyKey
+ type: object
+ type: array
+ type: object
+ type: object
+ annotations:
+ additionalProperties:
+ type: string
+ description: |-
+ Custom annotations to add to dataplane pods. Note that this does not affect the
+ annotations added to the Deployment (this come from the correspnding Gateway), just the
+ pods. Note also that mandatory pod annotations override whatever you set here on
+ conflict, and the annotations set here override annotations manually added to the pods.
+ type: object
+ args:
+ description: Arguments to the entrypoint.
+ items:
+ type: string
+ type: array
+ command:
+ description: 'Entrypoint array. Defaults: "stunnerd".'
+ items:
+ type: string
+ type: array
+ containerSecurityContext:
+ description: |-
+ ContainerSecurityContext holds container-level security attributes specifically for the
+ stunnerd container.
+ properties:
+ allowPrivilegeEscalation:
+ description: |-
+ AllowPrivilegeEscalation controls whether a process can gain more
+ privileges than its parent process. This bool directly controls if
+ the no_new_privs flag will be set on the container process.
+ AllowPrivilegeEscalation is true always when the container is:
+ 1) run as Privileged
+ 2) has CAP_SYS_ADMIN
+ Note that this field cannot be set when spec.os.name is windows.
+ type: boolean
+ capabilities:
+ description: |-
+ The capabilities to add/drop when running containers.
+ Defaults to the default set of capabilities granted by the container runtime.
+ Note that this field cannot be set when spec.os.name is windows.
+ properties:
+ add:
+ description: Added capabilities
+ items:
+ description: Capability represent POSIX capabilities type
+ type: string
+ type: array
+ drop:
+ description: Removed capabilities
+ items:
+ description: Capability represent POSIX capabilities type
+ type: string
+ type: array
+ type: object
+ privileged:
+ description: |-
+ Run container in privileged mode.
+ Processes in privileged containers are essentially equivalent to root on the host.
+ Defaults to false.
+ Note that this field cannot be set when spec.os.name is windows.
+ type: boolean
+ procMount:
+ description: |-
+ procMount denotes the type of proc mount to use for the containers.
+ The default is DefaultProcMount which uses the container runtime defaults for
+ readonly paths and masked paths.
+ This requires the ProcMountType feature flag to be enabled.
+ Note that this field cannot be set when spec.os.name is windows.
+ type: string
+ readOnlyRootFilesystem:
+ description: |-
+ Whether this container has a read-only root filesystem.
+ Default is false.
+ Note that this field cannot be set when spec.os.name is windows.
+ type: boolean
+ runAsGroup:
+ description: |-
+ The GID to run the entrypoint of the container process.
+ Uses runtime default if unset.
+ May also be set in PodSecurityContext. If set in both SecurityContext and
+ PodSecurityContext, the value specified in SecurityContext takes precedence.
+ Note that this field cannot be set when spec.os.name is windows.
+ format: int64
+ type: integer
+ runAsNonRoot:
+ description: |-
+ Indicates that the container must run as a non-root user.
+ If true, the Kubelet will validate the image at runtime to ensure that it
+ does not run as UID 0 (root) and fail to start the container if it does.
+ If unset or false, no such validation will be performed.
+ May also be set in PodSecurityContext. If set in both SecurityContext and
+ PodSecurityContext, the value specified in SecurityContext takes precedence.
+ type: boolean
+ runAsUser:
+ description: |-
+ The UID to run the entrypoint of the container process.
+ Defaults to user specified in image metadata if unspecified.
+ May also be set in PodSecurityContext. If set in both SecurityContext and
+ PodSecurityContext, the value specified in SecurityContext takes precedence.
+ Note that this field cannot be set when spec.os.name is windows.
+ format: int64
+ type: integer
+ seLinuxOptions:
+ description: |-
+ The SELinux context to be applied to the container.
+ If unspecified, the container runtime will allocate a random SELinux context for each
+ container. May also be set in PodSecurityContext. If set in both SecurityContext and
+ PodSecurityContext, the value specified in SecurityContext takes precedence.
+ Note that this field cannot be set when spec.os.name is windows.
+ properties:
+ level:
+ description: Level is SELinux level label that applies to
+ the container.
+ type: string
+ role:
+ description: Role is a SELinux role label that applies to
+ the container.
+ type: string
+ type:
+ description: Type is a SELinux type label that applies to
+ the container.
+ type: string
+ user:
+ description: User is a SELinux user label that applies to
+ the container.
+ type: string
+ type: object
+ seccompProfile:
+ description: |-
+ The seccomp options to use by this container. If seccomp options are
+ provided at both the pod & container level, the container options
+ override the pod options.
+ Note that this field cannot be set when spec.os.name is windows.
+ properties:
+ localhostProfile:
+ description: |-
+ localhostProfile indicates a profile defined in a file on the node should be used.
+ The profile must be preconfigured on the node to work.
+ Must be a descending path, relative to the kubelet's configured seccomp profile location.
+ Must be set if type is "Localhost". Must NOT be set for any other type.
+ type: string
+ type:
+ description: |-
+ type indicates which kind of seccomp profile will be applied.
+ Valid options are:
+
+
+ Localhost - a profile defined in a file on the node should be used.
+ RuntimeDefault - the container runtime default profile should be used.
+ Unconfined - no profile should be applied.
+ type: string
+ required:
+ - type
+ type: object
+ windowsOptions:
+ description: |-
+ The Windows specific settings applied to all containers.
+ If unspecified, the options from the PodSecurityContext will be used.
+ If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.
+ Note that this field cannot be set when spec.os.name is linux.
+ properties:
+ gmsaCredentialSpec:
+ description: |-
+ GMSACredentialSpec is where the GMSA admission webhook
+ (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the
+ GMSA credential spec named by the GMSACredentialSpecName field.
+ type: string
+ gmsaCredentialSpecName:
+ description: GMSACredentialSpecName is the name of the GMSA
+ credential spec to use.
+ type: string
+ hostProcess:
+ description: |-
+ HostProcess determines if a container should be run as a 'Host Process' container.
+ All of a Pod's containers must have the same effective HostProcess value
+ (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers).
+ In addition, if HostProcess is true then HostNetwork must also be set to true.
+ type: boolean
+ runAsUserName:
+ description: |-
+ The UserName in Windows to run the entrypoint of the container process.
+ Defaults to the user specified in image metadata if unspecified.
+ May also be set in PodSecurityContext. If set in both SecurityContext and
+ PodSecurityContext, the value specified in SecurityContext takes precedence.
+ type: string
+ type: object
+ type: object
+ disableHealthCheck:
+ description: |-
+ Disable health-checking. Default is to enable HTTP health-checks on port 8086: a
+ liveness probe responder will be exposed on path `/live` and readiness probe on path
+ `/ready`.
+ type: boolean
+ enableMetricsEndpoint:
+ description: |-
+ EnableMetricsEnpoint can be used to enable metrics scraping (Prometheus). If enabled, a
+ metrics endpoint will be available at http://0.0.0.0:8080 at all dataplane pods. Default
+ is no metrics collection.
+ type: boolean
+ env:
+ description: List of environment variables to set in the stunnerd
+ container.
+ items:
+ description: EnvVar represents an environment variable present in
+ a Container.
+ properties:
+ name:
+ description: Name of the environment variable. Must be a C_IDENTIFIER.
+ type: string
+ value:
+ description: |-
+ Variable references $(VAR_NAME) are expanded
+ using the previously defined environment variables in the container and
+ any service environment variables. If a variable cannot be resolved,
+ the reference in the input string will be unchanged. Double $$ are reduced
+ to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e.
+ "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)".
+ Escaped references will never be expanded, regardless of whether the variable
+ exists or not.
+ Defaults to "".
+ type: string
+ valueFrom:
+ description: Source for the environment variable's value. Cannot
+ be used if value is not empty.
+ properties:
+ configMapKeyRef:
+ description: Selects a key of a ConfigMap.
+ properties:
+ key:
+ description: The key to select.
+ type: string
+ name:
+ description: |-
+ Name of the referent.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?
+ type: string
+ optional:
+ description: Specify whether the ConfigMap or its key
+ must be defined
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ description: |-
+ Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`,
+ spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.
+ properties:
+ apiVersion:
+ description: Version of the schema the FieldPath is
+ written in terms of, defaults to "v1".
+ type: string
+ fieldPath:
+ description: Path of the field to select in the specified
+ API version.
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ description: |-
+ Selects a resource of the container: only resources limits and requests
+ (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.
+ properties:
+ containerName:
+ description: 'Container name: required for volumes,
+ optional for env vars'
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Specifies the output format of the exposed
+ resources, defaults to "1"
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ description: 'Required: resource to select'
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ description: Selects a key of a secret in the pod's namespace
+ properties:
+ key:
+ description: The key of the secret to select from. Must
+ be a valid secret key.
+ type: string
+ name:
+ description: |-
+ Name of the referent.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?
+ type: string
+ optional:
+ description: Specify whether the Secret or its key must
+ be defined
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ envFrom:
+ description: List of sources to populate environment variables in
+ the stunnerd container.
+ items:
+ description: EnvFromSource represents the source of a set of ConfigMaps
+ properties:
+ configMapRef:
+ description: The ConfigMap to select from
+ properties:
+ name:
+ description: |-
+ Name of the referent.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?
+ type: string
+ optional:
+ description: Specify whether the ConfigMap must be defined
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ prefix:
+ description: An optional identifier to prepend to each key in
+ the ConfigMap. Must be a C_IDENTIFIER.
+ type: string
+ secretRef:
+ description: The Secret to select from
+ properties:
+ name:
+ description: |-
+ Name of the referent.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?
+ type: string
+ optional:
+ description: Specify whether the Secret must be defined
+ type: boolean
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ type: array
+ hostNetwork:
+ description: |-
+ Host networking requested for the stunnerd pod to use the host's network namespace.
+ Can be used to implement public TURN servers with Kubernetes. Defaults to false.
+ type: boolean
+ image:
+ description: Container image name.
+ type: string
+ imagePullPolicy:
+ description: Image pull policy. One of Always, Never, IfNotPresent.
+ type: string
+ imagePullSecrets:
+ description: |-
+ ImagePullSecrets is an optional list of references to secrets to use for pulling the
+ stunnerd image. Note that the referenced secrets are not watched by the operator, so
+ modifications will in effect only for newly created pods. Also note that the Secret is
+ always searched in the same namespace as the Gateway, which allows to use separate pull
+ secrets per each namespace.
+ items:
+ description: |-
+ LocalObjectReference contains enough information to let you locate the
+ referenced object inside the same namespace.
+ properties:
+ name:
+ description: |-
+ Name of the referent.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?
+ type: string
+ type: object
+ x-kubernetes-map-type: atomic
+ type: array
+ labels:
+ additionalProperties:
+ type: string
+ description: |-
+ Custom labels to add to dataplane pods. Note that this does not affect the labels added
+ to the Deployment (those come from the Gateway), just the pods. Note also that mandatory
+ pod labels override whatever you set here on conflict. The only way to set pod labels is
+ here: whatever you set manually on the dataplane pod will be reset by the opetator.
+ type: object
+ replicas:
+ description: |-
+ Number of desired pods. If empty or set to 1, use whatever is in the target Deployment.
+ Otherwise, enforce this setting, overwiting whatever is set in the Deployment (this may
+ block autoscaling the dataplane though). Defaults to 1.
+ format: int32
+ type: integer
+ resources:
+ description: Resources required by stunnerd.
+ properties:
+ claims:
+ description: |-
+ Claims lists the names of resources, defined in spec.resourceClaims,
+ that are used by this container.
+
+
+ This is an alpha field and requires enabling the
+ DynamicResourceAllocation feature gate.
+
+
+ This field is immutable. It can only be set for containers.
+ items:
+ description: ResourceClaim references one entry in PodSpec.ResourceClaims.
+ properties:
+ name:
+ description: |-
+ Name must match the name of one entry in pod.spec.resourceClaims of
+ the Pod where this field is used. It makes that resource available
+ inside a container.
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ description: |-
+ Limits describes the maximum amount of compute resources allowed.
+ More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ description: |-
+ Requests describes the minimum amount of compute resources required.
+ If Requests is omitted for a container, it defaults to Limits if that is explicitly specified,
+ otherwise to an implementation-defined value. Requests cannot exceed Limits.
+ More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+ type: object
+ type: object
+ securityContext:
+ description: SecurityContext holds pod-level security attributes and
+ common container settings.
+ properties:
+ fsGroup:
+ description: |-
+ A special supplemental group that applies to all containers in a pod.
+ Some volume types allow the Kubelet to change the ownership of that volume
+ to be owned by the pod:
+
+
+ 1. The owning GID will be the FSGroup
+ 2. The setgid bit is set (new files created in the volume will be owned by FSGroup)
+ 3. The permission bits are OR'd with rw-rw----
+
+
+ If unset, the Kubelet will not modify the ownership and permissions of any volume.
+ Note that this field cannot be set when spec.os.name is windows.
+ format: int64
+ type: integer
+ fsGroupChangePolicy:
+ description: |-
+ fsGroupChangePolicy defines behavior of changing ownership and permission of the volume
+ before being exposed inside Pod. This field will only apply to
+ volume types which support fsGroup based ownership(and permissions).
+ It will have no effect on ephemeral volume types such as: secret, configmaps
+ and emptydir.
+ Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used.
+ Note that this field cannot be set when spec.os.name is windows.
+ type: string
+ runAsGroup:
+ description: |-
+ The GID to run the entrypoint of the container process.
+ Uses runtime default if unset.
+ May also be set in SecurityContext. If set in both SecurityContext and
+ PodSecurityContext, the value specified in SecurityContext takes precedence
+ for that container.
+ Note that this field cannot be set when spec.os.name is windows.
+ format: int64
+ type: integer
+ runAsNonRoot:
+ description: |-
+ Indicates that the container must run as a non-root user.
+ If true, the Kubelet will validate the image at runtime to ensure that it
+ does not run as UID 0 (root) and fail to start the container if it does.
+ If unset or false, no such validation will be performed.
+ May also be set in SecurityContext. If set in both SecurityContext and
+ PodSecurityContext, the value specified in SecurityContext takes precedence.
+ type: boolean
+ runAsUser:
+ description: |-
+ The UID to run the entrypoint of the container process.
+ Defaults to user specified in image metadata if unspecified.
+ May also be set in SecurityContext. If set in both SecurityContext and
+ PodSecurityContext, the value specified in SecurityContext takes precedence
+ for that container.
+ Note that this field cannot be set when spec.os.name is windows.
+ format: int64
+ type: integer
+ seLinuxOptions:
+ description: |-
+ The SELinux context to be applied to all containers.
+ If unspecified, the container runtime will allocate a random SELinux context for each
+ container. May also be set in SecurityContext. If set in
+ both SecurityContext and PodSecurityContext, the value specified in SecurityContext
+ takes precedence for that container.
+ Note that this field cannot be set when spec.os.name is windows.
+ properties:
+ level:
+ description: Level is SELinux level label that applies to
+ the container.
+ type: string
+ role:
+ description: Role is a SELinux role label that applies to
+ the container.
+ type: string
+ type:
+ description: Type is a SELinux type label that applies to
+ the container.
+ type: string
+ user:
+ description: User is a SELinux user label that applies to
+ the container.
+ type: string
+ type: object
+ seccompProfile:
+ description: |-
+ The seccomp options to use by the containers in this pod.
+ Note that this field cannot be set when spec.os.name is windows.
+ properties:
+ localhostProfile:
+ description: |-
+ localhostProfile indicates a profile defined in a file on the node should be used.
+ The profile must be preconfigured on the node to work.
+ Must be a descending path, relative to the kubelet's configured seccomp profile location.
+ Must be set if type is "Localhost". Must NOT be set for any other type.
+ type: string
+ type:
+ description: |-
+ type indicates which kind of seccomp profile will be applied.
+ Valid options are:
+
+
+ Localhost - a profile defined in a file on the node should be used.
+ RuntimeDefault - the container runtime default profile should be used.
+ Unconfined - no profile should be applied.
+ type: string
+ required:
+ - type
+ type: object
+ supplementalGroups:
+ description: |-
+ A list of groups applied to the first process run in each container, in addition
+ to the container's primary GID, the fsGroup (if specified), and group memberships
+ defined in the container image for the uid of the container process. If unspecified,
+ no additional groups are added to any container. Note that group memberships
+ defined in the container image for the uid of the container process are still effective,
+ even if they are not included in this list.
+ Note that this field cannot be set when spec.os.name is windows.
+ items:
+ format: int64
+ type: integer
+ type: array
+ sysctls:
+ description: |-
+ Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported
+ sysctls (by the container runtime) might fail to launch.
+ Note that this field cannot be set when spec.os.name is windows.
+ items:
+ description: Sysctl defines a kernel parameter to be set
+ properties:
+ name:
+ description: Name of a property to set
+ type: string
+ value:
+ description: Value of a property to set
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ windowsOptions:
+ description: |-
+ The Windows specific settings applied to all containers.
+ If unspecified, the options within a container's SecurityContext will be used.
+ If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.
+ Note that this field cannot be set when spec.os.name is linux.
+ properties:
+ gmsaCredentialSpec:
+ description: |-
+ GMSACredentialSpec is where the GMSA admission webhook
+ (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the
+ GMSA credential spec named by the GMSACredentialSpecName field.
+ type: string
+ gmsaCredentialSpecName:
+ description: GMSACredentialSpecName is the name of the GMSA
+ credential spec to use.
+ type: string
+ hostProcess:
+ description: |-
+ HostProcess determines if a container should be run as a 'Host Process' container.
+ All of a Pod's containers must have the same effective HostProcess value
+ (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers).
+ In addition, if HostProcess is true then HostNetwork must also be set to true.
+ type: boolean
+ runAsUserName:
+ description: |-
+ The UserName in Windows to run the entrypoint of the container process.
+ Defaults to the user specified in image metadata if unspecified.
+ May also be set in PodSecurityContext. If set in both SecurityContext and
+ PodSecurityContext, the value specified in SecurityContext takes precedence.
+ type: string
+ type: object
+ type: object
+ terminationGracePeriodSeconds:
+ description: Optional duration in seconds the stunnerd needs to terminate
+ gracefully. Defaults to 3600 seconds.
+ format: int64
+ type: integer
+ tolerations:
+ description: If specified, the pod's tolerations.
+ items:
+ description: |-
+ The pod this Toleration is attached to tolerates any taint that matches
+ the triple using the matching operator .
+ properties:
+ effect:
+ description: |-
+ Effect indicates the taint effect to match. Empty means match all taint effects.
+ When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.
+ type: string
+ key:
+ description: |-
+ Key is the taint key that the toleration applies to. Empty means match all taint keys.
+ If the key is empty, operator must be Exists; this combination means to match all values and all keys.
+ type: string
+ operator:
+ description: |-
+ Operator represents a key's relationship to the value.
+ Valid operators are Exists and Equal. Defaults to Equal.
+ Exists is equivalent to wildcard for value, so that a pod can
+ tolerate all taints of a particular category.
+ type: string
+ tolerationSeconds:
+ description: |-
+ TolerationSeconds represents the period of time the toleration (which must be
+ of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default,
+ it is not set, which means tolerate the taint forever (do not evict). Zero and
+ negative values will be treated as 0 (evict immediately) by the system.
+ format: int64
+ type: integer
+ value:
+ description: |-
+ Value is the taint value the toleration matches to.
+ If the operator is Exists, the value should be empty, otherwise just a regular string.
+ type: string
+ type: object
+ type: array
+ topologySpreadConstraints:
+ description: |-
+ TopologySpreadConstraints describes how stunnerd pods ought to spread across topology
+ domains.
+ items:
+ description: TopologySpreadConstraint specifies how to spread matching
+ pods among the given topology.
+ properties:
+ labelSelector:
+ description: |-
+ LabelSelector is used to find matching pods.
+ Pods that match this label selector are counted to determine the number of pods
+ in their corresponding topology domain.
+ 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
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ 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
+ matchLabelKeys:
+ description: |-
+ MatchLabelKeys is a set of pod label keys to select the pods over which
+ spreading will be calculated. The keys are used to lookup values from the
+ incoming pod labels, those key-value labels are ANDed with labelSelector
+ to select the group of existing pods over which spreading will be calculated
+ for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector.
+ MatchLabelKeys cannot be set when LabelSelector isn't set.
+ Keys that don't exist in the incoming pod labels will
+ be ignored. A null or empty list means only match against labelSelector.
+
+
+ This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default).
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ maxSkew:
+ description: |-
+ MaxSkew describes the degree to which pods may be unevenly distributed.
+ When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference
+ between the number of matching pods in the target topology and the global minimum.
+ The global minimum is the minimum number of matching pods in an eligible domain
+ or zero if the number of eligible domains is less than MinDomains.
+ For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same
+ labelSelector spread as 2/2/1:
+ In this case, the global minimum is 1.
+ | zone1 | zone2 | zone3 |
+ | P P | P P | P |
+ - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2;
+ scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2)
+ violate MaxSkew(1).
+ - if MaxSkew is 2, incoming pod can be scheduled onto any zone.
+ When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence
+ to topologies that satisfy it.
+ It's a required field. Default value is 1 and 0 is not allowed.
+ format: int32
+ type: integer
+ minDomains:
+ description: |-
+ MinDomains indicates a minimum number of eligible domains.
+ When the number of eligible domains with matching topology keys is less than minDomains,
+ Pod Topology Spread treats "global minimum" as 0, and then the calculation of Skew is performed.
+ And when the number of eligible domains with matching topology keys equals or greater than minDomains,
+ this value has no effect on scheduling.
+ As a result, when the number of eligible domains is less than minDomains,
+ scheduler won't schedule more than maxSkew Pods to those domains.
+ If value is nil, the constraint behaves as if MinDomains is equal to 1.
+ Valid values are integers greater than 0.
+ When value is not nil, WhenUnsatisfiable must be DoNotSchedule.
+
+
+ For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same
+ labelSelector spread as 2/2/2:
+ | zone1 | zone2 | zone3 |
+ | P P | P P | P P |
+ The number of domains is less than 5(MinDomains), so "global minimum" is treated as 0.
+ In this situation, new pod with the same labelSelector cannot be scheduled,
+ because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones,
+ it will violate MaxSkew.
+
+
+ This is a beta field and requires the MinDomainsInPodTopologySpread feature gate to be enabled (enabled by default).
+ format: int32
+ type: integer
+ nodeAffinityPolicy:
+ description: |-
+ NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector
+ when calculating pod topology spread skew. Options are:
+ - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations.
+ - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations.
+
+
+ If this value is nil, the behavior is equivalent to the Honor policy.
+ This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.
+ type: string
+ nodeTaintsPolicy:
+ description: |-
+ NodeTaintsPolicy indicates how we will treat node taints when calculating
+ pod topology spread skew. Options are:
+ - Honor: nodes without taints, along with tainted nodes for which the incoming pod
+ has a toleration, are included.
+ - Ignore: node taints are ignored. All nodes are included.
+
+
+ If this value is nil, the behavior is equivalent to the Ignore policy.
+ This is a beta-level feature default enabled by the NodeInclusionPolicyInPodTopologySpread feature flag.
+ type: string
+ topologyKey:
+ description: |-
+ TopologyKey is the key of node labels. Nodes that have a label with this key
+ and identical values are considered to be in the same topology.
+ We consider each as a "bucket", and try to put balanced number
+ of pods into each bucket.
+ We define a domain as a particular instance of a topology.
+ Also, we define an eligible domain as a domain whose nodes meet the requirements of
+ nodeAffinityPolicy and nodeTaintsPolicy.
+ e.g. If TopologyKey is "kubernetes.io/hostname", each Node is a domain of that topology.
+ And, if TopologyKey is "topology.kubernetes.io/zone", each zone is a domain of that topology.
+ It's a required field.
+ type: string
+ whenUnsatisfiable:
+ description: |-
+ WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy
+ the spread constraint.
+ - DoNotSchedule (default) tells the scheduler not to schedule it.
+ - ScheduleAnyway tells the scheduler to schedule the pod in any location,
+ but giving higher precedence to topologies that would help reduce the
+ skew.
+ A constraint is considered "Unsatisfiable" for an incoming pod
+ if and only if every possible node assignment for that pod would violate
+ "MaxSkew" on some topology.
+ For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same
+ labelSelector spread as 3/1/1:
+ | zone1 | zone2 | zone3 |
+ | P P P | P | P |
+ If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled
+ to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies
+ MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler
+ won't make it *more* imbalanced.
+ It's a required field.
+ type: string
+ required:
+ - maxSkew
+ - topologyKey
+ - whenUnsatisfiable
+ type: object
+ type: array
+ type: object
+ type: object
+ served: true
+ storage: true
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: |-
+ Dataplane is a collection of configuration parameters that can be used for spawning a `stunnerd`
+ instance for a Gateway. Labels and annotations on the Dataplane object will be copied verbatim
+ into the target Deployment.
+ 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 behavior of a Dataplane resource.
+ properties:
+ affinity:
+ description: Scheduling constraints.
+ properties:
+ nodeAffinity:
+ description: Describes node affinity scheduling rules for the
+ pod.
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ description: |-
+ The scheduler will prefer to schedule pods to nodes that satisfy
+ the affinity expressions specified by this field, but it may choose
+ a node that violates one or more of the expressions. The node that is
+ most preferred is the one with the greatest sum of weights, i.e.
+ for each node that meets all of the scheduling requirements (resource
+ request, requiredDuringScheduling affinity expressions, etc.),
+ compute a sum by iterating through the elements of this field and adding
+ "weight" to the sum if the node matches the corresponding matchExpressions; the
+ node(s) with the highest sum are the most preferred.
+ items:
+ description: |-
+ An empty preferred scheduling term matches all objects with implicit weight 0
+ (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op).
+ properties:
+ preference:
+ description: A node selector term, associated with the
+ corresponding weight.
+ properties:
+ matchExpressions:
+ description: A list of node selector requirements
+ by node's labels.
+ items:
+ description: |-
+ A node selector requirement is a selector that contains values, a key, and an operator
+ that relates the key and values.
+ properties:
+ key:
+ description: The label key that the selector
+ applies to.
+ type: string
+ operator:
+ description: |-
+ Represents a key's relationship to a set of values.
+ Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+ type: string
+ values:
+ description: |-
+ 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. If the operator is Gt or Lt, the values
+ array must have a single element, which will be interpreted as an integer.
+ This array is replaced during a strategic merge patch.
+ items:
+ type: string
+ type: array
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ matchFields:
+ description: A list of node selector requirements
+ by node's fields.
+ items:
+ description: |-
+ A node selector requirement is a selector that contains values, a key, and an operator
+ that relates the key and values.
+ properties:
+ key:
+ description: The label key that the selector
+ applies to.
+ type: string
+ operator:
+ description: |-
+ Represents a key's relationship to a set of values.
+ Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+ type: string
+ values:
+ description: |-
+ 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. If the operator is Gt or Lt, the values
+ array must have a single element, which will be interpreted as an integer.
+ This array is replaced during a strategic merge patch.
+ items:
+ type: string
+ type: array
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ type: object
+ x-kubernetes-map-type: atomic
+ weight:
+ description: Weight associated with matching the corresponding
+ nodeSelectorTerm, in the range 1-100.
+ format: int32
+ type: integer
+ required:
+ - preference
+ - weight
+ type: object
+ type: array
+ requiredDuringSchedulingIgnoredDuringExecution:
+ description: |-
+ If the affinity requirements specified by this field are not met at
+ scheduling time, the pod will not be scheduled onto the node.
+ If the affinity requirements specified by this field cease to be met
+ at some point during pod execution (e.g. due to an update), the system
+ may or may not try to eventually evict the pod from its node.
+ properties:
+ nodeSelectorTerms:
+ description: Required. A list of node selector terms.
+ The terms are ORed.
+ items:
+ description: |-
+ A null or empty node selector term matches no objects. The requirements of
+ them are ANDed.
+ The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.
+ properties:
+ matchExpressions:
+ description: A list of node selector requirements
+ by node's labels.
+ items:
+ description: |-
+ A node selector requirement is a selector that contains values, a key, and an operator
+ that relates the key and values.
+ properties:
+ key:
+ description: The label key that the selector
+ applies to.
+ type: string
+ operator:
+ description: |-
+ Represents a key's relationship to a set of values.
+ Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+ type: string
+ values:
+ description: |-
+ 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. If the operator is Gt or Lt, the values
+ array must have a single element, which will be interpreted as an integer.
+ This array is replaced during a strategic merge patch.
+ items:
+ type: string
+ type: array
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ matchFields:
+ description: A list of node selector requirements
+ by node's fields.
+ items:
+ description: |-
+ A node selector requirement is a selector that contains values, a key, and an operator
+ that relates the key and values.
+ properties:
+ key:
+ description: The label key that the selector
+ applies to.
+ type: string
+ operator:
+ description: |-
+ Represents a key's relationship to a set of values.
+ Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
+ type: string
+ values:
+ description: |-
+ 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. If the operator is Gt or Lt, the values
+ array must have a single element, which will be interpreted as an integer.
+ This array is replaced during a strategic merge patch.
+ items:
+ type: string
+ type: array
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ type: object
+ x-kubernetes-map-type: atomic
+ type: array
+ required:
+ - nodeSelectorTerms
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ podAffinity:
+ description: Describes pod affinity scheduling rules (e.g. co-locate
+ this pod in the same node, zone, etc. as some other pod(s)).
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ description: |-
+ The scheduler will prefer to schedule pods to nodes that satisfy
+ the affinity expressions specified by this field, but it may choose
+ a node that violates one or more of the expressions. The node that is
+ most preferred is the one with the greatest sum of weights, i.e.
+ for each node that meets all of the scheduling requirements (resource
+ request, requiredDuringScheduling affinity expressions, etc.),
+ compute a sum by iterating through the elements of this field and adding
+ "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the
+ node(s) with the highest sum are the most preferred.
+ items:
+ description: The weights of all of the matched WeightedPodAffinityTerm
+ fields are added per-node to find the most preferred node(s)
+ properties:
+ podAffinityTerm:
+ description: Required. A pod affinity term, associated
+ with the corresponding weight.
+ properties:
+ labelSelector:
+ description: |-
+ A label query over a set of resources, in this case pods.
+ If it's null, this PodAffinityTerm matches with no Pods.
+ 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
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ 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
+ matchLabelKeys:
+ description: |-
+ MatchLabelKeys is a set of pod label keys to select which pods will
+ be taken into consideration. The keys are used to lookup values from the
+ incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)`
+ to select the group of existing pods which pods will be taken into consideration
+ for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming
+ pod labels will be ignored. The default value is empty.
+ The same key is forbidden to exist in both MatchLabelKeys and LabelSelector.
+ Also, MatchLabelKeys cannot be set when LabelSelector isn't set.
+ This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ description: |-
+ MismatchLabelKeys is a set of pod label keys to select which pods will
+ be taken into consideration. The keys are used to lookup values from the
+ incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)`
+ to select the group of existing pods which pods will be taken into consideration
+ for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming
+ pod labels will be ignored. The default value is empty.
+ The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector.
+ Also, MismatchLabelKeys cannot be set when LabelSelector isn't set.
+ This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ description: |-
+ A label query over the set of namespaces that the term applies to.
+ The term is applied to the union of the namespaces selected by this field
+ and the ones listed in the namespaces field.
+ null selector and null or empty namespaces list means "this pod's namespace".
+ An empty selector ({}) matches all namespaces.
+ 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
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ 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
+ namespaces:
+ description: |-
+ namespaces specifies a static list of namespace names that the term applies to.
+ The term is applied to the union of the namespaces listed in this field
+ and the ones selected by namespaceSelector.
+ null or empty namespaces list and null namespaceSelector means "this pod's namespace".
+ items:
+ type: string
+ type: array
+ topologyKey:
+ description: |-
+ This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching
+ the labelSelector in the specified namespaces, where co-located is defined as running on a node
+ whose value of the label with key topologyKey matches that of any node on which any of the
+ selected pods is running.
+ Empty topologyKey is not allowed.
+ type: string
+ required:
+ - topologyKey
+ type: object
+ weight:
+ description: |-
+ weight associated with matching the corresponding podAffinityTerm,
+ in the range 1-100.
+ format: int32
+ type: integer
+ required:
+ - podAffinityTerm
+ - weight
+ type: object
+ type: array
+ requiredDuringSchedulingIgnoredDuringExecution:
+ description: |-
+ If the affinity requirements specified by this field are not met at
+ scheduling time, the pod will not be scheduled onto the node.
+ If the affinity requirements specified by this field cease to be met
+ at some point during pod execution (e.g. due to a pod label update), the
+ system may or may not try to eventually evict the pod from its node.
+ When there are multiple elements, the lists of nodes corresponding to each
+ podAffinityTerm are intersected, i.e. all terms must be satisfied.
+ items:
+ description: |-
+ Defines a set of pods (namely those matching the labelSelector
+ relative to the given namespace(s)) that this pod should be
+ co-located (affinity) or not co-located (anti-affinity) with,
+ where co-located is defined as running on a node whose value of
+ the label with key matches that of any node on which
+ a pod of the set of pods is running
+ properties:
+ labelSelector:
+ description: |-
+ A label query over a set of resources, in this case pods.
+ If it's null, this PodAffinityTerm matches with no Pods.
+ 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
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ 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
+ matchLabelKeys:
+ description: |-
+ MatchLabelKeys is a set of pod label keys to select which pods will
+ be taken into consideration. The keys are used to lookup values from the
+ incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)`
+ to select the group of existing pods which pods will be taken into consideration
+ for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming
+ pod labels will be ignored. The default value is empty.
+ The same key is forbidden to exist in both MatchLabelKeys and LabelSelector.
+ Also, MatchLabelKeys cannot be set when LabelSelector isn't set.
+ This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ description: |-
+ MismatchLabelKeys is a set of pod label keys to select which pods will
+ be taken into consideration. The keys are used to lookup values from the
+ incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)`
+ to select the group of existing pods which pods will be taken into consideration
+ for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming
+ pod labels will be ignored. The default value is empty.
+ The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector.
+ Also, MismatchLabelKeys cannot be set when LabelSelector isn't set.
+ This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ description: |-
+ A label query over the set of namespaces that the term applies to.
+ The term is applied to the union of the namespaces selected by this field
+ and the ones listed in the namespaces field.
+ null selector and null or empty namespaces list means "this pod's namespace".
+ An empty selector ({}) matches all namespaces.
+ 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
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ 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
+ namespaces:
+ description: |-
+ namespaces specifies a static list of namespace names that the term applies to.
+ The term is applied to the union of the namespaces listed in this field
+ and the ones selected by namespaceSelector.
+ null or empty namespaces list and null namespaceSelector means "this pod's namespace".
+ items:
+ type: string
+ type: array
+ topologyKey:
+ description: |-
+ This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching
+ the labelSelector in the specified namespaces, where co-located is defined as running on a node
+ whose value of the label with key topologyKey matches that of any node on which any of the
+ selected pods is running.
+ Empty topologyKey is not allowed.
+ type: string
+ required:
+ - topologyKey
+ type: object
+ type: array
+ type: object
+ podAntiAffinity:
+ description: Describes pod anti-affinity scheduling rules (e.g.
+ avoid putting this pod in the same node, zone, etc. as some
+ other pod(s)).
+ properties:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ description: |-
+ The scheduler will prefer to schedule pods to nodes that satisfy
+ the anti-affinity expressions specified by this field, but it may choose
+ a node that violates one or more of the expressions. The node that is
+ most preferred is the one with the greatest sum of weights, i.e.
+ for each node that meets all of the scheduling requirements (resource
+ request, requiredDuringScheduling anti-affinity expressions, etc.),
+ compute a sum by iterating through the elements of this field and adding
+ "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the
+ node(s) with the highest sum are the most preferred.
+ items:
+ description: The weights of all of the matched WeightedPodAffinityTerm
+ fields are added per-node to find the most preferred node(s)
+ properties:
+ podAffinityTerm:
+ description: Required. A pod affinity term, associated
+ with the corresponding weight.
+ properties:
+ labelSelector:
+ description: |-
+ A label query over a set of resources, in this case pods.
+ If it's null, this PodAffinityTerm matches with no Pods.
+ 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
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ 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
+ matchLabelKeys:
+ description: |-
+ MatchLabelKeys is a set of pod label keys to select which pods will
+ be taken into consideration. The keys are used to lookup values from the
+ incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)`
+ to select the group of existing pods which pods will be taken into consideration
+ for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming
+ pod labels will be ignored. The default value is empty.
+ The same key is forbidden to exist in both MatchLabelKeys and LabelSelector.
+ Also, MatchLabelKeys cannot be set when LabelSelector isn't set.
+ This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ description: |-
+ MismatchLabelKeys is a set of pod label keys to select which pods will
+ be taken into consideration. The keys are used to lookup values from the
+ incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)`
+ to select the group of existing pods which pods will be taken into consideration
+ for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming
+ pod labels will be ignored. The default value is empty.
+ The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector.
+ Also, MismatchLabelKeys cannot be set when LabelSelector isn't set.
+ This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ description: |-
+ A label query over the set of namespaces that the term applies to.
+ The term is applied to the union of the namespaces selected by this field
+ and the ones listed in the namespaces field.
+ null selector and null or empty namespaces list means "this pod's namespace".
+ An empty selector ({}) matches all namespaces.
+ 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
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ 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
+ namespaces:
+ description: |-
+ namespaces specifies a static list of namespace names that the term applies to.
+ The term is applied to the union of the namespaces listed in this field
+ and the ones selected by namespaceSelector.
+ null or empty namespaces list and null namespaceSelector means "this pod's namespace".
+ items:
+ type: string
+ type: array
+ topologyKey:
+ description: |-
+ This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching
+ the labelSelector in the specified namespaces, where co-located is defined as running on a node
+ whose value of the label with key topologyKey matches that of any node on which any of the
+ selected pods is running.
+ Empty topologyKey is not allowed.
+ type: string
+ required:
+ - topologyKey
+ type: object
+ weight:
+ description: |-
+ weight associated with matching the corresponding podAffinityTerm,
+ in the range 1-100.
+ format: int32
+ type: integer
+ required:
+ - podAffinityTerm
+ - weight
+ type: object
+ type: array
+ requiredDuringSchedulingIgnoredDuringExecution:
+ description: |-
+ If the anti-affinity requirements specified by this field are not met at
+ scheduling time, the pod will not be scheduled onto the node.
+ If the anti-affinity requirements specified by this field cease to be met
+ at some point during pod execution (e.g. due to a pod label update), the
+ system may or may not try to eventually evict the pod from its node.
+ When there are multiple elements, the lists of nodes corresponding to each
+ podAffinityTerm are intersected, i.e. all terms must be satisfied.
+ items:
+ description: |-
+ Defines a set of pods (namely those matching the labelSelector
+ relative to the given namespace(s)) that this pod should be
+ co-located (affinity) or not co-located (anti-affinity) with,
+ where co-located is defined as running on a node whose value of
+ the label with key matches that of any node on which
+ a pod of the set of pods is running
+ properties:
+ labelSelector:
+ description: |-
+ A label query over a set of resources, in this case pods.
+ If it's null, this PodAffinityTerm matches with no Pods.
+ 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
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ 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
+ matchLabelKeys:
+ description: |-
+ MatchLabelKeys is a set of pod label keys to select which pods will
+ be taken into consideration. The keys are used to lookup values from the
+ incoming pod labels, those key-value labels are merged with `LabelSelector` as `key in (value)`
+ to select the group of existing pods which pods will be taken into consideration
+ for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming
+ pod labels will be ignored. The default value is empty.
+ The same key is forbidden to exist in both MatchLabelKeys and LabelSelector.
+ Also, MatchLabelKeys cannot be set when LabelSelector isn't set.
+ This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ mismatchLabelKeys:
+ description: |-
+ MismatchLabelKeys is a set of pod label keys to select which pods will
+ be taken into consideration. The keys are used to lookup values from the
+ incoming pod labels, those key-value labels are merged with `LabelSelector` as `key notin (value)`
+ to select the group of existing pods which pods will be taken into consideration
+ for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming
+ pod labels will be ignored. The default value is empty.
+ The same key is forbidden to exist in both MismatchLabelKeys and LabelSelector.
+ Also, MismatchLabelKeys cannot be set when LabelSelector isn't set.
+ This is an alpha field and requires enabling MatchLabelKeysInPodAffinity feature gate.
+ items:
+ type: string
+ type: array
+ x-kubernetes-list-type: atomic
+ namespaceSelector:
+ description: |-
+ A label query over the set of namespaces that the term applies to.
+ The term is applied to the union of the namespaces selected by this field
+ and the ones listed in the namespaces field.
+ null selector and null or empty namespaces list means "this pod's namespace".
+ An empty selector ({}) matches all namespaces.
+ 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
+ required:
+ - key
+ - operator
+ type: object
+ type: array
+ 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
+ namespaces:
+ description: |-
+ namespaces specifies a static list of namespace names that the term applies to.
+ The term is applied to the union of the namespaces listed in this field
+ and the ones selected by namespaceSelector.
+ null or empty namespaces list and null namespaceSelector means "this pod's namespace".
+ items:
+ type: string
+ type: array
+ topologyKey:
+ description: |-
+ This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching
+ the labelSelector in the specified namespaces, where co-located is defined as running on a node
+ whose value of the label with key topologyKey matches that of any node on which any of the
+ selected pods is running.
+ Empty topologyKey is not allowed.
+ type: string
+ required:
+ - topologyKey
+ type: object
+ type: array
+ type: object
+ type: object
+ args:
+ description: Arguments to the entrypoint.
+ items:
+ type: string
+ type: array
+ command:
+ description: 'Entrypoint array. Defaults: "stunnerd".'
+ items:
+ type: string
+ type: array
+ env:
+ description: List of environment variables to set in the stunnerd
+ container.
+ items:
+ description: EnvVar represents an environment variable present in
+ a Container.
+ properties:
+ name:
+ description: Name of the environment variable. Must be a C_IDENTIFIER.
+ type: string
+ value:
+ description: |-
+ Variable references $(VAR_NAME) are expanded
+ using the previously defined environment variables in the container and
+ any service environment variables. If a variable cannot be resolved,
+ the reference in the input string will be unchanged. Double $$ are reduced
+ to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e.
+ "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)".
+ Escaped references will never be expanded, regardless of whether the variable
+ exists or not.
+ Defaults to "".
+ type: string
+ valueFrom:
+ description: Source for the environment variable's value. Cannot
+ be used if value is not empty.
+ properties:
+ configMapKeyRef:
+ description: Selects a key of a ConfigMap.
+ properties:
+ key:
+ description: The key to select.
+ type: string
+ name:
+ description: |-
+ Name of the referent.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?
+ type: string
+ optional:
+ description: Specify whether the ConfigMap or its key
+ must be defined
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ fieldRef:
+ description: |-
+ Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`,
+ spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.
+ properties:
+ apiVersion:
+ description: Version of the schema the FieldPath is
+ written in terms of, defaults to "v1".
+ type: string
+ fieldPath:
+ description: Path of the field to select in the specified
+ API version.
+ type: string
+ required:
+ - fieldPath
+ type: object
+ x-kubernetes-map-type: atomic
+ resourceFieldRef:
+ description: |-
+ Selects a resource of the container: only resources limits and requests
+ (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported.
+ properties:
+ containerName:
+ description: 'Container name: required for volumes,
+ optional for env vars'
+ type: string
+ divisor:
+ anyOf:
+ - type: integer
+ - type: string
+ description: Specifies the output format of the exposed
+ resources, defaults to "1"
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ resource:
+ description: 'Required: resource to select'
+ type: string
+ required:
+ - resource
+ type: object
+ x-kubernetes-map-type: atomic
+ secretKeyRef:
+ description: Selects a key of a secret in the pod's namespace
+ properties:
+ key:
+ description: The key of the secret to select from. Must
+ be a valid secret key.
+ type: string
+ name:
+ description: |-
+ Name of the referent.
+ More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
+ TODO: Add other useful fields. apiVersion, kind, uid?
+ type: string
+ optional:
+ description: Specify whether the Secret or its key must
+ be defined
+ type: boolean
+ required:
+ - key
+ type: object
+ x-kubernetes-map-type: atomic
+ type: object
+ required:
+ - name
+ type: object
+ type: array
+ healthCheckPort:
+ description: If specified, the health-check port.
+ type: integer
+ hostNetwork:
+ description: |-
+ Host networking requested for the stunnerd pod to use the host's network namespace.
+ Can be used to implement public TURN servers with Kubernetes. Defaults to false.
+ type: boolean
+ image:
+ description: Container image name.
+ type: string
+ imagePullPolicy:
+ description: Image pull policy. One of Always, Never, IfNotPresent.
+ type: string
+ replicas:
+ description: |-
+ Number of desired pods. This is a pointer to distinguish between explicit zero and not
+ specified. Defaults to 1.
+ format: int32
+ type: integer
+ resources:
+ description: Resources required by stunnerd.
+ properties:
+ claims:
+ description: |-
+ Claims lists the names of resources, defined in spec.resourceClaims,
+ that are used by this container.
+
+
+ This is an alpha field and requires enabling the
+ DynamicResourceAllocation feature gate.
+
+
+ This field is immutable. It can only be set for containers.
+ items:
+ description: ResourceClaim references one entry in PodSpec.ResourceClaims.
+ properties:
+ name:
+ description: |-
+ Name must match the name of one entry in pod.spec.resourceClaims of
+ the Pod where this field is used. It makes that resource available
+ inside a container.
+ type: string
+ required:
+ - name
+ type: object
+ type: array
+ x-kubernetes-list-map-keys:
+ - name
+ x-kubernetes-list-type: map
+ limits:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ description: |-
+ Limits describes the maximum amount of compute resources allowed.
+ More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+ type: object
+ requests:
+ additionalProperties:
+ anyOf:
+ - type: integer
+ - type: string
+ pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ x-kubernetes-int-or-string: true
+ description: |-
+ Requests describes the minimum amount of compute resources required.
+ If Requests is omitted for a container, it defaults to Limits if that is explicitly specified,
+ otherwise to an implementation-defined value. Requests cannot exceed Limits.
+ More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
+ type: object
+ type: object
+ securityContext:
+ description: SecurityContext holds pod-level security attributes and
+ common container settings.
+ properties:
+ fsGroup:
+ description: |-
+ A special supplemental group that applies to all containers in a pod.
+ Some volume types allow the Kubelet to change the ownership of that volume
+ to be owned by the pod:
+
+
+ 1. The owning GID will be the FSGroup
+ 2. The setgid bit is set (new files created in the volume will be owned by FSGroup)
+ 3. The permission bits are OR'd with rw-rw----
+
+
+ If unset, the Kubelet will not modify the ownership and permissions of any volume.
+ Note that this field cannot be set when spec.os.name is windows.
+ format: int64
+ type: integer
+ fsGroupChangePolicy:
+ description: |-
+ fsGroupChangePolicy defines behavior of changing ownership and permission of the volume
+ before being exposed inside Pod. This field will only apply to
+ volume types which support fsGroup based ownership(and permissions).
+ It will have no effect on ephemeral volume types such as: secret, configmaps
+ and emptydir.
+ Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used.
+ Note that this field cannot be set when spec.os.name is windows.
+ type: string
+ runAsGroup:
+ description: |-
+ The GID to run the entrypoint of the container process.
+ Uses runtime default if unset.
+ May also be set in SecurityContext. If set in both SecurityContext and
+ PodSecurityContext, the value specified in SecurityContext takes precedence
+ for that container.
+ Note that this field cannot be set when spec.os.name is windows.
+ format: int64
+ type: integer
+ runAsNonRoot:
+ description: |-
+ Indicates that the container must run as a non-root user.
+ If true, the Kubelet will validate the image at runtime to ensure that it
+ does not run as UID 0 (root) and fail to start the container if it does.
+ If unset or false, no such validation will be performed.
+ May also be set in SecurityContext. If set in both SecurityContext and
+ PodSecurityContext, the value specified in SecurityContext takes precedence.
+ type: boolean
+ runAsUser:
+ description: |-
+ The UID to run the entrypoint of the container process.
+ Defaults to user specified in image metadata if unspecified.
+ May also be set in SecurityContext. If set in both SecurityContext and
+ PodSecurityContext, the value specified in SecurityContext takes precedence
+ for that container.
+ Note that this field cannot be set when spec.os.name is windows.
+ format: int64
+ type: integer
+ seLinuxOptions:
+ description: |-
+ The SELinux context to be applied to all containers.
+ If unspecified, the container runtime will allocate a random SELinux context for each
+ container. May also be set in SecurityContext. If set in
+ both SecurityContext and PodSecurityContext, the value specified in SecurityContext
+ takes precedence for that container.
+ Note that this field cannot be set when spec.os.name is windows.
+ properties:
+ level:
+ description: Level is SELinux level label that applies to
+ the container.
+ type: string
+ role:
+ description: Role is a SELinux role label that applies to
+ the container.
+ type: string
+ type:
+ description: Type is a SELinux type label that applies to
+ the container.
+ type: string
+ user:
+ description: User is a SELinux user label that applies to
+ the container.
+ type: string
+ type: object
+ seccompProfile:
+ description: |-
+ The seccomp options to use by the containers in this pod.
+ Note that this field cannot be set when spec.os.name is windows.
+ properties:
+ localhostProfile:
+ description: |-
+ localhostProfile indicates a profile defined in a file on the node should be used.
+ The profile must be preconfigured on the node to work.
+ Must be a descending path, relative to the kubelet's configured seccomp profile location.
+ Must be set if type is "Localhost". Must NOT be set for any other type.
+ type: string
+ type:
+ description: |-
+ type indicates which kind of seccomp profile will be applied.
+ Valid options are:
+
+
+ Localhost - a profile defined in a file on the node should be used.
+ RuntimeDefault - the container runtime default profile should be used.
+ Unconfined - no profile should be applied.
+ type: string
+ required:
+ - type
+ type: object
+ supplementalGroups:
+ description: |-
+ A list of groups applied to the first process run in each container, in addition
+ to the container's primary GID, the fsGroup (if specified), and group memberships
+ defined in the container image for the uid of the container process. If unspecified,
+ no additional groups are added to any container. Note that group memberships
+ defined in the container image for the uid of the container process are still effective,
+ even if they are not included in this list.
+ Note that this field cannot be set when spec.os.name is windows.
+ items:
+ format: int64
+ type: integer
+ type: array
+ sysctls:
+ description: |-
+ Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported
+ sysctls (by the container runtime) might fail to launch.
+ Note that this field cannot be set when spec.os.name is windows.
+ items:
+ description: Sysctl defines a kernel parameter to be set
+ properties:
+ name:
+ description: Name of a property to set
+ type: string
+ value:
+ description: Value of a property to set
+ type: string
+ required:
+ - name
+ - value
+ type: object
+ type: array
+ windowsOptions:
+ description: |-
+ The Windows specific settings applied to all containers.
+ If unspecified, the options within a container's SecurityContext will be used.
+ If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.
+ Note that this field cannot be set when spec.os.name is linux.
+ properties:
+ gmsaCredentialSpec:
+ description: |-
+ GMSACredentialSpec is where the GMSA admission webhook
+ (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the
+ GMSA credential spec named by the GMSACredentialSpecName field.
+ type: string
+ gmsaCredentialSpecName:
+ description: GMSACredentialSpecName is the name of the GMSA
+ credential spec to use.
+ type: string
+ hostProcess:
+ description: |-
+ HostProcess determines if a container should be run as a 'Host Process' container.
+ All of a Pod's containers must have the same effective HostProcess value
+ (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers).
+ In addition, if HostProcess is true then HostNetwork must also be set to true.
+ type: boolean
+ runAsUserName:
+ description: |-
+ The UserName in Windows to run the entrypoint of the container process.
+ Defaults to the user specified in image metadata if unspecified.
+ May also be set in PodSecurityContext. If set in both SecurityContext and
+ PodSecurityContext, the value specified in SecurityContext takes precedence.
+ type: string
+ type: object
+ type: object
+ terminationGracePeriodSeconds:
+ description: Optional duration in seconds the stunnerd needs to terminate
+ gracefully. Defaults to 3600 seconds.
+ format: int64
+ type: integer
+ tolerations:
+ description: If specified, the pod's tolerations.
+ items:
+ description: |-
+ The pod this Toleration is attached to tolerates any taint that matches
+ the triple using the matching operator .
+ properties:
+ effect:
+ description: |-
+ Effect indicates the taint effect to match. Empty means match all taint effects.
+ When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.
+ type: string
+ key:
+ description: |-
+ Key is the taint key that the toleration applies to. Empty means match all taint keys.
+ If the key is empty, operator must be Exists; this combination means to match all values and all keys.
+ type: string
+ operator:
+ description: |-
+ Operator represents a key's relationship to the value.
+ Valid operators are Exists and Equal. Defaults to Equal.
+ Exists is equivalent to wildcard for value, so that a pod can
+ tolerate all taints of a particular category.
+ type: string
+ tolerationSeconds:
+ description: |-
+ TolerationSeconds represents the period of time the toleration (which must be
+ of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default,
+ it is not set, which means tolerate the taint forever (do not evict). Zero and
+ negative values will be treated as 0 (evict immediately) by the system.
+ format: int64
+ type: integer
+ value:
+ description: |-
+ Value is the taint value the toleration matches to.
+ If the operator is Exists, the value should be empty, otherwise just a regular string.
+ type: string
+ type: object
+ type: array
+ type: object
+ type: object
+ served: true
+ storage: false
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.14.0
+ name: udproutes.stunner.l7mp.io
+spec:
+ group: stunner.l7mp.io
+ names:
+ categories:
+ - stunner
+ kind: UDPRoute
+ listKind: UDPRouteList
+ plural: udproutes
+ singular: udproute
+ scope: Namespaced
+ versions:
+ - additionalPrinterColumns:
+ - jsonPath: .metadata.creationTimestamp
+ name: Age
+ type: date
+ name: v1
+ 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.
+
+
+ Differences from Gateway API UDPRoutes
+ - port-ranges are correctly handled ([port, endPort])
+ - port is not mandatory
+ - backend weight is not supported
+ 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, experimental, 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, experimental, 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, experimental, 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. Note that attaching Routes to Services as Parents
+ is part of experimental Mesh support and is not supported for any other
+ purpose.
+
+
+ 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
+ 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. UDPRouteRules correctly handle port ranges.
+ items:
+ description: BackendRef defines how a Route should forward
+ a request to a Kubernetes resource.
+ properties:
+ endPort:
+ description: EndPort specifies the upper threshold of
+ the port-range. Only considered of port is also specified.
+ format: int32
+ maximum: 65535
+ minimum: 1
+ type: integer
+ 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".
+ 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.
+ 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. If port is not
+ specified, all ports are allowed. If port is defined but endPort is not, allow only
+ access to the given port. If both are specified, allows access in the port-range [port,
+ endPort] inclusive.
+ format: int32
+ maximum: 65535
+ minimum: 1
+ type: integer
+ required:
+ - name
+ type: object
+ maxItems: 16
+ minItems: 1
+ type: array
+ type: object
+ maxItems: 16
+ minItems: 1
+ type: array
+ 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 non-existent parent.
+ * The Route is of a type that the controller does not support.
+ * The Route is in a namespace the controller does not have access to.
+ items:
+ description: "Condition contains details for one aspect of
+ the current state of this API Resource.\n---\nThis struct
+ is intended for direct use as an array at the field path
+ .status.conditions. For example,\n\n\n\ttype FooStatus
+ struct{\n\t // Represents the observations of a foo's
+ current state.\n\t // Known .status.conditions.type are:
+ \"Available\", \"Progressing\", and \"Degraded\"\n\t //
+ +patchMergeKey=type\n\t // +patchStrategy=merge\n\t //
+ +listType=map\n\t // +listMapKey=type\n\t Conditions
+ []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\"
+ patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"`\n\n\n\t
+ \ // other fields\n\t}"
+ 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.
+ ---
+ Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be
+ useful (see .node.status.conditions), the ability to deconflict is important.
+ The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+ 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, experimental, 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. Note that attaching Routes to Services as Parents
+ is part of experimental Mesh support and is not supported for any other
+ purpose.
+
+
+ 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:
+ - controllerName
+ - parentRef
+ type: object
+ maxItems: 32
+ type: array
+ required:
+ - parents
+ type: object
+ required:
+ - spec
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
diff --git a/deploy/manifests/static/stunner-gateway-operator-manifests.yaml b/deploy/manifests/static/stunner-gateway-operator-manifests.yaml
index 0fc55ce9..09b9b5c7 100644
--- a/deploy/manifests/static/stunner-gateway-operator-manifests.yaml
+++ b/deploy/manifests/static/stunner-gateway-operator-manifests.yaml
@@ -49,6 +49,18 @@ kind: ClusterRole
metadata:
name: stunner-gateway-operator-manager-role
rules:
+- apiGroups:
+ - apps
+ resources:
+ - deployments
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
- apiGroups:
- ""
resources:
@@ -70,9 +82,11 @@ rules:
- apiGroups:
- ""
resources:
- - endpoints
- - nodes
- - secrets
+ - deployments/finalizers
+ - deployments/status
+ - endpoints/status
+ - nodes/status
+ - services/status
verbs:
- get
- list
@@ -80,11 +94,14 @@ rules:
- apiGroups:
- ""
resources:
- - endpoints/status
- - nodes/status
- - services/status
+ - endpoints
+ - namespaces
+ - nodes
+ - secrets
verbs:
- get
+ - list
+ - watch
- apiGroups:
- ""
resources:
@@ -121,7 +138,10 @@ rules:
- apiGroups:
- stunner.l7mp.io
resources:
+ - dataplanes
- gatewayconfigs
+ - staticservices
+ - udproutes
verbs:
- get
- list
@@ -131,7 +151,11 @@ rules:
- apiGroups:
- stunner.l7mp.io
resources:
+ - dataplanes/finalizers
- gatewayconfigs/finalizers
+ - staticservices/finalizers
+ - udproutes/finalizers
+ - udproutes/status
verbs:
- update
---
@@ -306,10 +330,12 @@ spec:
terminationGracePeriodSeconds: 10
containers:
- name: stunner-auth-server
- image: l7mp/stunner-auth-server:dev
+ image: "l7mp/stunner-auth-server:0.16.0"
imagePullPolicy: Always
command: [ "./manager" ]
- args: ["-zap-log-level","10", "-port", "8088"]
+ args:
+ - --zap-log-level=10
+ - --port=8088
securityContext:
allowPrivilegeEscalation: false
capabilities:
@@ -334,6 +360,10 @@ spec:
requests:
cpu: 10m
memory: 64Mi
+ nodeSelector:
+ kubernetes.io/os: linux
+ tolerations:
+ []
---
apiVersion: apps/v1
kind: Deployment
@@ -366,34 +396,53 @@ spec:
- containerPort: 8443
name: https
protocol: TCP
+ - containerPort: 13478
+ name: cds
+ protocol: TCP
resources:
limits:
- cpu: 500m
- memory: 128Mi
+ cpu: 1000m
+ memory: 256Mi
requests:
- cpu: 5m
- memory: 64Mi
+ cpu: 250m
+ memory: 128Mi
- args:
- - --health-probe-bind-address=:8081
- - --metrics-bind-address=127.0.0.1:8080
- - --leader-elect
+ - --health-probe-bind-address=:8081
+ - --metrics-bind-address=127.0.0.1:8080
+ - --leader-elect
+ - --zap-log-level=info
+ - --dataplane-mode=legacy
command:
- /manager
- image: "l7mp/stunner-gateway-operator:0.15.0"
+ env:
+ - name: STUNNER_GATEWAY_OPERATOR_ADDRESS
+ valueFrom:
+ fieldRef:
+ apiVersion: v1
+ fieldPath: status.podIP
+ image: "l7mp/stunner-gateway-operator:0.16.0"
imagePullPolicy: IfNotPresent
livenessProbe:
+ failureThreshold: 3
httpGet:
path: /healthz
port: 8081
+ scheme: HTTP
initialDelaySeconds: 15
periodSeconds: 20
+ successThreshold: 1
+ timeoutSeconds: 1
name: manager
readinessProbe:
+ failureThreshold: 3
httpGet:
path: /readyz
port: 8081
+ scheme: HTTP
initialDelaySeconds: 5
periodSeconds: 10
+ successThreshold: 1
+ timeoutSeconds: 1
resources:
limits:
cpu: 1000m
@@ -406,6 +455,7 @@ spec:
securityContext:
runAsNonRoot: true
serviceAccountName: stunner-gateway-operator-controller-manager
+ serviceAccount: stunner-gateway-operator-controller-manager
terminationGracePeriodSeconds: 10
nodeSelector:
kubernetes.io/os: linux
diff --git a/deploy/manifests/static/stunner-manifests.yaml b/deploy/manifests/static/stunner-manifests.yaml
index 09a62ad6..b363f0e1 100644
--- a/deploy/manifests/static/stunner-manifests.yaml
+++ b/deploy/manifests/static/stunner-manifests.yaml
@@ -6,21 +6,23 @@ metadata:
namespace: stunner
---
apiVersion: rbac.authorization.k8s.io/v1
-kind: ClusterRole
+kind: Role
metadata:
- name: stunner-config-watcher-clusterrole
+ name: stunner-config-watcher-role
+ namespace: stunner
rules:
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "watch", "list"]
---
-kind: ClusterRoleBinding
+kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
- name: stunner-config-watcher-clusterrolebind
+ name: stunner-config-watcher-rolebind
+ namespace: stunner
roleRef:
- kind: ClusterRole
- name: stunner-config-watcher-clusterrole
+ kind: Role
+ name: stunner-config-watcher-role
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
@@ -50,8 +52,8 @@ metadata:
namespace: stunner
annotations:
app: stunner
- helm.sh/chart: stunner-0.15.0
- app.kubernetes.io/version: "0.15.0"
+ helm.sh/chart: stunner-0.16.0
+ app.kubernetes.io/version: "0.16.0"
spec:
selector:
matchLabels:
@@ -75,7 +77,7 @@ spec:
hostNetwork: false
containers:
- name: stunnerd
- image: "l7mp/stunnerd:0.15.0"
+ image: "l7mp/stunnerd:0.16.0"
imagePullPolicy: IfNotPresent
command: ["stunnerd"]
args: ["-w", "-c", "/etc/stunnerd/stunnerd.conf", "--udp-thread-num=16"]
diff --git a/deploy/manifests/stunner-expose-kube-dns.yaml b/deploy/manifests/stunner-expose-kube-dns.yaml
index 8065d8f0..35b1464f 100644
--- a/deploy/manifests/stunner-expose-kube-dns.yaml
+++ b/deploy/manifests/stunner-expose-kube-dns.yaml
@@ -1,4 +1,4 @@
-apiVersion: gateway.networking.k8s.io/v1alpha2
+apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: stunner-gatewayclass
@@ -12,7 +12,7 @@ spec:
description: "STUNner is a WebRTC ingress gateway for Kubernetes"
---
-apiVersion: stunner.l7mp.io/v1alpha1
+apiVersion: stunner.l7mp.io/v1
kind: GatewayConfig
metadata:
name: stunner-gatewayconfig
@@ -24,7 +24,7 @@ spec:
password: "pass-1"
---
-apiVersion: gateway.networking.k8s.io/v1alpha2
+apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: udp-gateway
@@ -36,7 +36,7 @@ spec:
port: 3478
protocol: UDP
---
-apiVersion: gateway.networking.k8s.io/v1alpha2
+apiVersion: stunner.l7mp.io/v1
kind: UDPRoute
metadata:
name: stunner-udproute
@@ -46,6 +46,5 @@ spec:
- name: udp-gateway
rules:
- backendRefs:
- - name: dummy
- # - name: kube-dns
- # namespace: kube-system
+ - name: kube-dns
+ namespace: kube-system
diff --git a/deploy/manifests/stunner-standalone-tls.yaml.template b/deploy/manifests/stunner-standalone-tls.yaml.template
deleted file mode 100644
index 22741af2..00000000
--- a/deploy/manifests/stunner-standalone-tls.yaml.template
+++ /dev/null
@@ -1,220 +0,0 @@
-# Stunner: An ingress gateway for WebRTC: TLS/DTLS in standalone mode
----
-# STUN/TURN server config: Make sure to customize the below configurations, see the README.pm for
-# more details
-apiVersion: v1
-kind: ConfigMap
-metadata:
- name: stunner-config
-data:
- # * `STUNNER_PUBLIC_ADDR` (no default): The public IP address clients can use to reach
- # STUNner. By default, the public IP address will be dynamically assigned by the Kubernetes
- # LoadBalancer service. The Helm installation script takes care of updating the configuration
- # with the correct value. However, if installing from the static manifests then the external IP
- # must be set manually.
- STUNNER_PUBLIC_ADDR: "A.B.C.D"
-
- # * `STUNNER_PUBLIC_PORT` (default: 3478): The public port used by clients to reach STUNner. It
- # is important that applications use the public port as found in the configuration, since the
- # Helm installation scripts may overwrite this configuration. This occurs when the installation
- # falls back to a NodePort service (i.e., when STUNner fails to obtain an external IP from the
- # load-balancer).
- STUNNER_PUBLIC_PORT: "443"
-
- # * `STUNNER_PORT` (default: 3478): The internal port used by STUNner for communication inside
- # the cluster. It is safe to set this to the public port.
- STUNNER_PORT: "443"
-
- # * `STUNNER_REALM` (default `stunner.l7mp.io`): the REALM used to guide the user agent in
- # authenticating with STUNner.
- STUNNER_REALM: "stunner.l7mp.io"
-
- # * `STUNNER_AUTH_TYPE` (default: `plaintext`): the STUN/TURN authentication mode, either
- # "plaintext" over the username/password pair $STUNNER_USERNAME/$STUNNER_PASSWORD, or
- # "longterm", using $STUNNER_SECRET. Make sure to customize!
- STUNNER_AUTH_TYPE: "plaintext"
-
- # * `STUNNER_USERNAME` (default: `user`): the USERNAME attribute clients can use the authenticate
- # with STUNner over plain-text authentication. Make sure to customize!
- STUNNER_USERNAME: "user1"
-
- # * `STUNNER_PASSWORD` (default: `pass`): the password clients can use to authenticate with
- # STUNner over plain-text authentication. Make sure to customize!
- STUNNER_PASSWORD: "passwd1"
-
- # * `STUNNER_SHARED_SECRET`: the shared secret used for longterm authentication.
- STUNNER_SHARED_SECRET: "my-shared-secret"
-
- # * `STUNNER_DURATION` (default: `86400`, i.e., one day): the lifetime of STUNner credentials
- # * over longterm authentication.
- STUNNER_DURATION: "86400"
-
- # * `STUNNER_LOGLEVEL` (default: `all:WARN`): the default log level used by the STUNner daemons.
- STUNNER_LOGLEVEL: "all:INFO"
-
- # * `STUNNER_MIN_PORT` (default: 10000): smallest relay transport port assigned by STUNner.
- STUNNER_MIN_PORT: "10000"
-
- # * `STUNNER_MAX_PORT` (default: 20000): highest relay transport port assigned by STUNner.
- STUNNER_MAX_PORT: "20000"
-
- STUNNER_TLS_KEY: |
- XXXXXXX
- STUNNER_TLS_CERT: |
- YYYYYYY
-
----
-## custom static stunnerd conf for TLS/DTLS
-apiVersion: v1
-kind: ConfigMap
-metadata:
- name: stunnerd-conf
-data:
- "stunnerd.conf" : |
- version: v1alpha1
- admin:
- name: stunnerd
- loglevel: $STUNNER_LOGLEVEL
- metrics_endpoint: "http://0.0.0.0:8080/metrics"
- auth:
- type: $STUNNER_AUTH_TYPE
- realm: $STUNNER_REALM
- credentials:
- username: $STUNNER_USERNAME
- password: $STUNNER_PASSWORD
- secret: $STUNNER_SHARED_SECRET
- clusters:
- - name: media-plane
- type: STRICT_DNS
- endpoints:
- - media-plane.default.svc.cluster.local
- listeners:
- - name: stunner-dtls
- public_address: "$STUNNER_PUBLIC_ADDR"
- public_port: $STUNNER_PUBLIC_PORT
- address: "$STUNNER_ADDR"
- port: $STUNNER_PORT
- protocol: dtls
- min_port: $STUNNER_MIN_PORT
- max_port: $STUNNER_MAX_PORT
- key: $STUNNER_TLS_KEY
- cert: $STUNNER_TLS_CERT
- routes:
- - media-plane
- - name: stunner-tls
- public_address: "$STUNNER_PUBLIC_ADDR"
- public_port: $STUNNER_PUBLIC_PORT
- address: "$STUNNER_ADDR"
- port: $STUNNER_PORT
- protocol: tls
- min_port: $STUNNER_MIN_PORT
- max_port: $STUNNER_MAX_PORT
- key: $STUNNER_TLS_KEY
- cert: $STUNNER_TLS_CERT
- routes:
- - media-plane
-
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: stunner
-spec:
- selector:
- matchLabels:
- app: stunner
- replicas: 1
- template:
- metadata:
- labels:
- app: stunner
- spec:
- containers:
- - name: stunnerd
- image: l7mp/stunnerd:latest
- imagePullPolicy: Always
- command: ["stunnerd"]
- args: ["-c", "/etc/stunnerd/stunnerd.conf"]
- envFrom:
- - configMapRef:
- name: stunner-config
- env:
- - name: STUNNER_ADDR # we use the POD IP
- valueFrom:
- fieldRef:
- fieldPath: status.podIP
- volumeMounts:
- - name: stunnerd-config-volume
- mountPath: /etc/stunnerd
- readOnly: true
- - name: stunnerd-cert-volume
- mountPath: /etc/ssl/certs
- readOnly: true
- # Uncomment this if you want to deploy a sidecar container with stunner to sniff traffic
- # - name: net-debug
- # image: l7mp/net-debug:latest
- # command: ["/bin/sh"]
- # args: ["-c", "while true; do echo hello; sleep 10;done"]
- volumes:
- - name: stunnerd-config-volume
- configMap:
- name: stunnerd-conf
- optional: true
- - name: stunnerd-cert-volume
- secret:
- secretName: stunner-tls
- optional: true
-
----
-apiVersion: v1
-kind: Service
-metadata:
- name: stunner-dtls
- labels:
- app: stunner
-spec:
- ports:
- - port: 443
- targetPort: 443
- protocol: UDP
- name: stunner-dtls
- type: LoadBalancer
- selector:
- app: stunner
-
----
-apiVersion: v1
-kind: Service
-metadata:
- name: stunner-tls
- labels:
- app: stunner
-spec:
- ports:
- - port: 443
- targetPort: 443
- protocol: TCP
- name: stunner-tls
- type: LoadBalancer
- selector:
- app: stunner
-
----
-# lock down access from the TURN server to anywhere!
-apiVersion: networking.k8s.io/v1
-kind: NetworkPolicy
-metadata:
- name: stunner-network-policy
-spec:
- podSelector:
- matchLabels:
- app: stunner
- policyTypes:
- - Egress
- egress:
- - to:
- - podSelector:
- matchLabels:
- app: media-plane
- ports:
- - protocol: UDP
diff --git a/deploy/manifests/stunner-standalone.yaml b/deploy/manifests/stunner-standalone.yaml
index e73d1823..229a09f5 100644
--- a/deploy/manifests/stunner-standalone.yaml
+++ b/deploy/manifests/stunner-standalone.yaml
@@ -1,147 +1,91 @@
-# Stunner: An ingress gateway for WebRTC
---
-# STUN/TURN server config: Make sure to customize the below configurations, see the README.pm for
-# more details
-apiVersion: v1
-kind: ConfigMap
+apiVersion: stunner.l7mp.io/v1
+kind: Dataplane
metadata:
- name: stunner-config
- namespace: default
-data:
- # * `STUNNER_PUBLIC_ADDR` (no default): The public IP address clients can use to reach
- # STUNner. By default, the public IP address will be dynamically assigned by the Kubernetes
- # LoadBalancer service. The Helm installation script takes care of updating the configuration
- # with the correct value. However, if installing from the static manifests then the external IP
- # must be set manually.
- STUNNER_PUBLIC_ADDR: "A.B.C.D"
-
- # * `STUNNER_PUBLIC_PORT` (default: 3478): The public port used by clients to reach STUNner. It
- # is important that applications use the public port as found in the configuration, since the
- # Helm installation scripts may overwrite this configuration. This occurs when the installation
- # falls back to a NodePort service (i.e., when STUNner fails to obtain an external IP from the
- # load-balancer).
- STUNNER_PUBLIC_PORT: "3478"
-
- # * `STUNNER_PORT` (default: 3478): The internal port used by STUNner for communication inside
- # the cluster. It is safe to set this to the public port.
- STUNNER_PORT: "3478"
-
- # * `STUNNER_REALM` (default `stunner.l7mp.io`): the REALM used to guide the user agent in
- # authenticating with STUNner.
- STUNNER_REALM: "stunner.l7mp.io"
-
- # * `STUNNER_AUTH_TYPE` (default: `plaintext`): the STUN/TURN authentication mode, either
- # "plaintext" over the username/password pair $STUNNER_USERNAME/$STUNNER_PASSWORD, or
- # "longterm", using $STUNNER_SECRET. Make sure to customize!
- STUNNER_AUTH_TYPE: "plaintext"
-
- # * `STUNNER_USERNAME` (default: `user`): the USERNAME attribute clients can use the authenticate
- # with STUNner over plain-text authentication. Make sure to customize!
- STUNNER_USERNAME: "user1"
-
- # * `STUNNER_PASSWORD` (default: `pass`): the password clients can use to authenticate with
- # STUNner over plain-text authentication. Make sure to customize!
- STUNNER_PASSWORD: "passwd1"
-
- # * `STUNNER_SHARED_SECRET`: the shared secret used for longterm authentication.
- STUNNER_SHARED_SECRET: "my-shared-secret"
-
- # * `STUNNER_DURATION` (default: `86400`, i.e., one day): the lifetime of STUNner credentials
- # * over longterm authentication.
- STUNNER_DURATION: "86400"
-
- # * `STUNNER_LOGLEVEL` (default: `all:WARN`): the default log level used by the STUNner daemons.
- STUNNER_LOGLEVEL: "all:INFO"
-
- # * `STUNNER_MIN_PORT` (default: 10000): smallest relay transport port assigned by STUNner.
- STUNNER_MIN_PORT: "10000"
-
- # * `STUNNER_MAX_PORT` (default: 20000): highest relay transport port assigned by STUNner.
- STUNNER_MAX_PORT: "20000"
-
+ name: host-net
+spec:
+ command:
+ - stunnerd
+ args:
+ - -w
+ - --udp-thread-num=16
+ image: l7mp/stunnerd:dev
+ hostNetwork: true
+ resources:
+ limits:
+ cpu: 200m
+ memory: 512Mi
+ requests:
+ cpu: 100m
+ memory: 128Mi
---
-apiVersion: apps/v1
-kind: Deployment
+apiVersion: gateway.networking.k8s.io/v1
+kind: GatewayClass
metadata:
- name: stunner
- namespace: default
+ name: stunner-gatewayclass
spec:
- selector:
- matchLabels:
- app: stunner
- replicas: 1
- template:
- metadata:
- labels:
- app: stunner
- spec:
- containers:
- - name: stunnerd
- image: l7mp/stunnerd:latest
- imagePullPolicy: Always
- command: ["stunnerd"]
- args: ["-c", "/stunnerd.conf"]
- # args: ["-c", "/stunnerd.conf"]
- envFrom:
- - configMapRef:
- name: stunner-config
- env:
- - name: STUNNER_ADDR # we use the POD IP
- valueFrom:
- fieldRef:
- fieldPath: status.podIP
- # Uncomment this if you want to deploy a sidecar container with stunner to sniff traffic
- # - name: net-debug
- # image: l7mp/net-debug:latest
- # command: ["/bin/sh"]
- # args: ["-c", "while true; do echo hello; sleep 10;done"]
-
+ controllerName: "stunner.l7mp.io/gateway-operator"
+ parametersRef:
+ group: "stunner.l7mp.io"
+ kind: GatewayConfig
+ name: stunner-gatewayconfig
+ namespace: stunner
+ description: "STUNner is a WebRTC media gateway for Kubernetes"
---
-apiVersion: v1
-kind: Service
+apiVersion: stunner.l7mp.io/v1
+kind: GatewayConfig
metadata:
- name: stunner
- namespace: default
- labels:
- app: stunner
+ name: stunner-gatewayconfig
+ namespace: stunner
spec:
- ports:
- - port: 3478
- targetPort: 3478
- # nodePort: 30478
- protocol: UDP
- name: stunner-udp
- type: LoadBalancer
- selector:
- app: stunner
-
+ dataplane: host-net
+ realm: stunner.l7mp.io
+ authRef:
+ name: stunner-auth-secret
+ namespace: stunner
---
apiVersion: v1
-kind: Service
+kind: Secret
+metadata:
+ name: stunner-auth-secret
+ namespace: stunner
+type: Opaque
+stringData:
+ type: static
+ username: user-1
+ password: pass-1
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+ name: turn-gateway
+ namespace: stunner
+spec:
+ gatewayClassName: stunner-gatewayclass
+ listeners:
+ - name: turn-listener
+ port: 3478
+ protocol: TURN-UDP
+---
+apiVersion: stunner.l7mp.io/v1
+kind: UDPRoute
metadata:
- name: stunner-tcp
- labels:
- app: stunner
+ name: open-route
+ namespace: stunner
spec:
- ports:
- - port: 3478
- targetPort: 3478
- protocol: TCP
- name: stunner-tcp
- type: LoadBalancer
- selector:
- app: stunner
-
+ parentRefs:
+ - name: turn-gateway
+ rules:
+ - backendRefs:
+ - group: stunner.l7mp.io
+ kind: StaticService
+ name: wildcard-backend
---
-# lock down access from the TURN server to anywhere!
-apiVersion: networking.k8s.io/v1
-kind: NetworkPolicy
+apiVersion: stunner.l7mp.io/v1
+kind: StaticService
metadata:
- name: stunner-network-policy
- namespace: default
+ name: wildcard-backend
+ namespace: stunner
spec:
- podSelector:
- matchLabels:
- app: stunner
- policyTypes:
- - Egress
+ prefixes:
+ - "0.0.0.0/0"
diff --git a/deploy/manifests/stunner-test.yaml b/deploy/manifests/stunner-test.yaml
new file mode 100644
index 00000000..7749f08d
--- /dev/null
+++ b/deploy/manifests/stunner-test.yaml
@@ -0,0 +1,63 @@
+apiVersion: gateway.networking.k8s.io/v1
+kind: GatewayClass
+metadata:
+ name: stunner-gatewayclass
+spec:
+ controllerName: "stunner.l7mp.io/gateway-operator"
+ parametersRef:
+ group: "stunner.l7mp.io"
+ kind: GatewayConfig
+ name: stunner-gatewayconfig
+ namespace: stunner
+ description: "STUNner is a WebRTC ingress gateway for Kubernetes"
+---
+
+apiVersion: stunner.l7mp.io/v1
+kind: GatewayConfig
+metadata:
+ name: stunner-gatewayconfig
+ namespace: stunner
+spec:
+ realm: stunner.l7mp.io
+ authType: plaintext
+ userName: "user-1"
+ password: "pass-1"
+
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+ name: udp-gateway
+ namespace: stunner
+spec:
+ gatewayClassName: stunner-gatewayclass
+ listeners:
+ - name: udp-listener
+ port: 3478
+ protocol: TURN-UDP
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+ name: tcp-gateway
+ namespace: stunner
+spec:
+ gatewayClassName: stunner-gatewayclass
+ listeners:
+ - name: tcp-listener
+ port: 3478
+ protocol: TURN-TCP
+---
+apiVersion: stunner.l7mp.io/v1
+kind: UDPRoute
+metadata:
+ name: media-plane
+ namespace: stunner
+spec:
+ parentRefs:
+ - name: udp-gateway
+ - name: tcp-gateway
+ rules:
+ - backendRefs:
+ - name: media-plane
+ namespace: default
diff --git a/docs/AUTH.md b/docs/AUTH.md
index 675975cd..329b4e1b 100644
--- a/docs/AUTH.md
+++ b/docs/AUTH.md
@@ -1,56 +1,25 @@
# Authentication
-STUNner uses the IETF STUN/TURN protocol suite to ingest media traffic into the Kubernetes cluster,
-which, [by design](https://datatracker.ietf.org/doc/html/rfc5766#section-17), provides
-comprehensive security. In particular, STUNner provides message integrity and, if configured with
-the TLS/TCP or DTLS/UDP listeners, complete confidentiality. To complete the CIA triad, this guide
-shows how to configure user authentication with STUNner.
+STUNner uses the IETF STUN/TURN protocol suite to ingest media traffic into a Kubernetes cluster, which, [by design](https://datatracker.ietf.org/doc/html/rfc5766#section-17), provides comprehensive security. In particular, STUNner provides message integrity and, if configured with the TURN-TLS or TURN-DTLS listeners, confidentiality. To complete the CIA triad, this guide shows how to configure user authentication with STUNner.
## The long-term credential mechanism
STUNner relies on the STUN [long-term credential
mechanism](https://www.rfc-editor.org/rfc/rfc8489.html#page-26) to provide user authentication.
-The long-term credential mechanism assumes that, prior to the communication, STUNner and the WebRTC
-clients agree on a username and password to be used for authentication. The credential is
-considered long-term since it is assumed that it is provisioned for a user and remains in effect
-until the user is no longer a subscriber of the system (STUNner's `static` authentication mode),
-or until the predefined lifetime of the username/password pair passes and the credential expires
-(`ephemeral` authentication mode in STUNner).
+The long-term credential mechanism assumes that, prior to the communication, STUNner and the WebRTC clients agree on a username and password to be used for authentication. The credential is considered long-term since it is assumed to remain in effect until the user is no longer a subscriber of the system (STUNner's `static` authentication mode), or until the predefined lifetime of the credential expires (`ephemeral` authentication mode in STUNner).
-STUNner secures the authentication process against replay attacks using a digest challenge. In
-this mechanism, the server sends the user a realm (used to guide the user or agent in selection of
-a username and password) and a nonce. The nonce provides replay protection. The client also
-includes a message-integrity attribute in the authentication message, which provides an HMAC over
-the entire request, including the nonce. The server validates the nonce and checks the message
-integrity. If they match, the request is authenticated, otherwise the server rejects the request.
+STUNner secures the authentication process against replay attacks using a digest challenge. In this mechanism, the server sends the user a realm (used to guide the user or agent in selection of a username and password) and a nonce. The nonce provides replay protection. The client also includes a message-integrity attribute in the authentication message, which provides an HMAC over the entire request, including the nonce. The server validates the nonce and checks the message integrity. If they match, the request is authenticated, otherwise the server rejects the request.
## Authentication workflow
-The intended authentication workflow in STUNner is as follows.
+The authentication workflow of STUNner is as follows.
-1. *A username/password pair is generated.* This is outside the scope of STUNner; however, STUNner
- comes with a comprehensive [authentication
- service](https://github.com/l7mp/stunner-auth-service) that can be queried for a valid ICE
- configuration for STUNner. The ICE configs returned by this service can be used by clients as
- the [option field in the `PeerConnection`
- call](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection#parameters),
- so that the resultant PeerConnections will be opened via STUNner as the TURN server.
+1. *A username/password pair is generated.* This is outside the scope of STUNner; however, STUNner comes with a custom [authentication service](https://github.com/l7mp/stunner-auth-service) that can be queried for a valid ICE configuration to be used by clients to authenticate with STUNner. The ICE configs returned by this service can be used by clients as the [option field in the `PeerConnection` call](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection#parameters), so that the resultant PeerConnections will be opened via STUNner as the TURN server.
- > **Warning**
- Clients should never query the STUNner authentication service directly to obtain an ICE
- config. Instead, the WebRTC application server should retrieve the ICE config in the name of the
- client during session establishment and return the generated ICE config to the client.
+ The ICE configs generated by the [STUNner authentication service](https://github.com/l7mp/stunner-auth-service) are always up to date with the most recent dataplane configuration. This makes sure that whenever you modify the STUNner Gateway API configuration (say, switch from `static` authentication to `ephemeral`), your clients will always receive an ICE config that reflects these changes (that is, the username/password pair will provide a time-windowed ephemeral credential).
- The ICE configs generated by the [STUNner authentication
- service](https://github.com/l7mp/stunner-auth-service) are always up to date with the most
- recent dataplane configuration. This makes sure that whenever you modify the STUNner Gateway API
- configuration (say, switch from `static` authentication to `ephemeral`), your clients will
- always receive an ICE config that reflects these changes (that is, the username/password pair
- will provide a time-windowed credential).
-
- For instance, the below will query the STUnner auth service, which is by default available at
- the URL `http://stunner-auth.stunner-system:8088`, for a valid ICE config.
+ Below is a query to the STUnner auth service, by default available at the URL `http://stunner-auth.stunner-system:8088`, that returns a valid ICE config.
```console
curl "http://stunner-auth.stunner-system:8088/ice?service=turn"
@@ -69,59 +38,38 @@ The intended authentication workflow in STUNner is as follows.
}
```
- Use the below to specify the lifetime of the generated credential to one hour (`ttl`, only makes sense when
- STUNner uses `ephemeral` authentication credentials) for a user named `my-user`, and you want
- the user to enter your cluster via the STUNner Gateway called `my-gateway` deployed into the
- `my-namespace` namespace.
+ Use the below query to generate a valid STUNner credential to access the Gateway called `my-gateway` deployed into the `my-namespace` namespace:
```console
- curl "http://stunner-auth.stunner-system:8088/ice?service=turn?ttl=3600&username=my-user&namespace=my-namespace&gateway=my-gateway"
+ curl "http://stunner-auth.stunner-system:8088/ice?service=turn&ttl=3600&username=my-user&namespace=my-namespace&gateway=my-gateway"
```
-2. The clients *receive the ICE configuration* (usually, from the application server) over a secure
- channel. This is outside the context of STUNner; our advice is to return the ICE configuration
- during the session setup process, say, along with the initial configuration returned for clients
- before starting the call.
+2. The clients *receive the ICE configuration* (usually, from the application server) over a secure channel. This is outside the context of STUNner. Our advice is to return the ICE configuration during the session setup process, say, along with the initial configuration returned for clients before starting the call.
-3. WebRTC clients are *configured with the ICE configuration* obtained above. The below snippet
- shows how to initialize a WebRTC
- [`PeerConnection`](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection)
- to use the above ICE server configuration in order to use STUNner as the default TURN service.
+3. WebRTC clients are *configured with the ICE configuration*. The below snippet shows how to initialize a WebRTC [`PeerConnection`](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection) to use the above ICE server configuration in order to use STUNner as the default TURN service.
- ```javascript
+ ```
var iceConfig =
var pc = new RTCPeerConnection(iceConfig);
```
## Static authentication
-In STUNner, `static` authentication is the simplest and least secure authentication mode, basically
-corresponding to a traditional "log-in" username and password pair given to users. STUNner accepts
-(and sometimes reports) the alias `plaintext` to mean the `static` authentication mode; the use of
-`plaintext` is deprecated and will be removed in a later release.
+In STUNner, `static` authentication is the simplest and least secure authentication mode, basically corresponding to a traditional "log-in" username and password pair given to users.
-When STUNner is configured to use `static` authentication only a single username/password pair is
-used for *all* clients. This makes configuration easy; e.g., the ICE server configuration can be
-hardcoded into the static Javascript code served to clients. At the same time, `static`
-authentication is prone to leaking the credentials: once an attacker learns a username/password
-pair they can use it without limits to reach STUNner (until the administrator rolls the
-credentials, see below).
+When STUNner is configured to use `static` authentication only a single username/password pair is used for *all* clients. This makes configuration easy; e.g., the ICE server configuration can be hardcoded into the static Javascript code served to clients. At the same time, `static` authentication is prone to leaking credentials: once an attacker learns a username/password pair they can use it without limits to reach STUNner (until the administrator rolls the credentials, see below).
-The first step of configuring STUNner for the `static` authentication mode is to create a
-Kubernetes Secret to hold the username/password pair. The below will set the username to `my-user`
-and the password to `my-password`. Note that if no `type` is set then STUNner defaults to `static`
-authentication.
+The first step of configuring STUNner for the `static` authentication mode is to create a Kubernetes Secret to hold the username/password pair. The below will set the username to `my-user` and the password to `my-password`. If no `type` is set then STUNner defaults to `static` authentication.
```console
kubectl -n stunner create secret generic stunner-auth-secret --from-literal=type=static \
--from-literal=username=my-user --from-literal=password=my-password
```
-Then, we create or update the current [GatewayConfig](REFERENCE.md) to refer STUNner to this secret
-for setting the authentication credentials.
+Then, we update the [GatewayConfig](GATEWAY.md) to refer STUNner to this Secret for setting authentication credentials.
```yaml
-apiVersion: stunner.l7mp.io/v1alpha1
+apiVersion: stunner.l7mp.io/v1
kind: GatewayConfig
metadata:
name: stunner-gatewayconfig
@@ -133,53 +81,28 @@ spec:
namespace: stunner
```
-The main use of static authentication is for testing. The reason for this is that static
-authentication credentials are easily discoverable: since the WebRTC Javascript API uses the TURN
-credentials unencrypted, an attacker can easily extract the STUNner credentials from the
-client-side Javascript code. In order to mitigate the risk, it is a good security practice to reset
-the username/password pair every once in a while. This can be done by simply updating the Secret
-that holds the credentials.
+It is a good security practice to reset the username/password pair every once in a while. This can be done by simply updating the Secret that holds the credentials.
```yaml
kubectl -n stunner edit secret stunner-auth-secret
```
-> **Warning**
-Modifying STUNner's credentials goes *without* restarting the TURN server but may affect existing
-sessions, in that existing sessions will not be able to refresh the active TURN allocation with the
-old credentials. The application server may also need to be restarted to learn the new TURN
-credentials.
+> [!WARNING]
+>
+> Modifying STUNner's credentials goes *without* restarting the TURN server but may affect existing sessions, in that active sessions will not be able to refresh their TURN allocation any more. This will result in the disconnection of clients using the old credentials.
## Ephemeral authentication
-For production use, STUNner provides the `ephemeral` authentication mode that uses per-client
-time-limited STUN/TURN authentication credentials. Ephemeral credentials are dynamically generated
-with a pre-configured lifetime and, once the lifetime expires, the credential cannot be used to
-authenticate (or refresh) with STUNner any more. This authentication mode is more secure since
-credentials are not shared between clients and come with a limited lifetime. Configuring
-`ephemeral` authentication may be more complex though, since credentials must be dynamically
-generated for each session and properly returned to clients. STUNner accepts (and sometimes
-reports) the alias `longterm` to mean the `ephemeral` authentication mode; the use of `longterm` is
-deprecated and will be removed in a later release. Note also that the alias `timewindowed` is also
-accepted.
-
-To implement this mode, STUNner adopts the [quasi-standard time-windowed TURN authentication
-credential format](https://datatracker.ietf.org/doc/html/draft-uberti-behave-turn-rest-00). In this
-format, the TURN username consists of a colon-delimited combination of the expiration timestamp and
-the user-id parameter, where the user-id is some application-specific id that is opaque to STUNner
-and the timestamp specifies the date of expiry of the credential as a UNIX timestamp. The TURN
-password is computed from the a secret key shared with the TURN server and the returned username
-value, by performing `base64(HMAC-SHA1(secret key, username))`. STUNner extends this scheme
-somewhat for maximizing interoperability with WebRTC apps, in that it allows the user-id and the
-timestamp to appear in any order in the TURN username and it accepts usernames with a plain
-timestamp, without the colon and/or the user-id.
+STUNner provides the `ephemeral` authentication mode for production use, which uses per-client time-limited STUN/TURN authentication credentials. Ephemeral credentials are dynamically generated with a pre-configured lifetime and, once the lifetime expires, the credential cannot be used to authenticate (or refresh) with STUNner any more. This authentication mode is more secure since credentials are not shared between clients and come with a limited lifetime. Configuring `ephemeral` authentication may be more complex though, since credentials must be dynamically generated for each session and properly returned to clients.
+
+STUNner adopts the [quasi-standard time-windowed TURN authentication credential format](https://datatracker.ietf.org/doc/html/draft-uberti-behave-turn-rest-00) for ephemeral authentication. The TURN username consists of a colon-delimited combination of the expiration timestamp and the user-id parameter, where the user-id is some application-specific id that is opaque to STUNner and the timestamp specifies the date of expiry of the credential as a UNIX timestamp. The TURN password is computed from the a secret key shared with the TURN server and the returned username value, by performing `base64(HMAC-SHA1(secret key, username))`. STUNner extends this scheme somewhat for maximizing interoperability with WebRTC apps, in that it allows the user-id and the timestamp to appear in any order in the TURN username and it accepts usernames with a plain timestamp, without the colon and/or the user-id.
The advantage of this mechanism is that it is enough to know the shared secret for STUNner to be
able to check the validity of a credential.
-> **Warning**
-The user-id is used only for the integrity check but STUNner in no way checks whether it identifies
-a valid user-id in the system.
+> [!WARNING]
+>
+> The user-id is to ensure that the password generated per user-id is unique, but STUNner in no way checks whether it identifies a valid user-id in the system.
In order to switch from `static` mode to `ephemeral` authentication, it is enough to update the
Secret that holds the credentials. The below will set the shared secret `my-shared-secret` for the
diff --git a/docs/CONCEPTS.md b/docs/CONCEPTS.md
index f09412fa..d8cc646a 100644
--- a/docs/CONCEPTS.md
+++ b/docs/CONCEPTS.md
@@ -1,27 +1,27 @@
# Concepts
-In this guide we describe STUNner's architecture and the most important components of an operational STUNner installation.
+This guide describes STUNner's architecture and the most important components of an operational installation.
## Architecture
-A STUNner installation consists of two parts, a *control plane* and a *dataplane*. The control plane consists of declarative policies specifying the way STUNner should route WebRTC media traffic to the media servers, plus a gateway operator that renders the high-level policies into an actual dataplane configuration. The dataplane in turn comprises one or more `stunnerd` pods, responsible for actually ingesting media traffic into the cluster through a STUN/TURN server. Since the TURN service underlying STUNner is agnostic to NATs, STUNner can inject clients' media traffic into the private Kubernetes pod network, addressing all NAT traversal steps (client-side and server-side) in a single go.
+A STUNner installation consists of two parts, a *control plane* and a *data plane*. The control plane consists of declarative policies specifying the way STUNner should route WebRTC media traffic to the media servers, plus a gateway operator that renders the high-level policies into an actual dataplane configuration. The data plane in turn comprises one or more `stunnerd` pods, which are responsible for actually ingesting media traffic into the cluster. The dataplane pods are automatically provisioned by the gateway operator so they should come and go as you add and remove STUNner gateways.
![STUNner architecture](img/stunner_arch_big.svg)
-The unit of the STUNner configuration is a [designated Kubernetes namespace](https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces) that holds the control plane configuration and the dataplane pods. You can run multiple STUNner deployments side-by-side by installing a separate dataplane into a each namespace and defining a distinct gateway hierarchy to configure each dataplane separately.
-
-### Control plane
+## Control plane
The STUNner control plane consists of the following components:
-* **Gateway hierarchy:** A gateway hierarchy is a collection of [Kubernetes Custom Resources](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources) that together describe the way media traffic should enter the cluster, including public IP addresses and ports clients can use to reach STUNner, TURN credentials, forwarding rules, etc. The anchor of the gateway hierarchy is the GatewayClass object, and the rest of the resources form a complete hierarchy underneath it: the GatewayConfig describes general STUNner configuration, Gateways define the port and transport protocol for each TURN server listener, and UDPRoutes point to the backend services client traffic should be forwarded to. See [here](GATEWAY.md) for a full reference.
+* **Gateway API resources:** The high-level STUNner configuration is a collection of [Gateway API](https://gateway-api.sigs.k8s.io) resources that together describe the way media traffic should enter the cluster. The anchor of the configuration hierarchy is the GatewayClass object, and the rest of the resources form a complete hierarchy underneath it: the GatewayConfig describes general STUNner configuration, Gateways define the port and transport protocol per each TURN server listener, and UDPRoutes point to the backend services client traffic should be forwarded to. See [here](GATEWAY.md) for a full reference.
-* **Gateway operator:** The main purpose of the gateway operator is to watch gateway hierarchies for change and, once a custom resource is added or modified by the user, render a new dataplane configuration. This configuration is then mapped into the filesystem of the `stunnerd` pods running in the same namespace, so that each `stunnerd` instance will use the most recent configuration. The STUNner Helm chart [automatically installs](INSTALL.md) the gateway operator; more information can be found [here](https://github.com/l7mp/stunner-gateway-operator).
+* **Gateway operator:** The main purpose of the gateway operator is to watch Gateway API resources and, once a Gateway API resource is added or modified by the user, update the dataplane accordingly (see below).
-* **STUNner ConfigMap:** The STUNner ConfigMap contains the running dataplane configuration. Of course, we could let the `stunnerd` pods themselves to watch the control plane for changes, but this would run into scalability limitations for large deployments. Instead, we separate the control plane and the dataplane, which brings cool [benefits](https://en.wikipedia.org/wiki/Software-defined_networking). The STUNner ConfigMap is usually named as `stunnerd-config`, but you can override this from the GatewayConfig.
+* **STUNner authentication service** (not shown on the figure): The auth service is an ancillary service that can be used to generate TURN credentials and complete [ICE server configurations](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection#iceservers) to bootstrap clients. See more info [here](AUTH.md).
## Dataplane
-The STUNner dataplane is comprised of a fleet of `stunnerd` pods. These pods actually implement the TURN server, using the configuration available in the STUNner ConfigMap which is mapped into the pods' filesystem dynamically. Then, `stunnerd` will watch for changes in the config file and, once a change is detected, it [reconciles](https://kubernetes.io/docs/concepts/architecture/controller) the dataplane to match the new user policies.
+The STUNner dataplane is comprised of a fleet of `stunnerd` pods implementing the TURN servers that can be used by clients to create WebRTC connections, plus some additional configuration to expose the TURN services to clients. The complete dataplane configuration per each Gateway is as follows:
+
+* **`stunnerd` Deployment:** Once you create a new Gateway the gateway operator will spawn a new dataplane for the Gateway automatically. For each Gateway there will be `stunnerd` Deployment with the same name and namespace. The `stunnerd` daemon itself is a TURN server implemented on top of the [pion/turn](https://github.com/pion/turn) Go WebRTC framework. The daemon will instantiate a separate *TURN listener* for each Gateway listener in the gateway configuration to terminate clients' TURN sessions, a *cluster* per each UDPRoute to forward packets to the backend services (e.g., to the media servers), with some ancillary administrative and authentication mechanisms in place to check client credentials, logging, etc. Whenever you modify a Gateway (UDPRoute), the gateway operator renders a new dataplane configuration with the modified listener (cluster, respectively) specs and downloads it to the `stunnerd` pods, which in turn reconcile their internal state with respect the new configuration. You are free to scale the dataplane to as many `stunnerd` pods as you wish: Kubernetes will make sure that new client connections are distributed evenly over the scaled-out STUNner dataplane.
-The `stunnerd` daemon itself is essentially a simple TURN server on top of [pion/turn](https://github.com/pion/turn) written in Go. The daemon will instantiate a separate *TURN listener* for each Gateway listener in the gateway hierarchy to terminate clients' TURN sessions, a *cluster* per each UDPRoute to forward packets to the backend services (e.g., to the media servers), with some ancillary administrative and authentication mechanisms in place to check client credentials before admitting traffic into the cluster, logging, etc. There is a one-to-one mapping between the control-plane Gateway listeners and the `stunnerd` TURN listeners, as well as between the UDPRoute resources and `stunnerd`'s clusters. Whenever you modify a Gateway (UDPRoute), the gateway operator renders a new dataplane configuration with the modified listener (cluster, respectively) specs and the `stunnerd` pods reconcile their internal state to the new configuration. You are free to scale the dataplane to as many `stunnerd` pods as you like: Kubernetes will make sure that new client connections are distributed evenly over the scaled-out STUNner dataplane.
+* **LoadBalancer Service:** STUNner creates a separate LoadBalancer Service per each Gateway to expose the TURN listeners of the `stunnerd` pods to the outside world. Similarly to the case of the `stunnerd` Deployment, there will be a separate LoadBalancer Service per each Gateway with the same name and namespace.
diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md
index 66b9b808..24da4338 100644
--- a/docs/DEPLOYMENT.md
+++ b/docs/DEPLOYMENT.md
@@ -1,15 +1,14 @@
# Deployment models
-STUNner can be deployed in many combinations to support a wide range of operational
+STUNner can be deployed in many different ways, supporting a wide range of operational
requirements. First, it supports multiple [architectural models](#architectural-models) where it
can act either as a simple headless STUN/TURN server or a fully fledged ingress gateway in front of
an entire Kubernetes-based media server pool. Second, when STUNner is configured as an ingress
gateway then there are multiple [ICE models](#ice-models), based on whether only the client
connects via STUNner or both clients and media servers use STUNner to set up the media-plane
-connection. Third, STUNner can run in one of two [control plane models](#control-plane-models),
-based on whether the user manually supplies STUNner configuration or there is a separate STUNner
-control plane that automatically reconciles the dataplane state based on a high-level [declarative
-API](https://gateway-api.sigs.k8s.io).
+connection. Third, STUNner can run in one of several [data plane models](#data-plane-models), based
+on whether the dataplane is automatically provisioned or the user has to manually supply the
+dataplane pods for STUNner.
## Architectural models
@@ -26,11 +25,11 @@ this case the STUN/TURN servers are deployed into Kubernetes.
![STUNner headless deployment architecture](img/stunner_standalone_arch.svg)
-> **Warning**
-For STUNner to be able to connect WebRTC clients and servers in the headless model *all* the
-clients and servers *must* use STUNner as the TURN server. This is because STUNner opens the
-transport relay connections *inside* the cluster, on a private IP address, and this address is
-reachable only to STUNner itself, but not for external STUN/TURN servers.
+
+
+
+
+
### Media-plane deployment model
@@ -52,12 +51,18 @@ for clients' UDP transport streams then STUNner can be scaled freely, otherwise
result the [disconnection of a small number of client
connections](https://cilium.io/blog/2020/11/10/cilium-19/#maglev).
-#### Asymmetric ICE mode
+## ICE models
-The standard mode to supply an ICE server configuration for clients and media servers in the
-media-plane deployment model of STUNner is the *asymmetric ICE mode*. In this model the client is
-configured with STUNner as the TURN server and media servers run with no STUN or TURN servers
-whatsoever.
+The peers willing to create a connection via STUNner (e.g., two clients as per the headless model,
+or a client and a media server in the media-plane deployment model) need to decide how to create
+ICE candidates.
+
+### Asymmetric ICE mode
+
+In *asymmetric ICE mode*, one peer is configured with STUNner as the TURN server and the other peer
+runs with no STUN or TURN servers whatsoever. The first peer will create a TURN transport relay
+connection via STUNner to which the other peer can directly join. Asymmetric ICE mode is the
+recommended way for the media-plane deployment model.
![STUNner asymmetric ICE mode](img/stunner_asymmetric_ice.svg)
@@ -71,38 +76,34 @@ connection. In contrast, servers run without any STUN/TURN server whatsoever, so
only. Due to servers being deployed into ordinary Kubernetes pods, the server's host candidate will
likewise contain a private pod IP address. Then, since in the Kubernetes networking model ["pods
can communicate with all other pods on any other node without a
-NAT"](https://kubernetes.io/docs/concepts/services-networking), clients' relay candidates and the
-servers' host candidates will have direct connectivity in the Kubernetes private container network
-and the ICE connectivity check will succeed. See more explanation
+NAT"](https://kubernetes.io/docs/concepts/services-networking), the client's relay candidate and
+the server's host candidate will have direct connectivity in the Kubernetes private container
+network and the ICE connectivity check will succeed. See more explanation
[here](examples/kurento-one2one-call/README.md#what-is-going-on-here).
-> **Warning**
-Refrain from configuring additional public STUN/TURN servers, apart from STUNner itself. The rules
-to follow in setting the [ICE server
+Refrain from configuring additional public STUN/TURN servers apart from STUNner itself. The rules
+to follow for setting the [ICE server
configuration](https://github.com/l7mp/stunner#configuring-webrtc-clients) in asymmetric ICE mode
are as below:
-> - on the client, set STUNner as the *only* TURN server and configure *no* STUN servers, whereas
-> - on the server do *not* configure *any* STUN or TURN servers whatsoever.
+- on the client, set STUNner as the *only* TURN server and configure *no* STUN servers, and
+- on the server do *not* configure *any* STUN or TURN server whatsoever.
-Most users will want to deploy STUNner using the asymmetric ICE mode. In the rest of the docs,
-unless noted otherwise we will assume the asymmetric ICE mode with the media plane deployment
-model.
+Deviating from these rules *might* work in certain cases, but may have uncanny and hard-to-debug
+side-effects. For instance, configuring clients and servers with public STUN servers in certain
+unlucky situations may allow them to connect via server-reflexive ICE candidates, completely
+circumventing STUNner. This is on the one hand extremely fragile and, on the other hand, a security
+vulnerability; remember, STUNner should be the *only* external access point to your media plane. It
+is a good advice to set the `iceTransportPolicy` to `relay` on the clients to avoid side-effects:
+this will prevent clients from generating host and server-reflexive ICE candidates, leaving STUNner
+as the only option to obtain an ICE candidate from.
-> **Warning**
-Deviating from the above rules *might* work in certain cases, but may have uncanny and
-hard-to-debug side-effects. For instance, configuring clients and servers with public STUN servers
-in certain unlucky situations may allow them to connect via server-reflexive ICE candidates,
-completely circumventing STUNner. This is on the one hand extremely fragile and, on the other hand,
-a security vulnerability; remember, STUNner should be the *only* external access point to your
-media plane. It is a good advice to set the `iceTransportPolicy` to `relay` on the clients to avoid
-side-effects: this will prevent clients from generating host and server-reflexive ICE candidates,
-leaving STUNner as the only option to obtain an ICE candidate from.
-
-#### Symmetric ICE mode
+### Symmetric ICE mode
In the symmetric ICE mode both the client and the server obtain an ICE [relay
candidate](https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate/type) from STUNner and
-the connection occurs directly via STUNner.
+the connection occurs directly via STUNner. This is the simplest mode for the headless deployment
+model, but symmetric mode can also be used for the media-plane model as well to connect clients to
+media servers.
![STUNner symmetric ICE mode](img/stunner_symmetric_ice.svg)
@@ -119,7 +120,7 @@ priorities](https://www.ietf.org/rfc/rfc5245.txt) to different connection types)
is a good practice to configure the STUNner TURN URI in the server-side ICE server configuration
with the *internal* IP address and port used by STUNner (i.e., the ClusterIP of the `stunner`
Kubernetes service and the corresponding port), otherwise the server might connect via the external
-LoadBalancer IP causing an unnecessary roundtrip.
+LoadBalancer IP causing an unnecessary roundtrip (hairpinning).
The symmetric mode means more overhead compared to the asymmetric mode, since STUNner now performs
TURN encapsulation/decapsulation for both sides. However, the symmetric mode comes with certain
@@ -128,15 +129,10 @@ internal IP addresses in the ICE candidates from attackers; note that this is no
but feel free to open an issue if [exposing internal IP addresses](SECURITY.md) is blocking
you from adopting STUNner.
-## Control plane models
-
-STUNner can run in one of two modes: in the default mode STUNner configuration is controlled by a
-*gateway-operator* component based on high-level intent encoded in [Kubernetes Gateway API
-resources](https://gateway-api.sigs.k8s.io), while in the *standalone model* the user configures
-STUNner manually. The standalone mode provides perfect control over the way STUNner ingests media,
-but at the same time it requires users to deal with the subtleties of internal STUNner APIs that
-are subject to change between subsequent releases. As of v0.14, STUNner's operator-ful mode is
-feature complete and the standalone model is considered obsolete. If still interested,
-comprehensive documentation for the standalone can be found [here](OBSOLETE.md), but this mode
-is no longer supported.
+## Data plane models
+STUNner supports two dataplane provisioning modes. In the default *managed* mode, the dataplane
+pods (i.e., the `stunnerd` pods) are provisioned automatically per each Gateway existing in the
+cluster. In the *legacy* mode, the dataplane is supposed to be deployed by the user manually by
+installing the `stunner/stunner` Helm chart into the target namespaces. Legacy mode is considered
+obsolete at this point and it will be removed in a later release.
diff --git a/docs/GATEWAY.md b/docs/GATEWAY.md
index 51efe6bf..57ef7415 100644
--- a/docs/GATEWAY.md
+++ b/docs/GATEWAY.md
@@ -1,23 +1,22 @@
# Reference
-The [STUNner gateway operator](https://github.com/l7mp/stunner-gateway-operator) exposes the control plane configuration using the standard [Kubernetes Gateway API](https://gateway-api.sigs.k8s.io). This allows to configure STUNner in the familiar YAML-engineering style via Kubernetes manifests. The below reference gives a quick overview of the Gateway API. Note that STUNner implements only a subset of the full [spec](GATEWAY.md), see [here](https://github.com/l7mp/stunner-gateway-operator#caveats) for a list of the most important simplifications.
+The [STUNner gateway operator](https://github.com/l7mp/stunner-gateway-operator) exposes the control plane configuration using the standard [Kubernetes Gateway API](https://gateway-api.sigs.k8s.io). This allows to configure STUNner in the familiar YAML-engineering style via Kubernetes manifests. The below reference gives an overview of the subset of the Gateway API supported by STUNner, see [here](https://github.com/l7mp/stunner-gateway-operator#caveats) for a list of the most important simplifications.
-## Overview
-
-The main unit of the control plane configuration is the *gateway hierarchy*. Here, a Gateway hierarchy is a collection of [Kubernetes Custom Resources](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources) that together describe the way media traffic should enter the cluster via STUNner, including public IP addresses and ports clients can use to reach STUNner, TURN credentials, routing rules, etc. The anchor of the gateway hierarchy is the GatewayClass object, and the rest of the resources form a complete hierarchy underneath it.
-
-![Gateway hierarchy](img/gateway_api.svg)
-
-In general, the scope of a gateway hierarchy is a single namespace, but this is not strictly enforced: e.g., the GatewayClass is [cluster-scoped](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions) so it is outside the namespace, GatewayClasses can refer to GatewayConfigs across namespaces, Routes can attach to Gateways across a namespace boundary (if the Gateway allows this), etc. Still, it is a good practice to keep all control plane configuration, plus the actual dataplane pods, in a single namespace as much as possible.
+1. [GatewayClass](#gatewayclass)
+1. [GatewayConfig](#gatewayconfig)
+1. [Gateway](#gateway)
+1. [UDPRoute](#udproute)
+1. [StaticService](#staticservice)
+1. [Dataplane](#dataplane)
## GatewayClass
-The GatewayClass resource provides the root of the gateway hierarchy. GatewayClass resources are cluster-scoped, so they can be attached to from any namespace, and we usually assume that each namespaced gateway hierarchy will have a separate global GatewayClass as the anchor.
+The GatewayClass resource provides the root of a STUNner gateway configuration. GatewayClass resources are cluster-scoped, so they can be attached to from any namespace.
-Below is a sample GatewayClass resource. Each GatewayClass must specify a controller that will manage the Gateway objects created under the hierarchy; this must be set to `stunner.l7mp.io/gateway-operator` for the STUNner gateway operator to pick up the GatewayClass. In addition, a GatewayClass can refer to further implementation-specific configuration via a `parametersRef`; in the case of STUNner this will always be a GatewayConfig object (see [below](#gatewayconfig)).
+Below is a sample GatewayClass resource. Each GatewayClass specifies a controller that will manage the Gateway objects created under the class; this must be set to `stunner.l7mp.io/gateway-operator` for the STUNner gateway operator to pick up the GatewayClass. In addition, a GatewayClass can refer to further implementation-specific configuration via a `parametersRef`; in the case of STUNner this will always be a GatewayConfig object (see [below](#gatewayconfig)).
```yaml
-apiVersion: gateway.networking.k8s.io/v1alpha2
+apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: stunner-gatewayclass
@@ -31,7 +30,7 @@ spec:
description: "STUNner is a WebRTC ingress gateway for Kubernetes"
```
-Below is a quick reference of the most important fields of the GatewayClass [`spec`](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects)
+Below is a quick reference of the most important fields of the GatewayClass [`spec`](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects).
| Field | Type | Description | Required |
| :--- | :---: | :--- | :---: |
@@ -41,12 +40,12 @@ Below is a quick reference of the most important fields of the GatewayClass [`sp
## GatewayConfig
-The GatewayConfig resource provides general configuration for STUNner, most importantly the STUN/TURN authentication [credentials](AUTH.md) clients can use to connect to STUNner. GatewayClass resources attach a STUNner configuration to the hierarchy by specifying a particular GatewayConfig in the GatewayClass `parametersRef`. GatewayConfig resources are namespaced, and every hierarchy can contain at most one GatewayConfig. Failing to specify a GatewayConfig is an error because the authentication credentials cannot be learned by the dataplane otherwise.
+The GatewayConfig resource provides general configuration for STUNner, most importantly the STUN/TURN authentication [credentials](AUTH.md) clients can use to connect to STUNner. GatewayClass resources attach a STUNner configuration to the hierarchy by specifying a particular GatewayConfig in the GatewayClass `parametersRef`. GatewayConfig resources are namespaced, and every hierarchy can contain at most one GatewayConfig. Failing to specify a GatewayConfig is an error because the authentication credentials cannot be learned otherwise.
-The following example takes the [STUNner authentication settings](AUTH.md) from the Secret called `stunner-auth-secret` in the `stunner` namespace, sets the authentication realm to `stunner.l7mp.io`, sets the dataplane loglevel to `all:DEBUG,turn:INFO` (this will set all loggers to `DEBUG` level except the TURN protocol machinery's logger which is set to `INFO`), and sets the default URL for metric scraping.
+The following example takes the [STUNner authentication settings](AUTH.md) from the Secret called `stunner-auth-secret` in the `stunner` namespace, sets the authentication realm to `stunner.l7mp.io`, and sets the dataplane loglevel to `all:DEBUG,turn:INFO` (this will set all loggers to `DEBUG` level except the TURN protocol machinery's logger which is set to `INFO`).
```yaml
-apiVersion: stunner.l7mp.io/v1alpha1
+apiVersion: stunner.l7mp.io/v1
kind: GatewayConfig
metadata:
name: stunner-gatewayconfig
@@ -54,42 +53,38 @@ metadata:
spec:
logLevel: "all:DEBUG,turn:INFO"
realm: stunner.l7mp.io
- authRef:
+ authRef:
name: stunner-auth-secret
namespace: stunner
- metricsEndpoint: "http://0.0.0.0:8080/metrics"
```
-Below is a quick reference of the most important fields of the GatewayConfig [`spec`](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects)
+Below is a reference of the most important fields of the GatewayConfig [`spec`](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects)
| Field | Type | Description | Required |
| :--- | :---: | :--- | :---: |
-| `stunnerConfig` | `string` | The name of the ConfigMap into which the operator renders the `stunnerd` running configuration. Default: `stunnerd-config`. | No |
-| `logLevel` | `string` | Logging level for the dataplane daemon pods (`stunnerd`). Default: `all:INFO`. | No |
-| `realm` | `string` | The STUN/TURN authentication realm to be used for clients to authenticate with STUNner. The realm must consist of lower case alphanumeric characters or `-` and `-`, and must start and end with an alphanumeric character. Default: `stunner.l7mp.io`. | No |
+| `dataplane` | `string` | The name of the Dataplane template to use for provisioning `stunnerd` pods. Default: `default`. | No |
+| `logLevel` | `string` | Logging level for the dataplane pods. Default: `all:INFO`. | No |
+| `realm` | `string` | The STUN/TURN authentication realm to be used for clients to authenticate with STUNner. The realm must consist of lower case alphanumeric characters or `-` and must start and end with an alphanumeric character. Default: `stunner.l7mp.io`. | No |
| `authRef` | `reference` | Reference to a Secret (`namespace` and `name`) that defines the STUN/TURN authentication mechanism and the credentials. | No |
| `authType` | `string` | Type of the STUN/TURN authentication mechanism. Valid only if `authRef` is not set. Default: `static`. | No |
-| `username` | `string` | The username for [`static` authentication](AUTH.md). Valid only if `authRef` is not set. | No |
+| `userName` | `string` | The username for [`static` authentication](AUTH.md). Valid only if `authRef` is not set. | No |
| `password` | `string` | The password for [`static` authentication](AUTH.md). Valid only if `authRef` is not set. | No |
| `sharedSecret` | `string` | The shared secret for [`ephemeral` authentication](AUTH.md). Valid only if `authRef` is not set. | No |
-| `metricsEndpoint` | `string` | The metrics server (Prometheus) endpoint URL for the `stunnerd` pods.| No |
-| `healthCheckEndpoint` | `string` | HTTP health-check endpoint exposed by `stunnerd`. Liveness check will be available on path `/live` and readiness check on path `/ready`. Default is to enable health-checking on `http://0.0.0.0:8086/ready` and `http://0.0.0.0:8086/live`, use an empty string to disable.| No |
| `authLifetime` | `int` | The lifetime of [`ephemeral` authentication](AUTH.md) credentials in seconds. Not used by STUNner.| No |
-| `loadBalancerServiceAnnotations` | `map[string]string` | A list of annotations that will go into the LoadBalancer services created automatically by STUNner to obtain a public IP addresses. See more detail [here](https://github.com/l7mp/stunner/issues/32). | No |
+| `loadBalancerServiceAnnotations` | `map[string]string` | A list of annotations that will go into the LoadBalancer services created automatically by STUNner to obtain a public IP address. See more detail [here](https://github.com/l7mp/stunner/issues/32). | No |
-> **Warning**
At least a valid username/password pair *must* be supplied for `static` authentication, or a `sharedSecret` for the `ephemeral` mode, either via an external Secret or inline in the GatewayConfig. External authentication settings override inline settings. Missing both is an error.
-Except the TURN authentication realm, all GatewayConfig resources are safe for modification. That is, the `stunnerd` daemons know how to reconcile a change in the GatewayConfig without restarting listeners/TURN servers. Changing the realm, however, induces a *full* TURN server restart (see below).
+Except the TURN authentication realm, all GatewayConfig resources are safe for modification. That is, the `stunnerd` daemons know how to reconcile a change in the GatewayConfig without restarting listeners/TURN servers. Changing the realm, however, induces a *full* dataplane restart.
## Gateway
Gateways describe the STUN/TURN server listeners exposed to clients.
-The below Gateway will configure STUNner to open a STUN/TURN listener on the UDP port 3478 and automatically expose it on a public IP address and port by creating a [LoadBalancer service](https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer). The name and namespace of the automatically provisioned service are the same as those of the Gateway, and the service is automatically updated if the Gateway changes (e.g., a port changes).
+The below Gateway resource will configure STUNner to open a STUN/TURN listener over the UDP port 3478 and make it available on a public IP address and port to clients. Each Gateway will have a `stunnerd` Deployment that will run the dataplane and a LoadBalancer Service that will expose the gateway to the Internet, both using the same name and namespace as the Gateway. Once the Gateway is removed, the corresponding resources are automatically garbage-collected.
```yaml
-apiVersion: gateway.networking.k8s.io/v1alpha2
+apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: udp-gateway
@@ -99,13 +94,13 @@ spec:
listeners:
- name: udp-listener
port: 3478
- protocol: UDP
+ protocol: TURN-UDP
```
-The below more complex example defines two TURN listeners: a UDP listener at port 3478 that accepts routes from any namespace, and a TLS/TCP listener at port 443 that accepts routes from all namespaces labeled as `app:dev`.
+The below example defines two TURN listeners: a TURN listener at the UDP:3478 port that accepts routes from any namespace (see below), and a TURN listener at port TLS/TCP:443 that accepts routes only from namespaces labeled with `app=dev`.
```yaml
-apiVersion: gateway.networking.k8s.io/v1alpha2
+apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: complex-gateway
@@ -121,13 +116,13 @@ spec:
listeners:
- name: udp-listener
port: 3478
- protocol: UDP
+ protocol: TURN-UDP
allowedRoutes:
namespaces:
from: All
- name: tls-listener
port: 443
- protocol: TLS
+ protocol: TURN-TLS
tls:
mode: Terminate
certificateRefs:
@@ -142,64 +137,149 @@ spec:
app: dev
```
-Below is a quick reference of the most important fields of the Gateway [`spec`](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects).
+Below is a reference of the most important fields of the Gateway [`spec`](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects).
| Field | Type | Description | Required |
| :--- | :---: | :--- | :---: |
| `gatewayClassName` | `string` | The name of the GatewayClass that provides the root of the hierarchy the Gateway is attached to. | Yes |
| `listeners` | `list` | The list of TURN listeners. | Yes |
+| `addresses` | `list` | The list of manually hinted external IP addresses for the rendered service (only the first one is used). | No |
+
+> [!WARNING]
+>
+> Gateway resources are *not* safe for modification. This means that certain changes to a Gateway will restart the underlying TURN server listener, causing all active client sessions to terminate. The particular rules are as follows:
+> - adding or removing a listener will start/stop *only* the TURN listener being created/removed, without affecting the rest of the listeners on the same Gateway;
+> - changing the transport protocol, port or TLS keys/certs of an *existing* listener will restart the TURN listener but leave the rest of the listeners intact;
+> - changing the TURN authentication realm will restart *all* TURN listeners.
-Each TURN `listener` is defined by a unique name, a transport protocol and a port. In addition, a
-`tls` configuration is required for TLS and DTLS listeners.
+Manually hinted external address describes an address that can be bound to a Gateway. It is defined by an address type and an address value. Note that only the first address is used. Setting the `spec.addresses` field in the Gateway will result in the rendered Service's [loadBalancerIP](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#service-v1-core:~:text=non%20%27LoadBalancer%27%20type.-,loadBalancerIP,-string) and [externalIPs](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#service-v1-core:~:text=and%2Dservice%2Dproxies-,externalIPs,-string%20array) fields to be set.
+
+| Field | Type | Description | Required |
+|:--------|:--------:|:--------------------------------------------------------------|:--------:|
+| `type` | `string` | Type of the address. Currently only `IPAddress` is supported. | Yes |
+| `value` | `string` | Address that should be bound to the Gateway's service. | Yes |
+
+> [!WARNING]
+>
+> Be careful when using this feature. Since Kubernetes v1.24 the `loadBalancerIP` field is deprecated and it will be ignored if your Kubernetes install does not support the feature. In addition, the `externalIPs` field is denied by some cloud-providers.
+
+### Listener configuration
+
+Each TURN `listener` is defined by a unique name, a transport protocol and a port. In addition, a `tls` configuration is required for TURN-TLS and TURN-DTLS listeners. Per-listener configuration is as follows.
| Field | Type | Description | Required |
| :--- | :---: | :--- | :---: |
-| `name` | `string` | Name of the TURN listener. | Yes |
+| `name` | `string` | Name of the TURN listener. Must be unique per Gateway. | Yes |
| `port` | `int` | Network port for the TURN listener. | Yes |
-| `protocol` | `string` | Transport protocol for the TURN listener. Either UDP, TCP, TLS or DTLS. | Yes |
-| `tls` | `object` | [TLS configuration](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1.GatewayTLSConfig).| Yes (for TLS/DTLS) |
-| `allowedRoutes.from` | `object` | [Route attachment policy](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.AllowedRoutes), either `All`, `Selector`, or `Same` (default is `Same`) | No |
+| `protocol` | `string` | Transport protocol for the TURN listener. Either TURN-UDP, TURN-TCP, TURN-TLS or TURN-DTLS. | Yes |
+| `tls` | `object` | [TLS configuration](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1.GatewayTLSConfig).| Yes (for TURN-TLS/TURN-DTLS) |
+| `allowedRoutes.from` | `object` | [Route attachment policy](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1beta1.AllowedRoutes), either `All`, `Selector`, or `Same`. Default: `Same`. | No |
+
+For TURN-TLS/TURN-DTLS listeners, `tls.mode` must be set to `Terminate` or omitted (`Passthrough` does not make sense for TURN), and `tls.certificateRefs` must be a [reference to a Kubernetes Secret](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io%2fv1beta1.GatewayTLSConfig) of type `tls` or `opaque` with exactly two keys: `tls.crt` must hold the TLS PEM certificate and `tls.key` must hold the TLS PEM key.
+
+### Load balancer configuration
+
+STUNner will automatically generate a Kubernetes LoadBalancer Service to expose each Gateway to clients. All TURN listeners specified in the Gateway are wrapped by a single Service and will be assigned a single externally reachable IP address. If you want multiple TURN listeners on different public IPs, create multiple Gateways. TURN over UDP and TURN over DTLS listeners are exposed as UDP services, TURN-TCP and TURN-TLS listeners are exposed as TCP.
+
+STUNner implements several ways to customize the automatically created Service, each involving certain pre-defined [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations). First, you can add global annotations to the `loadBalancerServiceAnnotations` field of the [GatewayConfig spec](#gatewayconfig), which affect the Service created for each Gateway that links to the GatewayConfig (via the GatewayClass `parametersRef`). To customize annotations on a per-gateway status, you can also add specific annotations to the Gateway itself. Gateway annotations override the global annotations on conflict. Finally, you can also add custom labels/annotations to the automatically created STUNner Services manually, these are retained on Service update (unless there is a conflict). Note that labels/annotations added via the Gateway and the GatewayConfig are also propagated to the corresponding `stunnerd` Deployments.
+
+The following rules apply:
+- Each annotation is copied verbatim into the Service created for any Gateway. This can be used, for instance, to specify health-check settings on the load-balancer Service (using the `service.beta.kubernetes.io/*-loadbalancer-healthcheck-*` annotations, see above).
+- Annotations with the prefix `stunner.l7mp.io/...` have special meaning: apart from being copied into the Service these annotations also affect some specifics of the created Service, like the service type or the nodeports assigned to listeners.
+
+STUNner defines the following special annotations:
+
+1. **Service type:** The special annotation `stunner.l7mp.io/service-type` can be used to customize the type of the Service created by STUNner. The value can be either `ClusterIP`, `NodePort`, or `LoadBalancer` (this is the default); for instance, setting `stunner.l7mp.io/service-type: ClusterIP` will prevent STUNner from exposing a Gateway publicly (useful for testing).
+
+1. **Mixed-protocol support:** Currently, STUNner limits each Gateway to a single transport protocol, e.g., UDP or TCP. This is intended to improve the consistency across the Kubernetes services of different cloud providers, which provide varying support for [mixed multi-protocol LoadBalancer Services](https://kubernetes.io/docs/concepts/services-networking/service/#load-balancers-with-mixed-protocol-types). If you still want to expose a UDP and a TCP port on the same IP using a single Gateway, add the annotation `stunner.l7mp.io/enable-mixed-protocol-lb: true` to the Gateway. Since mixed-protocol LB support is not supported in many popular Kubernetes offerings, STUNner currently defaults to disabling this feature.
+
+ The below Gateway will expose both ports with their respective protocols.
+
+ ```yaml
+ apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ name: mixed-protocol-gateway
+ annotations:
+ stunner.l7mp.io/enable-mixed-protocol-lb: true
+ spec:
+ gatewayClassName: stunner-gatewayclass
+ listeners:
+ - name: udp-listener
+ port: 3478
+ protocol: TURN-UDP
+ - name: tcp-listener
+ port: 3479
+ protocol: TURN-TCP
+ ```
+
+1. **Retaining the source IP:** Normally, Kubernetes load balancers apply source IP address translation when ingesting packets into the cluster. This replaces clients' original IP address with a private IP address. For STUNner's intended use case, as an ingress media gateway exposing the cluster's media services over the TURN protocol, this does not matter. However, STUNner can also act as a STUN server, which requires clients' source IP to be retained at the load balancer. This can be achieved by adding the annotation `stunner.l7mp.io/external-traffic-policy: local` to a Gateway, which will set the [`service.spec.externalTrafficPolicy`](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip) field in the Service created by STUNner for the Gateway to `Local`. Note that this Kubernetes feature comes with fairly complex [limitations](https://kubernetes.io/docs/tutorials/services/source-ip): if a STUN or TURN request hits a Kubernetes node that is not running a `stunnerd` pod, then the request will silently fail. This is required for Kubernetes to retain the client IP, which otherwise would be lost when passing packets between nodes. Use this setting at your own [risk](https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#caveats-and-limitations-when-preserving-source-ips).
+
+1. **Manually provisioning the dataplane:** In some cases it may be useful to manually provision a dataplane for a Gateway, e.g., to deploy `stunnerd` in a DeamonSet instead of a Deployment. Adding the annotation `stunner.l7mp.io/disable-managed-dataplane: true` to a Gateway will prevent STUNner from spawning a dataplane for the Gateway. This then allows one to manually create a `stunnerd` dataplane and connect it to the CDS server exposed by the operator to obtain the dataplane configuration. Remove the annotation to revert to the default mode and let STUNner to manage the dataplane for the Gateway. Manual dataplane provisioning requires intimate knowledge with the STUNner internals, use this feature only if you know what you are doing.
+
+1. **Selecting the NodePort:** By default, Kubernetes assigns a random external port from the range [32000-32767] to each listener of a Gateway exposed with a NodePort Service. This requires all ports in the [32000-32767] range to be opened on the external firewall, which may raise security concerns for hardened deployments. In order to assign specific nodeports to particular listeners, add the annotation `stunner.l7mp.io/nodeport:` `{listener_name_1:nodeport_1,listener_name_2:nodeport_2,...}` to the Gateway, where each key-value pair is a name of a listener and the selected (numeric) NodePort. The value itself must be proper a JSON map. Unknown listeners are silently ignored. Note that STUNner makes no specific effort to reconcile conflicting NodePorts: whenever the selected NodePort is unavailable Kubernetes will silently reject the Service, which can lead to hard-to-debug failures. Use this feature at your own risk.
+
+1. **Selecting the target port:** Some hardened Kubernetes deployments prohibit containers to open privileged ports (i.e., everything under 1024). This causes problems when one wants to ingest TURN over the standard TCP/TLS port 443. Kubernetes lets Services to choose an arbitrary [target port](https://kubernetes.io/docs/concepts/services-networking/service/#defining-a-service) for each service port, which makes it possible to map a particular external port to an arbitrary (potentially non-privileged) port in the containers. In order to enforce a particular target port per listener, add the annotation `stunner.l7mp.io/targetport:` `{listener_name_1:targetport_1,...}` to the corresponding Gateway (the syntax and semantics are the same as those for the nodeport annotation). For instance, the below Gateway would expose the TURN TCP/TLS on port 443, but it would map the egress port to the target port 44321 in the STUNner dataplane container:
+
+ ```yaml
+ apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ name: tls-gateway
+ annotations:
+ stunner.l7mp.io/targetport: "{\"tls-listener\":44321}"
+ spec:
+ gatewayClassName: stunner-gatewayclass
+ listeners:
+ - name: tls-listener
+ port: 443
+ protocol: TURN-TLS
+ tls:
+ certificateRefs:
+ - kind: Secret
+ namespace: stunner
+ name: tls-secret
+ ```
+
+1. **Disabling the exposition of the health-check port:** Some older Kubernetes load-balancer providers required the exposition of the health-check port on LoadBalancer Services for UDP listeners to become externally reachable. Therefore, by default STUNner adds the health-check port (usually set via specific Gateway annotations) to the service-ports in automatically created LoadBalancer services. This has the unfortunate consequence that the health-check port becomes publicly reachable, which is considered a security issue by some, see https://github.com/l7mp/stunner-gateway-operator/issues/49. To prevent STUNner from exposing the health-check port, add the annotation `stunner.l7mp.io/disable-health-check-expose: true` to the corresponding Gateway. Note that this may cause TURN/UDP listeners unreachable on the Gateway, so use this only if you know that this setting will work with your Kubernetes provider.
+
+1. **Disabling session affinity:** By default STUNner applies the `sessionAffinity: ClientIP` setting on the LB services it creates to expose Gateways. Normally this setting improves stability by ensuring that each TURN session is safely pinned to the same dataplane pod for its entire lifetime. Certain hosted Kubernetes platforms, however, seem to reject UDP LB services that have this setting on, [breaking STUNner deployments](https://github.com/l7mp/stunner/issues/155) on these systems. In order to prevent STUNner from enforcing session affinity on the LB Service corresponding to a Gateway, just set the `stunner.l7mp.io/disable-session-affinity: true` annotation on the Gateway. Otherwise, session affinity is turned on.
+
+The below table summarizes the Gateway annotations supported by STUNner.
+
+| Key/value | Description | Default |
+|:----------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------:|
+| `stunner.l7mp.io/service-type: ` | [Type of the Service](https://kubernetes.io/docs/concepts/services-networking/service) per Gateway, either `ClusterIP`, `NodePort`, or `LoadBalancer`. | `LoadBalancer` |
+| `stunner.l7mp.io/enable-mixed-protocol-lb: ` | [Mixed protocol load balancer service](https://kubernetes.io/docs/concepts/services-networking/service/#load-balancers-with-mixed-protocol-types) support. | False |
+| `stunner.l7mp.io/external-traffic-policy: ` | Se the value to `Local` to preserve clients' source IP at the load balancer. | `Cluster` |
+| `stunner.l7mp.io/disable-managed-dataplane: ` | Switch managed-dataplane support off for a Gateway. | False |
+| `stunner.l7mp.io/nodeport: